이전 글에서 겨우 겨우 CPU를 완성하고,

 

그 다음으로 만들건 32K ROM이다 32K니까 2^5 * 2^10 = 32 * 1024이고 2^15만큼의 주소를 갖는다.

 

1. ROM 32K

 롬이야 읽기 전용 기억장치이니 쓰기를 위한 load 단자는 필요없고, 지난번에 16K까지 만들었는데 그냥 RAM16K 2개를 디먹스로 연결해주면 될거같다.

 

 

 근데 5장에서 롬을 만들려고 하니까 ROM.hdl은 없고 남은건 memory(ram)와 computer 뿐이다. rom은 어샘블러로 만든 기계어를 넣어둿다가 꺼내서 실행하니까 별도로 만드는건 아닌거같다.

 

 

2. RAM

 그래서 바로 램을 구현하게 잠깐 살펴보자. 여기선 RAM이랑 다른 입출력장치 매모리맵을 합쳐서 memory라고 하고 있네, RAM은 이전에 본 대로 16K 크기에다가 16K인 이유는 A 명령어가 14개의 비트 크기의 값과 주소를 저장해서 사용하기 때문이며, 그 뒤에는 스크린과 키보드의 공간이 존재한다.

 

 스크린은 16K램뒤에 바로 붙기 때문인지 16,384부터 시작하며, HACK의 스크린은 256 x 512 크기로 2^8 x 2^9 = 2^17 이나 레지스터 한개에 16=2^4개의 픽셀 값이 매핑되어있으므로 메모리 맵 상에서는 2^17/2^4 = 2^13 = 8K의 크기를 갖는다.  키보드야 키보드 베이스 레지스터 한개만 읽으면 되니 주소는 24,576 하나 뿐이다.

 

 HACK 컴퓨터의 메모리에 관한 설명은 여기까지 하고 바로 구현해보자

RAM16K(렘), RAM8K(스크린 매모리맵), register(키보드 메모리맵) 각각 한개씩에다가 어째저쨰 잘 연결하면 될거같다.

 

 어떻게 할까 생각해봣는데 일단 address[15]를 sel단자에 받는 디먹스를 놓고 생각해봐야할거같다.

 

CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];

    PARTS:
    DMux8Way(in=load, sel[2]=address[5], sel[1]=address[4], sel[0]=address[3],
    a=ram0load, b=ram1load, c=ram2load, d=ram3load, e=ram4load,
    f=ram5load, g=ram6load, h=ram7load);

    RAM8(in=in, load=ram0load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r0);
    RAM8(in=in, load=ram1load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r1);
    RAM8(in=in, load=ram2load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r2);
    RAM8(in=in, load=ram3load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r3);

    RAM8(in=in, load=ram4load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r4);
    RAM8(in=in, load=ram5load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r5);
    RAM8(in=in, load=ram6load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r6);
    RAM8(in=in, load=ram7load, address[2]=address[2], address[1]=address[1], address[0]=address[0], out=r7);

        
    Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7,
    sel[2]=address[5], sel[1]=address[4], sel[0]=address[3], out=out);
}

 지난번에 RAM 구현할때 디먹스랑, 먹스를 동시에 사용하면 편했는데 이걸 모르고 한참 삽질했었다.

이걸 참고해서 메모리를 구현해보자

 

 

잠깐 디먹스 파트를 생각해봤는데 대강 이정도면 될거같다

다만 키보드 레지스터의 경우 addr[0..12]가 00000...001이여야만 레지스터에 로드해야하는데

그 이상의 공간이 없으니 주소가 잘못 오는 경우가 없다고 생각하고 일단 구현해봐야겠다.

 

ram8k는 없어서 demux와 4k 2개로 대체한다.

 

CHIP Memory {
    IN in[16], load, address[15];
    OUT out[16];

    PARTS:
    //DMux part : set load bit
    DMux(in=load, sel=address[14], a=ram16kload, b=ram8kload);
    DMux(in=ram8kload, sel=address[13],a=ram8kload2, b=registerload);
    //ram 16k
    RAM16K(in=in, load=ram16kload, address=address[0..13], out=ram16kOut);
    //screem memory map
    DMux(in=ram8kload2, sel=address[12], a=ram4kload1, b=ram4kload2);
    RAM4K(in=in, load=ram4kload1, address=address[0..11], out=ram4k1Out);
    RAM4K(in=in, load=ram4kload2, address=address[0..11], out=ram4k2Out);
    //keyboard memory map
    Register(in=in, load=registerload, out=registerOut);

    //Mux part : get selected register output

}

일단 DMux 파트부터 구현했는데 오류는 뜨지 않는다.

 

이제 먹스 파트로 가서 주소에 따라 해당 메모리 값을 가져올수 있도록 하자.

 

잘 돌아가는데 키 입력이 되지를 않았다.

 

도저히 안되서 찾아보니

스크린 칩을 안쓰고 RAM4K 2개, 키보드 칩을 안쓰고 레지스터를 쓴게 문제였다 ..

빌트인으로 준건데!

 

 

 

위 그림처럼 다시 수정하면

 

CHIP Memory {
    IN in[16], load, address[15];
    OUT out[16];

    PARTS:
    //DMux part : set load bit
    DMux(in=load, sel=address[14], a=ram16kload, b=screenLoad);
    DMux(in=screenLoad, sel=address[13], a=screenload2);
    //ram 16k
    RAM16K(in=in, load=ram16kload, address=address[0..13], out=ram16kOut);

    //screem memory map
    Screen(in=in, load=screenload2, address[0..12]=address[0..12], out=screenOut);
    //keyboard memory map
    Keyboard(out=keyboardOut);

    //Mux part : get selected register output
    Mux16(a=screenOut, b=keyboardOut, sel=address[13], out=screenKeyboardOut);
    Mux16(a=ram16kOut, b=screenKeyboardOut, sel=address[14], out=out);
}

 

구현한 메모리 칩 테스트 돌려보니 키보드 입력도 잘받고 스크린 출력도 잘된다.

 

 

 

 

 

 

3. HACK 컴퓨터 구현하기

 드디여 nand2tetris 하드웨어 마지막 과제인 컴퓨터 구현까지 왔다. 앞서 어려운 로직들은 다 구현했으니까 이젠 연결만 하면 끝날거같다 ㅠㅜ

 동작이야 ROM32K에 입력된 명령어가져와서 CPU에서 연산하고 memory에 저장하고, 다음 명령어 이동해서 실행하고를 반복이고, reset 입력이 들어가면 프로그램 카운터가 0을 가리키면서 처음부터 재시작 된다.

 

 

 앞에서 하도 해매니까 컴퓨터 구현이 가장 쉽네 ㅋㅋㅋ

CHIP Computer {

    IN reset;

    PARTS:

    CPU(inM=inM, instruction=instruction, reset=reset, outM=outM,
    writeM=writeM, addressM=addressM, pc=pc);
    Memory(in=outM, load=writeM, address=addressM, out=inM);
    ROM32K(address=pc, out=instruction);
}

 

테스트가 너무 빨리 끝나서 screen 모드와 출력 모드 둘다 볼수 있게 찍었다.

 

 

 

이제 난드 게이트로 컴퓨터 구현까지 하드웨어 파트는 마무리했고,

이 다음부터는 소프트웨어 영역으로 넘어가게 된다.

 

익숙한 SW니까 금방 할수있을까 생각하다가 하드웨어에서 시간 엄청 낭비한걸 생각하면

소프트웨어도 HW 못지않게 해맬수도있을거같다.

 

 거의 일주일 동안 수업 시간을 제외해서는 하루종일 nand2tetris하면서 시간을 보냈다. 내가 이걸 하려는건 디지털 논리 회로 설계하려는건 아니고 컴퓨터 동작 원리를 제대로 공부하고 싶어서였는데 기계어, 어셈블리어, HW 구조 만큼은 제대로 굴럿다.

 

 이 다음에는 어셈블러, 가상머신, 고급언어, 운영체제, 어플리케이션 등이 나오는데, 끝날때까지 이것만 팔지 아니면 다른거랑도 병행해야할지는 좀 고민된다. 아마 이것만 올인하기는 시간아까워서 이전에 하다만 파이썬이나 공업수학이나 같이 병행하던가해야지.

+ Recent posts