- Instruction Count -> 명령어의 개수
- CPI -> 명령어 1개 당 clock cycle
- Clock Cycle Time(CCT) -> clock 1 cycle을 수행하는 시간
여기서 명령어 개수는 사용하는 ISA와 컴파일러의 최적화 옵션에 의해 결정이 되고, CPI와 CCT는 CPU 하드웨어에 의해 결정이 된다.
Datapath : 데이터의 실질적인 처리와 연산을 담당하는 CPU 구성 요소로, 산술 및 논리 연산을 수행하고 레지스터, ALU, multiplexer, 및 bus를 포함한다
Control path : 명령어의 실행을 관리하고 조정하는 CPU의 부분으로, 명령어 해독, 데이터 경로 제어, 및 제어 신호 생성을 담당한다
명령어를 실행하는 과정은 다음과 같다
입력값에 따라 결과값이 바뀌는, 데이터를 연산하는 circuit들을 의미한다
이전의 상태값을 저장할 수가 없다는 한계가 있어 state(sequential) element이 존재한다
레지스터나 메모리에 컴퓨터의 현재 상태나 정보를 저장하기 위한 것이다
state element에는 어떤 것들이 있는지 알아보자
set-reset 자물쇠의 약자로 과거의 상태를 이용해서 다음 상태를 어떻게 유지할 것인지를 결정하는 서킷이다.
서킷을 보면 R과 S 앞에 있는 NOR 게이트의 input에 Q와 !Q가 들어가는 것을 볼 수 있다. 이는 이전의 Q값에 대한 정보이다.
Q와 !Q는 서로 같은 값을 가질 수 없으며 같은 값을 가지게 되는 경우에는 input이 거부된다.
S와 R이 둘 다 0인 경우는 set과 reset을 모두 실행하지 않아 output 신호를 이전의 신호와 똑같이 유지한다
S와 R이 둘 다 1인 경우는 set과 reset을 모두 할 수 없으므로 회로의 구조상 정의에 위반되므로 invalid case로 판단하여 사용하지 않는다
S가 1, R이 0인 경우는 reset을 하지말고 set을 하라는 의미이므로 output이 1 이다.
S가 0, R이 1인 경우는 set을 하지말고, reset을 하라는 의미이므로 output은 0이 된다
여기서 C는 Clock, D는 data의 약자이다
S와 R에 각각 1이 들어가지 않도록 해 invalid case를 처리한 circuit이다
C신호는 D 신호를 뒤로 넘길지 말지 결정한다
C신호가 0이면 S와 R에 둘 다 0값이 인가되어 이전의 신호(state)가 유지된다(latch, 이전의 신호를 자물쇠로 걸어 잠군다)
C신호가 1이면 Q값을 D값으로 변경한다
즉, C신호는 data를 뒤로 넘길지 말지를 결정한다
flip-flop은 D latch를 2개 이어서 만든 회로이며, 데이터를 저장을 한 후 data값을 갱신해서 다음의 값에 저장할 수 있는 로직을 구현하는 회로이다
flip-flop의 특징은 C의 신호가 rising 하거나 falling 할 때 D의 값을 Q의 값으로 가져간다는 특징이 있다.
위 경우에는 C의 신호가 2번째 D latch로 가는 길에 NOT 게이트가 달려있기 때문에 Falling edge에서 D의 신호가 Q의 신호로 인가된다.
Rising edge에 똑같은 기능을 구현하고 싶다면 NOT게이트를 1번째 D-latch로 가는 길에 달아주면 된다.
위와 같이 clock의 주기(rising, falling edge)마다 D의 값이 Q로 유지되며 그 과정에서 약간의 delay가 발생한다.
이와 같이 flip-flop은 주기적으로 clock의 값에 따라 저장된 값을 update해야될 때 사용한다
clock의 주기를 몇으로 설정해야하는가?
일반적으로 clock의 주기와 실행속도는 반비례한다.
예를 들어 clock의 speed가 1GHz와 4GHz라면, 주기는 4GHz인게 더 짧을 것이다.
그래서 우리는 일반적으로 주기를 짧게 해 실행속도를 빠르게 하면 좋겠다고 생각할 것이다.
하지만 그것에는 제약이 있다.
위의 그림을 보면 앞에 있는 flip flop의 출력이 뒤에 있는 flip flop의 입력으로 전달되는 시간에 delay가 존재한다
따라서 combinational logic의 delay보다 clock cycle의 주기를 더 길게해야 combinational logic의 연산값이 전달될 수 있으므로 delay 시간의 최댓값이 하드웨어 설계 시 사용할 수 있는 clock 주기의 최소값을 결정한다
중간에 있는 combinational logic의 복잡도가 높아질수록 clock의 주기를 길게할 수 밖에 없다
요약하자면 최대로 delay되는 시간에 따라 clock의 주기를 결정할 수 밖에 없다
flip-flop은 1비트 단위의
현재 상태의 값을 저장을 하고, 다음 상태의 값을 갱신해 저장하는 동작을 한다.
그래서 flip-flop을 데이터를 저장하는 용도인 레지스터로 사용한다
flip-flop은 1비트 단위로 저장하므로 1비트에 flip flop이 1개라고 볼 수 있다
각 register당 64비트로 이루어져 있으므로, 64개의 flip-flop이 있다
이 레지스터 파일에는 32개의 64비트 레지스터가 존재한다
decoder는 n비트의 input을 받아서 2^n개의 output을 만들어내고 one-hot 코딩을 통해 output값을 선택하는 동작을 한다.
decoder는 연산값의 결과를 어느 레지스터에 저장할지 선택해주는 역할을 한다
레지스터 값을 읽어오는 것은 32by1의 multiplexer(mux)를 이용해서 구현할 수 있다.
입력 값으로 레지스터의 번호가 주어지고 해당 레지스터에 저장된 값이 출력된다
다음과 같이 decoder를 사용해 one-hot형태로 선택된 레지스터에만 C의 신호를 1로 인가하고 나머지 레지스터에는 C 신호를 0으로 인가한 후 AND 연산을 통해 선택된 레지스터에만 연산의 결과값을 저장하도록 한다
이 예제에서는 0번 레지스터의 C신호가 1로 인가가 된다
Instruction memory라는 주어진 주소값에 있는 명령어를 읽어와서 signal을 내보내는 하드웨어가 있다. 그래서 명령어를 fetch하는 과정은 다음과 같다.
Iinstruction Fetch시 다음의 연산이 필요하다
instruction fetch시 필요한 컴포넌트들은 다음과 같다
PC register
Instruction Memory/Cache(우리가 일반적으로 생각하는 Memory는 CPU 외부에 존재하는 DRAM이지만, instruction memory는 CPU 내부에 존재하는 cache를 의미한다)
Adder to increment PC value
R타입의 명령어의 포맷과 R타입의 명령어를 구현하기 위한 회로는 다음과 같다.
register file input
5비트의 레지스터 번호(0~31)
연산 결과 값 write data
RegWrite(때로는 연산 결과 값을 저장하지 않는 연산도 있기에 RegWrite 신호가 0이면 write 연산을 하지않고, RegWrite이라는 신호가 1이 되면 write register에 write data값을 저장한다)
ALU Operation
register file에서 읽은 2개의 피연산자 값을 ALU로 보내주고 ALU가 연산을 한다
ALU는 ALU operation이라는 4비트의 신호를 받는데, 이는 명령어의 종류를 뜻한다
D타입 명령어를 구현하기 위해 다음과 같은 컴포넌트들이 필요하다
Data memory : Load/Store 명령어의 경우 메모리 I/O 작업이 필요하기 때문에 메모리 컴포넌트가 필요하다
Sign-extend : 위의 예제와 같이 LDUR X1, [X2, offset]
명령어의 경우 base address(X2 register 값) + offset 연산을 해주어야한다.
그런데 레지스터의 값은 64bit이나 offset 값은 9bit인 address 필드에서 가져온다. 그래서 instruction에서 읽어온 9비트의 offset값을 64비트로 확장해주는 역할을 한다.
여기서 메모리 주소는 상대 주소로 나타내므로 부호가 필요하다. 따라서 sign 비트를 사용한다.
D타입의 명령어의 포맷과 D타입의 명령어를 구현하기 위한 회로는 다음과 같다.
register file은 레지스터 집합체, data memory는 메모리라고 생각하면된다
LDUR X1, [X2, offset]
를 실행할 때 회로의 흐름을 살펴보자
레지스터 파일 input으로 Read register1에 2라는 레지스터 번호가, Write Register에는 1이라는 레지스터 번호가 들어온다
또한 RegWrite에 1이라는 신호가 인가된다
X2 레지스터를 읽고, 읽은 base address를 1번째 ALU input으로 전달한다
address 필드에서 가져온 offset값이 Sign extend 컴포넌트를 거쳐 64비트로 확장된 후 ALU의 2번째 input으로 전달된다
ALU에서 base address + offset 연산을 통해 실제 메모리 주소값을 얻어낸 후 data memory로 전달한다
data memory에서 MemRead라는 신호가 1로 인가되어 받은 주소값으로 접근해 데이터를 읽고 레지스터 파일의 마지막 input인 Write data로 보내준다.
그 후 Write register 번호에 해당하는 레지스터(X1)에 write 작업을 실행한다
Store 명령어의 경우 레지스터 파일의 Read register2값으로 메인 메모리에 저장할 data가 들어온다
ALU의 2번째 input으로 전달되는 신호에 mux를 추가해 하나의 회로로 R과 D타입의 명령어를 지원할 수 있다.
ALUSrc 신호의 경우 피연산자의 레지스터값을 받을지 정하는 신호이다.
ALUSrc가 0이면 R타입의 명령어이므로 피연산자를 받고
1이면 D타입의 명령어이므로 나머지 1개의 피연산자가 존재하지 않아 받지 않고, offset을 64비트로 확장한 값을 받는다
CBZ X1,offset(=label)
SUBS XZR, X1, X2
B.EQ offset
Branch 명령어에는 다음의 2가지 포맷이 존재한다.
CBZ 명령어의 경우에는 읽은 레지스터값을 0과 비교해 0이면 PC + offset(19bit) 값으로 Branch하는 명령어이다
branch 명령어를 수행하기 위한 datapath 구조는 이렇다
offset값을 64비트로 확장하는 sign extend 컴포넌트가 존재한다
Shiftleft2라는 컴포넌트는 word(4byte) 단위인 Offset값을 word -> byte로 단위 변환을 해주기 위해 4를 곱해주는 역할을 한다
또한 덧셈기와 Zero인지 판단하는 ALU가 존재한다
CBZ X1,offset(=label)
명령어 수행 시 사실 그림과 달리 X1은 위의 a포트가 아닌 아래의 b포트로 이동한다.
그 후 Zero ALU에 ALU Operation이라는 신호가 0111로 인가된다.
ALU는 pass input b 연산을 통해서 b포트로 들어온 값을 result값으로 뱉어낸다.
Zero ALU는 Zero라는 신호를 result값이 0이면 1, 0이 아니면 0로 인가한다
Zero가 1이면 PC + offset, 0이면 PC + 4 연산을 수행한다
SUBS 명령어를 수행 시 Zero ALU에는 두 개의 레지스터 값이 모두 들어오게 되고 ALU Operation 신호가 0110으로 인가되어 뺄셈 연산을 수행해 result값이 0인지 아닌지 체크한 후 PC값을 계산한다
PCSrc는 다음 명령어를 실행할지 아니면 분기할 지 결정하는 신호이다
ALU에서 만들어낸 Zero값을 PCSrc라는 신호로 보낸다
PCSrc 신호를 받는 mux는 1이면 PC + offset값을, 0이면 PC + 4 값을 선택한다
Single-Cycle의 전체 Datapath는 이렇다
datapath에서 mux나, ALU, 레지스터 파일, data memory등 컴포넌트에 다양한 신호들이 인가되었다.
다양한 신호들이 존재하는만큼 그 신호를 제어할 유닛이 필요한데, 그 유닛이 control unit이다.
control unit은 datapath를 관리하기 위해 컨트롤 신호들을 생성해서 제어하는 요소이다.
control unit에는 상위 계층에서 전체 신호 전달을 담당하는 Main Controller(Control)와 ALU Operation을 결정하는 ALU Control이 있다.
이렇게 두 계층으로 나눈 이유는 속도라던지 하드웨어 복잡도를 낮추기 위해서이다.
ALU 연산은
에서 사용이 된다
그렇기에 우리는 ALU에 인가되는 신호를 세분화해서 제어할 필요가 있고 그것을 담당하는 것이 ALU Control이다
ALU Control이 ALU Operation을 정하는 2단계는 다음과 같다.
이 Main Controller(Control)가 2비트의 ALUOp 값을 생성해 ALU Control으로 보내준다.
이 단계는 ALUOp값을 통해 미리 해당 명령어의 대분류를 정하는 단계이다.
ALU Control이 2비트의 ALUOp값과 명령어의 11비트 opcode를 받아 4비트의 ALU Operation을 결정한다.
D타입, CBZ의 경우 ALUOp값이 정해져있지만, R타입의 경우 연산이 여러가지 이므로 ALUOp값으로는 구분이 불가능하다. 따라서 ALU Control이 필요한 것이다.
다음은 Control이 생성하는 signal을 정리한 표이다.
X로 돼있는 부분은 Don't Care비트이다.
CBZ의 경우 opcode로 총 8비트를 할당하지만 6비트만 사용을 하고
나머지 2자리는 0으로 채운다.
Reg2Loc: 2번째 레지스터 파일에 들어갈 값을 정하는 신호이다
ALUSrc : Register files 뒤에 위치한 mux에게 전달되며, register files에서 읽은 데이터와 immediate 중에서 어떤 값을 ALU에게 전달할 것인지를 결정한다.
MemtoReg : Data memory unit에서 읽은 데이터와 ALU에서 계산된 데이터 중에서 어떤 데이터를 register에 전달해서 write을 할 건지를 결정한다.
RegWrite: 레지스터에 데이터를 저장할 건지 아닌지를 결정한다.
MemWrite, MemRead: 메모리에서 데이터를 읽을건지 쓸건지에 대한 정보를 Data memory unit에 전달한다.
Branch: 분기가 되면은 PC값에 label 데이터를 전달하기 위해 사용된다.
ALUOp0, 1: ALU control에게 어떤 Type의 명령어가 들어왔는지 알려주는 signal이다.
이런 이유들 때문에 실제로는 single cycle 구조를 사용하지는 않고, pipelining을 통해 성능을 향상시킬 수 있다