레지스터와 카운터 만들기

레지스터와 카운터 만들기

Background

앞에서 만든 불 논리 칩과 산술 칩들은 모두 조합 칩(conbinational chip), 즉 입력 값의 조합에만 의존하는 함수를 계산한다.하지만 컴퓨터는 값을 저장하고 다시 불러올 수 있어야 함. 그러므로 시간이 지나도 데이터를 보존할 수 있는 메모리 소자가 필요하고, 이 메모리 소자는 순차 칩(sequential chip)으로 만듦

Data Flip-Flop

FlipFlop

Master Clock에 따라서 계속 바뀌는 Clock과 1비트 데이터 입력을 받는다. 이 데이터 입력과 클록 입력을 종합해서 시간에 따른 동작 out(t) = in(t - 1)을 수행함

Register

Register

시간이 지나도 값을 저장하고 로드할 수 있는 장치로 out(t) = out(t - 1)을 수행함.

1-Bit Register
CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    Mux(a=feedback, b=in, sel=load, out=a0);
    DFF(in=a0, out=feedback, out=out);
}
16-Bit-Register
CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
    Bit(in=in[0], load=load, out=out[0]);
    Bit(in=in[1], load=load, out=out[1]);
    Bit(in=in[2], load=load, out=out[2]);
    Bit(in=in[3], load=load, out=out[3]);
    Bit(in=in[4], load=load, out=out[4]);
    Bit(in=in[5], load=load, out=out[5]);
    Bit(in=in[6], load=load, out=out[6]);
    Bit(in=in[7], load=load, out=out[7]);
    Bit(in=in[8], load=load, out=out[8]);
    Bit(in=in[9], load=load, out=out[9]);
    Bit(in=in[10], load=load, out=out[10]);
    Bit(in=in[11], load=load, out=out[11]);
    Bit(in=in[12], load=load, out=out[12]);
    Bit(in=in[13], load=load, out=out[13]);
    Bit(in=in[14], load=load, out=out[14]);
    Bit(in=in[15], load=load, out=out[15]);
}

레지스터가 저장할 수 있는 비트의 개수는 폭(width)이라 부르며, 16, 32, 64비트가 그 폭을 뜻한다. 또한 레지스터에 저장되는 멀티비트 값은 보통 단어(word)라 부른다.

Memory

Memory

위의 그림은 레지스터를 여러 개 쌓아 올려서 만든 임의 접근 메모리(Random Access Memory)이다. 임의 접근 메모리라는 용어는, 접근 순서와 관계없이 무작위로 선택된 단어를 읽고 쓸 수 있다는 데서 나온 말이다. 말하자면 메모리 내의 어떤 단어든 간에 물리적 저장 위치와 관계없이 똑같은 속도로 직접 접근 가능하다는 뜻이다.

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

    PARTS:

    DMux8Way(in=load, sel=address, a=loada, b=loadb, c=loadc, d=loadd, e=loade, f=loadf, g=loadg, h=loadh);
    
    Register(in=in, load=loada, out=outa);
    Register(in=in, load=loadb, out=outb);
    Register(in=in, load=loadc, out=outc);
    Register(in=in, load=loadd, out=outd);
    Register(in=in, load=loade, out=oute);
    Register(in=in, load=loadf, out=outf);
    Register(in=in, load=loadg, out=outg);
    Register(in=in, load=loadh, out=outh);

    Mux8Way16(a=outa, b=outb, c=outc, d=outd, e=oute, f=outf, g=outg, h=outh, sel=address, out=out);
}
Ram64
CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];

    PARTS:

    DMux8Way(in=load, sel=address[0..2], a=la, b=lb, c=lc, d=ld, e=le, f=lf, g=lg, h=lh);

    RAM8(in=in, load=la, address=address[3..5], out=oa);
    RAM8(in=in, load=lb, address=address[3..5], out=ob);
    RAM8(in=in, load=lc, address=address[3..5], out=oc);
    RAM8(in=in, load=ld, address=address[3..5], out=od);
    RAM8(in=in, load=le, address=address[3..5], out=oe);
    RAM8(in=in, load=lf, address=address[3..5], out=of);
    RAM8(in=in, load=lg, address=address[3..5], out=og);
    RAM8(in=in, load=lh, address=address[3..5], out=oh);

    Mux8Way16(a=oa, b=ob, c=oc, d=od, e=oe, f=of, g=og, h=oh, sel=address[0..2], out=out);
}

Counter

Counter

계수기(Counter)는 매 시간 단위마다 내부 상태 값을 증가시키는 순차 칩으로, out(t) = out(t - c) + c라는 함수를 연산한다. (일반적으로 c는 1) Counter는 Program Counter와 같은 곳에서 쓰인다. Program Counter는 일반적으로 CPU에 탑재되어 다음에 실행해야 할 프로그램 명령의 주소를 출력하는 기능을 한다.

시간 문제

위에서 알아본 칩들은 모두 순차적이다. 조합 칩은 순차 칩과 다르게 시간을 인지하지 못하기 때문에, 피드백 루프를 넣으면 출력은 입력을, 입력은 출력을 따르므로, 결국 출력이 자기 자신을 따라 결정된다는 문제가 생긴다. 반면에 순차 칩은 DFF에 시간 지연이 있기 때문에 전혀 문제없이 출력을 다시 피드백할 수 있다. 시간 t의 출력은 자기 자신이 아니라 시간 t-1의 출력에 따르기 때문이다. 이 특성 덕에 조합 칩은 피드백 루프가 있을 때 생기는 데이터 경쟁(Data Race)을 피할 수 있다.

이렇게 순차 칩 출력의 이산화(discretization)로 얻게 되는 중요한 부가 효과가 있다. 바로 전체 컴퓨터 아키텍처를 동기화 하는 데 활용할 수 있다는 점이다. 예를 들어 산술 논리 연산 장치(ALU)에 x + y를 계산하라고 명령을 내렸다고 생각해 보자. 그런데 x는 근처 레지스터의 값이고, y는 멀리 있는 RAM 레지스터의 값이다. 여러가지 물리적 제한(거리, 저항, 간섭, 불규칙 잡음 등)때문에 x 및 y에 해당하는 전기 신호가 ALU에 다른 시점에 도착할 가능성이 크다. 조합 칩인 ALU는 시간 개념이 없어서 어떤 데이터 값이 도착하든 계속 값을 더할 뿐이다. 그러므로 ALU가 올바른 x + y 결과를 출력하는 데는 시간이 필요하며, 그 전까지 ALU의 출력은 의미 없는 값이 된다.

이 문제는 사실 ALU의 출력이 항상 어떤 순차 칩으로 전송되므로 이 문제를 신경 쓸 필요가 없다. 그냥 비트 하나가 컴퓨터 아키텍처 내에서 가장 긴 경로를 따라 전송되는 시간보다 클록 사이클 주기를 살짝 더 길게 만들기만 하면 된다. 이렇게 하면 조합 칩이 자신의 상태를 바꾸기 전까지, ALU에서 전송받은 입력값이 유효함을 보장할 수 있다.