Instruction Processing Process
명령어 처리 과정
- Program Termination 전까지 아래 4단계가 반복된다.
1. Fetch
- PC가 저장하고 있는, 실행될 명령어가 저장된 주소에서 해당 명령어를 CPU로 가져온다.
2. Decoding
- Extract Op-Code : 해당 명령어가 어떤 종류의 연산을 지시하는지 해석한다.
- Extract Operands : 명령어가 지시한 피연산자(Register, Immediate Field 등)를 해석한다.
3. Execution
- 앞서 해석한 연산의 종류에 따라 ALU를 적절히 활용한다.
- 산술/논리 연산의 경우, ALU를 산술/논리 연산에 활용한다.
- Read/Write 연산의 경우, ALU를 접근하고자 하는 메모리 주소를 계산하는데에 활용한다.
4. Next Fetch
- 현재 명령어 수행을 마치고, 다음 명령어를 실행하기 위한 단계이다.
- Jump, Branch 명령 이외의 명령어들이 실행되기 위해 PC값에 4만큼이 더해진다. (MIPS의 모든 명령어는 4byte 이다.)
Instruction Fetch (명령어 패치)
- PC가 저장하고 있는 주솟값으로부터 메모리에 접근하여, 해당 주소에 있는 32Bits 길이의 명령어가 MIPS CPU로 반환된다.
- 이후 Clock 신호에 맞춰, PC값은 ALU의 Add 연산에 의해 4만큼 증가된 값이 저장된다.
- MIPS에서 Reset 신호가 PC에 입력되면, PC는 0xBFC0 0000으로 초기화된다. (0xBFC0 0000은 시스템이 가장 먼저 실행해야 할 명령어가 저장된 주소로 Reserved된 값이다.)
- MIPS에서 Instruction Fetch를 수행하는 VHDL 코드 예시이다.
1. pcreg 모듈
- 입력 포트 : clk(1bit의 클럭 신호), reset(1bit의 리셋 신호), [31:0] pcnext(32bits의 다음 실행할 명령어 주소)
- 출력 포트 : [31:0] pc(32bits의 현재 실행할 명령어 주소)
※ reg는 always문에 대응되는 VHDL의 문법적 요소이다.
- always문에서는 Rising Edge Clock 방식을 채택함을 알 수 있다.
- reset 신호가 입력될 경우, PC값을 0으로 초기화한다. (본래, 0xBFC0 0000이어야 하나, 0으로 가정한다.)
- 아닐 경우, PC를 pcnext로 초기화한다.
2. myadder 모듈
- 입력 포트 : [31:0]a(32bits의 첫 번째 피연산자 값), [31:0]b(32bits의 두 번째 피연산자 값)
- 출력 포트 : [31:0]y(32bits의 연산 결과)
3. mips 모듈
- pcreg와 myadder 모듈은 각각 Behavioral Modeling 방식으로 구현되어 있으며, 이 두 모듈을 포함한 mips 모듈은 Structural Modeling 방식으로 구현되었다 할 수 있다.
- pcreg와 myadder를 Instance로 활용하여 Instantiation하고 있다. (해당 모듈에서 정의한 입출력 인터페이스를 따른다.)
- 입력 포트 : clk(1bit의 클럭 신호), reset(1bit의 리셋 신호)
- 출력 포트 : [31:0] pc(32bits의 현재 실행할 명령어 주소)
※ ALU에 입력되는 값 4는 내부에서 사용되는 값으로, mips 시스템 전체에서 봤을 때, 입력 신호라 할 수 없다.
Memory Writing (메모리 입력)
- 아래 예시는 반도체 구조를 고려하지 않은, Generic한 방법이다.
mem Module
\(\texttt{Input Port}\)
- clk : 클럭 신호 1bit를 받는 포트
- MemWrite : 메모리 쓰기 연산을 제어하는 Write Control Signal을 받는 포트, MemWrite = 1이면, 메모리 쓰기가 허용된다.
- Address : 64words의 메모리를 구분짓기 위한 6bits 신호를 받는 포트
- WriteData : 메모리에 쓸 데이터를 나타내는 32bits 신호를 받는 포트
\(\texttt{Output Port}\)
- ReadData : 요청받은 데이터를 32bits 신호로 내보내는 포트
\(\texttt{reg [31:0] RAM[63:0];}\)
- 본 모듈은 32bits 레지스터가 총 64개 포함된 구조이다.
memfile.dat
- \(\texttt{MemWrite}\) 신호가 1이면, 메모리 파일의 \(\texttt{Address}\) 위치에 \(\texttt{WriteData}\) 데이터를 입력한다.
- CPU가 요청한 데이터는 \(\texttt{ReadData}\)로 리턴된다.
- ReadControl을 따로 설계하지 않는 이유는, Read 연산의 경우 메모리가 수정될 위험이 없고, CPU 입장에서 반환된 데이터가 필요없다고 간주되면 폐기하면 그만이기 때문이다.
Instruction Decoding (명령어 해독)
- Fetch한 명령어의 6bits OP-Code를 해석하여 명령어 타입별로 비트 필드를 구분한다. (R, I, J Format)
- Op-Code 와 funct를 통해 연산의 종류가 결정된다.
- Operand 필드의 레지스터 번호는 레지스터 파일에 전달되어 적절한 레지스터로 선택된다.
- Operand 필드의 Immediate 상수는 명령어 종류에 따라 Sign-Extension 혹은 Zero-Extension이 수행되어 32bits 데이터로 확장된다.
- 오른쪽 하단 부분은 Instruction Fetch를 위한 소자들이 배치되어 있다.
- 명령어의 OP-Code와 funct를 해석하여 sign_ext 신호와 RegWrite 신호를 결정한다.
- 레지스터 파일에서는 메모리 컨트롤러에 의해 명령어에서 지목한 레지스터에 데이터를 입력하거나, 지목된 레지스터를 리턴한다.
- 피연산자가 Immediate 상수일 경우, Control Unit에서 지시한대로 Sign_extension 혹은 Zero-Extension을 수행하여 32bits로 확장한다.
regfile Module
\(\texttt{Input Port}\)
- clk : 클럭 신호 1bit를 받는 포트
- RegWrite : 레지스터 쓰기 연산을 제어하는 Write Control Signal을 받는 포트, RegWrite = 1이면, 데이터 쓰기가 허용된다.
- ra1, ra2 : 명령어에서 피연산자로 지정된 두 레지스터의 위치를 구분짓는 5bits 신호를 받는 포트
- wa : 데이터가 쓰여질 레지스터를 구분짓는 5bits 신호를 받는 포트
- wd : 레지스터에 쓸 데이터를 나타내는 32bits 신호를 받는 포트
\(\texttt{Output Port}\)
- rd1, rd2 : 사용해야 하는 두 레지스터에 저장된 2개의 값을 나타내는 32bits 신호들을 출력하는 포트
\(\texttt{reg [31:0] rf[31:0];}\)
- 본 모듈은 32bits 레지스터가 총 32개 포함된 구조이다.
\(\texttt{always @ (posedge clk)}\)
- 클럭 신호가 Rising Edge임과 동시에 RegWrite 신호가 1이면 wa 위치에 해당하는 레지스터에 wd 데이터를 쓴다.
\(\texttt{assign rd1 = (ra1 != 0) ? rf[ra1] : 0;}\)
- ra1이 0이 아니라면(어떤 레지스터를 지목하고 있다면) ra1이 지목하는 레지스터에 저장된 데이터를 rd1을 통해 읽어온다.
\(\texttt{assign rd2 = (ra2 != 0) ? rf[ra2] : 0;}\)
- ra1이 0이 아니라면(어떤 레지스터를 지목하고 있다면) ra1이 지목하는 레지스터에 저장된 데이터를 rd1을 통해 읽어온다.
sign_zero_ext Module
\(\texttt{Input Port}\)
- sign_ext : sign_ext = 1일 경우, imm 상수가 부호가 있는 수치임을 의미하는 것으로 Sign-Extension을 수행한다.
- a : 입력받은 16bits imm상수이다. (32bits로 확장되게 된다.)
\(\texttt{Output Port}\)
- y : 입력받은 imm상수를 Sign-Extension 혹은 Zero_extension을 수행하여 32Bits로 확장한 신호들을 출력하는 포트
\(\texttt{always @ (*)}\)
- always 구문의 에스테리스크(*)는 항상 Update함을 지시한다.
\(\texttt{if (sign_ext) y <= {{16{a[15]}}, a};}\)
- sign_ext 신호가 1이면, a의 MSB(16bit) 값으로 채워진 16bits를 a에 concatenation 한다.
\(\texttt{else y <= {{16{16{1'b0}}}, a};}\)
- sign_ext 신호가 1이 아니면, a에 0으로 채워진 16bits를 concatenation 한다.
Instruction Execution (명령어 실행)
- Control Unit에서 OP-Code와 funct를 해석하여, MemWrite, MemtoReg, ALUSrc 신호를 생성한다.
MemWrite : 메모리에 데이터를 입력할지를 결정하는 1bit 제어 신호
MemtoReg : 연산 결과를 저장하는 위치 (레지스터/메모리)를 결정하는 1bit 제어 신호
ALUSrc : 피연산자가 레지스터에 저장된 값인지, Immediate 상수인지를 구분짓는 1bit 제어 신호
- 간단한 2 in 1 Multiplexer를 구현하는 VHDL 코드이다.
- MUX는 제어 신호에 따라 다수의 입력값 중 하나를 출력에 매칭시킨다.
- 좌측 하단의 mux2 모듈처럼, Parameterized된 코드를 구현함으로써, 값의 크기에 관계없이 MUXing을 진행할 수 있게 된다.
Ex. \(\texttt{beq}\) Instruction이 실행되는 MIPS CPU 내부구조 예시
- Destination = {(PC + 4)[31:28], (imm << 2)[27:0]}
- 두 피연산자의 차이가 0이며 분기하는 메커니즘이다.
- PCSrc : 분기 여부를 결정하는 신호이다.
- beq는 분기 명령어이므로, 회로가 Instruction Fetch 부분과 연관이 깊음을 확인할 수 있다.
Ex. \(\texttt{j}\) Instruction이 실행되는 MIPS CPU 내부구조 예시
- Destination = {(PC + 4)[31:28], jump target, 2'b00}
- 앞의 MUX의 결과값에 관계없이 Jump하는 메커니즘이다.
Single Cycle MIPS Diagram
- 본 포스트에서 MIPS는 단일 사이클 동안 Instruction Fetch, Decoding, Execution, Next Fetch가 수행된다는 가정 하에 여러 가지 예시를 설명한다.
(즉, 다음 Rising Edge Clock Cycle이 입력되기 전에 한 명령어에 대한 Fetch, Decoding, Execution, Netx Fetch가 수행된다. Propagation Delay은 논외로 간주한다.)
- 검은색 모듈과 라인들은 Data Path를 도식화한 것이다.
- 파란색 모듈과 라인들은 Control Unit과 Control Signal들을 도식화한 것이다.
Example. lw 명령어 처리 과정
lw $s2, 80($0)
# $0 레지스터에 저장된 주솟값에 80(Byte 단위)만큼 더한 위치에 있는 데이터를
# $s2 레지스터에 저장한다.(읽어온다.)
# lw 명령어는 I-Type 명령어이다.
- Single Cycle MIPS에서 처리된다고 가정한다.
- 즉, 하나의 명령어가 한 Clock Cycle에 처리된다.
1. Instruction Fetch
- PC에는 수행해야 할 명령어가 위치한 메모리의 주소가 32bits값으로 저장되어 있다.
- PC값은 Clock에 관계없이, 명령어가 들어있는 메모리에 전달된다.
- PC값이 메모리에 전달되어, 해당 메모리 주솟값에 위치한 명령어(Instr)가 출력된다.
- PC'은 추후 Next Fetch과정에서 행해지는 연산의 결과(PC+4)를 의미한다.
2. Instruction Decoding
- Instr의 상위 6bits(OP-Code)를 해석하여 I-Type의 명령어임을 인식한다.
- I-Type Instr의 rs 필드[25:21]의 값이 레지스터 파일의 A1에 입력되어, A1이 지목하는 레지스터에 저장된 값이 RD1으로 출력된다.
- I-Type Instr의 Immediate 필드[15:0]의 값이 32bits로 Sign-Extend되어 "출력된다.(SignImm)
3. Instruction Execution
- SrcA는 RD1에서 출력된 Instr의 rs 필드가 가리키는 레지스터에 저장된 값이다.
- SrcB는 SignImm이다. (32bits로 확장된 Immediate필드 값이다.)
- ALU에서는 ALUControl 신호에 의해 SrcA와 SrcB를 피연산자로한 연산을 수행한다.
- 이 경우, 수행되는 연산(010)은 덧셈이다. (즉, 베이스 주소 $s0에 상수 80을 더한다.)
- 32bits 연산결과(ALUResult)는 곧바로 메모리에 입력된다.
- ALU의 Zero 포트는 두 피연산자의 차이가 0일 때, 1로 설정되는 값을 출력하는 단일 비트 출력 포트이다.
(추후에, beq 분기명령어를 처리하는 데 사용된다.)
- ALUResult를 입력받은 메모리는 ALUResult가 가리키는 메모리 위치에 저장된 값(ReadData)을 레지스터 파일의 WD3에 입력시킨다.
- 레지스터 파일의 A3는 데이터를 입력할 레지스터의 위치(5bits)를 입력받는 포트이며, WD3는 레지스터에 Write할 데이터 값(32bits)을 받는 포트이다.
- RegWrite가 1로 설정된 상태에서 클럭이 Rising-Edge로 전환될 때, A3가 지시하고 있는 레지스터에 WD3 값이 쓰이게 된다. (클락 신호에 영향을 받는 유일한 부분이다.)
- 클럭이 Rising-Edge가 되기 전, RegWrite Control 신호가 반드시 1로 설정되어 있어야 한다.
4. Next Fetch
- PC값은 클럭 신호에 관계없이 명령어가 저장된 메모리의 입력포트 A에 전달된다. (이 부분이 조합회로로 구성되어 있기 때문이다.)
- PC'에는 PC+4에 해당되는 값이 저장되어 있다.
- 이 전체 회로에 클럭이 Rising-Edge가 되는 순간, PC는 PC'값으로 설정된다.
- 즉, 클럭이 Rising-Edge로 전환되는 순간 PC값이 업데이트 된다.
Example. \(\texttt{sw}\) 명령어의 Instruction Execution 과정
- sw명령어는 lw명령어와 반대로, 메모리에 데이터를 입력하는 연산이다.
- sw명령어의 rs필드[20:16]가 지목하는 레지스터 위치가 A2로 전달된다.
- 레지스터는 A2가 지목하고 있는 레지스터에 저장된 값을 RD2로 출력한다.
- RD2는 메모리에 WD로 전달된다.
- MemWrite 신호가 1로 설정되어 있는 상황에서, 클럭이 라이징 엣지가 되는 순간, 메모리에서는 A가 지목하고 있는 메모리 위치에 WD값을 입력한다.
Example. 산술/논리 연산 명령어 처리 과정 (\(\texttt{and, sub, and, or}\))
- 산술/논리 연산 명령어는 R-Type 명령어이다.
- 산술/논리 연산의 결과(ALUResult)는 메모리에 쓰이지 않기 때문에 MemWrite 신호가 0으로 설정된다.
- 산술/논리 연산의 결과는 레지스터에 입력되어야하기 때문에, MemtoReg Control 신호가 0으로 설정된 MUX에 의해 레지스터 파일의 WD3 포트에 입력된다. (즉, R타입 명령어인 경우, MemtoReg가 0으로 설정된다.)
- R타입 명령어에서 rd 필드에는 목적지 레지스터의 위치값이 저장되어 있다.
- RegDst Control 신호가 1로 설정된 MUX에 의해 레지스터 파일의 A3 포트에 rd 필드에 저장된 값이 입력된다.
(즉, R타입 명령어는 rd필드[15:11]가 존재하기 때문에 RegDst가 1로 설정되고, I타입 명령어는 목적지 레지스터가 rt필드[20:16]에 저장되기 때문에 RegDst가 0으로 설정된다.)
- 레지스터 파일은 A3가 지목하고 있는 레지스터에 WD3 값을 입력하게 된다. (즉, 산술/논리 연산결과를 rd에 저장한다.)
※ MUX에 입력되는 파란색 Signal또한 Control Unit에서 생성되는 신호이다.
Example. \(\texttt{beq}\) 명령어 처리 과정
- \(\texttt{beq}\) 명령어는 I-Type 명령어이다.
- Instruction Decoding 과정에서 \(\texttt{beq}\) 명령어가 분기 명령어임이 확인되어, Branch Control 신호가 1로 설정된다.
- rs필드[25:21]와 rt필드[20:16]에 해당되는 레지스터 파일에 저장된 값들에 대한 뺄셈 연산이 ALU에서 수행된다.
(뺄셈 연산에 해당되는 ALUControl값은 110이다.)
- 이 때, MemWrite Control 신호는 0으로 설정되어 연산 결과가 메모리에 쓰여지지 않는다.
- 또한, RegWrite Control 신호 또한 0으로 설정되어 연산 결과가 레지스터에도 쓰여지지 않는다.
(이로 인해, RegDst Control 신호는 Don't Care이다.)
- 연산 결과가 사용되는 부분이 없기 때문에 MemtoReg Control 신호는 x(Don't Care)값으로 설정된다. (어떤 값으로 설정되어도 상관없다.)
- 단, ALU의 연산 결과가 0(두 피연산자가 같은 경우)이라면 Zero 포트에 1이 출력된다.
- 즉, Branch Signal이 1이고, ALUResult가 0이면 PCSrc Signal은 1로 설정되어 MUX에 전달된다.
- Imm 필드에 저장된 Offset값은 Sign\Extension이 수행된 후, 4가 곱해진다.* (2만큼 Shift)
- ALU에서는 (Offset * 4) 값과 (PC + 4) 값이 더해져서 PCBranch 포트로 출력된다.
- 즉, 다음 실행될 명령어의 주소값이 저장되는 PC' 레지스터에는 (Offset * 4) + (PC + 4) 가 저장되어, 클럭이 Rising-Edge로 전환되는 순간, PC값이 업데이트된다.
* 조건 분기 명령어에서 Imm필드에는 Byte 단위가 아닌, Word 단위의 Offset 값이 저장되어야 함을 알 수 있다. (Imm 필드값에 4가 곱해질 예정이기 때문이다.)
Example. \(\texttt{or}\) 명령어 처리 과정
- 회색 파선 : Next Fetch을 수행하는 회로 부분이다.
- 파란색 파선 : Instruction Fetch, Decoding, Execution을 수행하는 회로 부분이다.
- \(\texttt{or}\) 명령어는 R-Type 명령어이다. 즉, Data Memory와는 연관이 없으며, 3개의 피연산자 모두 레지스터 파일에서 읽고, 쓰게된다.
- \(\texttt{or}\) 연산에 해당되는 ALUControl 신호는 001이다.
이미지 출처 : 권건우 교수님 강의록