Pipeline implementation
multicycle cpu의 data path에서 크게 고칠건 없다.
근데 pipeline register같은 추가적인 레지스터들이 필요할 수 있다. 컨트롤 시그널과 데이터를 포워딩하기 위함. 그리고 항상 그렇듯이, 언제 어디서 control information을 생성할지는 implementation에 달려있다.
하지만 한 가지 옵션은 ID stage에서 control information을 만들어내고 파이프라인 stage를 통해 forwarding 시키는 거다. 이후 몇 개는 계속 쓰이고, 몇 개는 아닐꺼기 때문에 전부다를 forwarding할 필요는 없다.
Hazards
pipeline CPU에서는 hazard 문제가 발생한다.
hazard는 그 다음 사이클에서 next instruction을 제대로 실행하지 못한다는 걸 의미한다.
data hazard, control hazard, structural hazard 등이 있다.
기본적으로 hazard가 일어나는 이유는 이전 instruction에서 바뀐 변화들을 보지 못하기 때문이다. 그 architectural stage의 변화를 보기위해서 기다리지 않는다면.
하지만 우린 모든 instruction이 이전 꺼를 기다리기 바라지 않기에, 몇 가지 방법이 잇다.
Data Hazard
forwarding을 하려면 우리는 forwarding unit 이라는게 있어서, 이런 상황을 detect하고 어디로 bypassing 할지 정해야한다.
Control Hazard
next pc를 예측하고, 만약에 mis-predict하다면, 한 마디로 next PC 값이 틀렸다면, pipeline stage를 flush 하고 correct instruction을 fetch 해야한다.
branch prediction을 하기 위해 branch prediction 이라는 unit을 만들어야한다.
Pipeline CPU lab
1.
instruction memory 와 data memory는 물리적으로 분리되어야한다.
2.
딴거는 뭐 다 똑같고
3.
5 stage pipeline 을 implement 해야한다.
4.
data hazard와 control hazard를 해결해야한다.
5.
JAL
6.
JALR
7.
BEQ,BNE,BLT, BGE, BLTU, BGEU
8.
LW
9.
SW
10.
ADDI, SLTI, SLTIU, XORI, ANDI, SLLI, SRLI, SRAI
11.
ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND
12.
terminal condition은 똑같다.
forwarding unit을 쓰면 95프로 점수
alwyas taken 으로 해서 BTB를 쓰면 100프로 점수
always taken으로 해서 2-bit saturation이랑 BTB를 쓰면 110프로 점수
구현해야하는 기능
5 Stage & pipeline
•
immediate register를 사용
•
매 posedge마다, immediate register에 쓰자.
•
IF, ID, EX, MEM, WB의 결과물을 immediate register에 적절히 연결하자.
•
필요한 레지스터들
IF/ID
ID/EX
EX/MEM
MEM/WB
각 단계마다 해야할 것들
AUIPC | LUI | JAL | JALR | BEQ, BNE, BLT, BGE, BLTU, BGEU | LG, LH, LW, LBU, LHU | SB, SH, SW | ADDI (arithmetic), SLTI, SLTIU (conditional), XORI, ORI, ANDI(logical), SLLI, SRLI, SRAI (shift) | ADD, SUB{signed,unsigned}, SLL, SRL, SRA, (Shift) SLT, SLTU,(Conditional) XOR, OR, AND(Logical) - Rtype | |
IF | pc = next_pc (sequential)
IR ← MEM[pc](sequential)
next_pc = pc +4 | pc = next_pc (sequential)
IR ← MEM[pc](sequential) next_pc = pc +4 | IR ← MEM[pc](sequential)
pc = next_pc (sequential) | pc = next_pc (sequential)
IR ← MEM[pc] (sequential) | pc = next_pc (sequential)
IR ← MEM[pc] (sequential)
Imm 생성
next_pc = pc + 4 | pc = next_pc (sequential)
IR ← MEM[pc] (sequential)
next_pc = pc + 4
Imm 생성 | pc = next_pc (sequential)
IR ← MEM[pc] (sequential)
next_pc = pc + 4
Imm 생성 | pc = next_pc (sequential)
IR ← MEM[pc] (sequential)
next_pc = pc + 4
Imm 생성 | pc = next_pc (sequential)
IR ← MEM[pc] (sequential)
next_pc = pc + 4
Imm 생성 |
ID | pass | pass | pass | Ra1 읽기 | Ra1과 Ra2에서 값 읽기
Ra1과 Ra2로부터 값 비교해서 ControlUnit에 넣기
ControlUnit에서 taken인지 not taken인지 판단하고 control signal에 mispredict인지 아닌지 넣기 | Ra1 값 가져오기 | Ra1, Ra2 값 가져오기 | Ra1 값 가져오기 | Ra1 값 가져오기 |
EX | ALUOut ← pc + Imm | pass | ALUOut ← pc + imm
next_pc = ALUOut | ALUOut ← Ra1 + Imm
next_pc = ALUOut | ALUOut ← pc + Imm
next_pc = ALUOut
mispredict라는 시그널이 있으면, pc값 update하고 IF/ID, ID/EX 값 nop 처리하기 | ALUOut ← Imm + Ra1 | ALUOut ← Imm + Ra1 | ALUOut ← Imm + Ra1 | ALUOut ← Imm + Ra1 |
MEM | pass | pass | pass | pass | pass | MDR ← MEM[ALUOut] | MEM[ALUOut] ← Ra2 | pass | pass |
WB | RF[rd] ← ALUOut | RF[rd] ← Imm | RF[rd] ← ALUResult = pc + 4 | RF[rd] ← ALUResult = pc + 4 | pass | RF[rd] ← MDR | RF[rd] ← aluOut | RF[rd] ← ALUOut | RF[rd] ← ALUOut |
data forwarding
•
Forwarding Control Logic을 짜자
•
Forwarding Control logic에 꽂혀야할 값들
◦
EX 단계의 Rs1, Rs2 from inst(ID/Ex)
◦
Mem 단계의 Rd from inst(Ex/Mem)
◦
Wb단계의 Rd from inst(Mem/WB)
◦
MEM 단계에 있는 RegWrite 시그널 from control signal(Ex/Mem)
◦
WB 단계에 있는 RegWrite 시그널 from control signal(Mem/WB)
•
Forwarding Control logic
ForwardA,B
◦
00: ID/EX에서 온 값을 쓴다.
◦
10: Ex/Mem에서 온 값을 쓴다. 즉 Ex Hazard(dist 1)
◦
01: MeM/WB에서 온 값을 쓴다. 즉. MeM Hazard (dist 2)
Ex Hazard
MEM Hazard
EX 단계에서 detect
load-use data hazard
•
load 같은 경우는 load 바로 그 다음 단계를 bubble로 하고 hazard detect 해야함
detect 언제하지
Stall 하는 법
Branch Prediction
•
사용할 방법은 BTB + 2-bit saturation
•
Prediction은 IF 단계에서
•
BTB에 5000개의 row
•
BTB → 2-bit saturation, target address, 총 14bit
BTB
input: pc
output: taken, not taken to mux
mux에는 pc+4와 BTB의 target address 가 꽂혀있다. taken이면 target address 아니면 pc+4
ID단계에서 taken인지 not taken인지에 대한 결과를 볼 수 있다.
EX 단계에서 target address의 주소를 볼 수 있다.
그냥 EX 단계로 BranchComp를 옮기자
stall 시킬 때는
input: pc, pc_ID/EX, inst_ID/EX, isTaken, target_address
output: 2-bit에 해당하는 predict값
•
input: pc, pc_ID/EX, inst_ID/EX, isTaken, target_address
•
output: predict, target_address
IF 단계
EX단계
Instruction | regWrite | isSign | wbSel | mem_WEN |
ADD, SUB{signed,unsigned}, SLL, SRL, SRA, (Shift) SLT, SLTU,(Conditional) XOR, OR, AND(Logical) - Rtype | 1 | 00 | 1 | |
ADDI (arithmetic), SLTI, SLTIU (conditional), XORI, ORI, ANDI(logical), SLLI, SRLI, SRAI (shift) | 1 | 00 | 1 | |
LG, LH, LW, LBU, LHU | 1 | 01 | 1 | |
SB, SH, SW | 0 | 00 | 0 | |
JAL | 1 | 10 | 1 | |
JALR | 1 | 10 | 1 | |
BEQ, BNE, BLT, BGE, BLTU, BGEU | 0 | 00 | 1 | |
isNop
→ misPredict 가 1일때 isNop_IF_ID ⇒ 1, 아닐 때 0
→ misPredict가 1일 때 isNop_ID_EX ⇒ 1, 아닐 때 0
misPredict → isTaken값과 pc_ID_EX값에 있는 pred 값이 다를 때 1, 아닐 때 0, 이 때 INST_ID_EX의 명령어가 J-type이거나 B-type일 때만 조건문, 아닐 때는 전부 0
Control Unit
1.
INST_ID_EX 가 B-type이거나 J-type인 경우만 isTaken을 변경하도록 한다. 그외에는 건들지 말자
2.
misPredict가 1일 때 isNop_IF_ID, 및 isNop_ID_EX 를 1로 만든다. 그외에는 항상 0으로 설정한다.
INST_IF_ID 에서 명령어 받아서 decode
•
regWrite : 쓰면 1, 아니면 0
•
wbSel : 00이면 alu_out, 01이면 D_MEM_IN, 10이면 pc_EX_MEM + 4
•
memWrite : 쓰면 1, 아니면 0
•
alu_control: 그냥 저냥
•
is_sign: unsigned이면 0, 아니면1
•
immSel: 타입별로 그냥 종류
•
ASel: 1이면 pc 0이면 이전 mux
•
BSel: 1이면 imm, 0이면 이전 mux
•
memByte: 항상 1111
isTaken 결정
필요 요소: INST_ID_EX, BrEq, BrLt
if(inst == J-type and inst == B-type)
→ 명령어 종류 따라 BrEq, BrLt를 보고 isTaken 결정
else
isTaken = 0
isNop_IF_ID & isNop_ID_EX_c
필요요소: misPredict, inst_ID_EX
if(INST_ID_EX == J-type and INST_ID_EX = B-type)
if(misPredict)
→ isNop_IF_ID = 1, isNop_ID_EX_c = 1
else
isNop_IF_ID = 0, isNop_ID_EX_c = 0