이것이 점프 투 공작소

NandToTetris-Hardware simulator / 레지스터, 메모리, PC 실습 (밑바닥부터 만드는 컴퓨팅 시스템) 본문

카테고리 없음

NandToTetris-Hardware simulator / 레지스터, 메모리, PC 실습 (밑바닥부터 만드는 컴퓨팅 시스템)

겅겅겅 2025. 1. 30. 22:45

이전까지 만들었던 칩들은 시간과 무관한 조합(combination)칩입니다.

여기서 실습할 칩들은 현재시점의 입력과 이전에 처리되었던 칩들의 결과에도 영향을 받는 순차(sequential)칩 입니다.

컴퓨터에서의 시간은 클럭(tick, tock)이라는 2개의 이산신호를 이용해서 구현 할 수 있습니다.

 

데이터 플립 플롯 (DFF)

컴퓨터에서의 시간 클럭(tick, tock)을 사용하는 논리게이트입니다.

DFF는 클럭 신호에 맞추어 데이터를 출력하며, 이전에 입력되었던 신호를 출력하는 간단한 동작을 구현합니다.

(클럭 신호는 클록 전용 버스를 통해 DDL로 전달됩니다.)

 

예를들어 DFF의 in에 1이 들어간다면 DFF는 in으로 들어온 데이터 1이 저장되고

다음 클럭 때 DFF에서 가지고 있던 데이터 1을 출력합니다.

// 저번 클럭(t-1) 때 들어온 데이터를 이번 클럭(t)에 출력합니다
out(t)=in(t-1)

 

레지스터 (Register)

DFF은 이전에 들어온 입력데이터를 계속해서 출력만 하기에

DFF 저장된 데이터를 변경하거나 출력하기 위한 장치, 레지스터가 필요합니다.

 

레지스터에 저장되는 비트 수는 width라고도 하며, 이 데이터를 word, 크기를 wordsize라고 합니다.

32Bit 운영체제의 wordsize는 32Bit, 64Bit는 64Bit입니다.

 

데이터 비트를 전달하는 입력 in, 쓰기 기능을 설정하는 load입력, 레지스터의 현재상태를 출력하는 out이 있습니다.

load가 1이면 레지스터의 출력은 in(t)의 값을 저장 후 출력하고,

load가 0이면 레지스터의 출력은 out(t-1), 즉 저번 클럭에 유지된 값을 값을 출력합니다.

1bit 레지스터

레지스터에 입력이 들어오면 먼저 Mux에 의해 선택된 out이 DFF로 전달됩니다.

 

레지스터에 들어온 load가 0이라면,

레지스터는 이전에 DFF에 존재하던 데이터를 출력해야합니다.

먼저 DFF의 in에는 Mux에 따른 출력(out)이 전달되고 (load가 0이기에 DFF에 0이 전달됨),

DFF에 0이 들어오면 상태가 변하지 않으므로 다음 클럭에 맞추어 이전 출력을 그대로 출력합니다.

 

만약 레지스터의 load에 1이 전달되면.

in에 따른 MUX의 out이 DFF에 전달되고

DFF는 들어온 in(0 또는 1)에 따라 이전 데이터를 출력하거나 새로운 값을 DFF에 저장하고 다음 클럭에 출력합니다.

 

(16Bit 레지스터도 구현은 동일합니다.)

CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    Mux(a=muxin, b=in , sel=load, out=muxout);
    DFF(in=muxout, out=muxin, out=out); // MUX, 최종out 두 방향으로 전달
}

메모리 (Random-Access-Memory)

메모리 (RAM)는 n개의 레지스터 칩으로 구성되며, 

RAM은 각 레지스터마다 특정주소(0 ~ n-1)를 할당하고 이 주소를 이용해 특정 레지스터의 값을 읽거나 쓸 수 있는 입니다.

어떤 레지스터의 주소든 같은 속도로 접근 가능하기에 Random Access Memory라고 합니다.

 

메모리는 in, load, address 세 개의 입력을 받아 address에 위치한 레지스터에서 읽기 또는 쓰기를 하여 결과를 반환합니다.

읽기,쓰기가 일어나는 '한번의 동작'에서 address로 들어온 주소의 레지스터만 사용합니다.

 

load가 0과 address가 들어오면 선택한 레지스터의 값을 반환하고,

load가 1, address, in이 들어오면 In에 들어온 값을 레지스터에 저장하고 다음 클럭에 그 값을 반환합니다.

CHIP RAM8 {
    IN  in[16], load, address[3]; 
    OUT out[16];                 

    PARTS:
    Register(in=in, load=sel0, out=r0);
    Register(in=in, load=sel1, out=r1);
    Register(in=in, load=sel2, out=r2);
    Register(in=in, load=sel3, out=r3);
    Register(in=in, load=sel4, out=r4);
    Register(in=in, load=sel5, out=r5);
    Register(in=in, load=sel6, out=r6);
    Register(in=in, load=sel7, out=r7);

    // 주소(address)에 따라 8개의 레지스터 중 하나만 load 활성화
    DMux8Way(in=load, sel=address, a=sel0, b=sel1, c=sel2, d=sel3, 
             e=sel4, f=sel5, g=sel6, h=sel7);

    // 주소(address)에 따라 선택된 레지스터의 데이터를 출력
    Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7, 
              sel=address, out=out);
}

 

프로그램 카운터 (Program Counter)

프로그램 카운터는 CPU가 다음에 실행할 명령어의 주소를 레지스터에 전달해주는 기능을 합니다.

 

load, inc, reset, in 4개의 제어비트가 있으며

in, load에 데이터가 들어오면 in에 해당하는 값으로 PC의 값을 설정하거나 매 클럭마다 값을 1씩 증가시키는 동작을 합니다.

 

추가로 카운터를 관리하는 inc와 reset 두가지의 제어비트가 추가로 존재하는데,

inc가 활성화(1) 되어있으면 매 클록 주기마다 상태값을 1씩 증가시키며 (pc++) reset 비트를 활성화(1)하면 카운터(PC)의 상태값이 초기화됩니다.

만약 load, inc, reset모두 0이라면 현재 PC값을 유지합니다.

CHIP PC {
    IN in[16], reset, load, inc;
    OUT out[16];
    
    PARTS:
    // 현재 PC 값을 저장할 16비트 레지스터
    Register(in=pcNext, load=true, out=pcCurrent, out=out);

    // 현재 PC + 1 계산 (자동 증가)
    Inc16(in=pcCurrent, out=pcPlusOne);

    // Mux로 동작 선택
    Mux16(a=pcCurrent, b=pcPlusOne, sel=inc, out=pcInc);   // inc=1이면 증가

    Mux16(a=pcInc, b=in, sel=load, out=pcLoad);            // load=1이면 in 값으로 점프

    Mux16(a=pcLoad, b=false, sel=reset, out=pcNext);       // reset=1이면 0으로 리셋    
}