Lab2:forwarding and Axies
1 实验步骤¶
1.1 在 lab1 的基础上实现 Forwarding 机制¶
- 建议在 lab1 的基础上实现 forward 通过测试,再进行第 2 步 添加了 Forwarding 的数据通路:
![[img/PipelineWithForwarding.png]]
另外,修改了 RegFile,使得使用上升沿的同时能解决同时读寄存器读写冲突。
`timescale 1ns / 1ps
module Regs(
input clk,
input rst,
input we,
input [4:0] read_addr_1,
input [4:0] read_addr_2,
input [4:0] write_addr,
input [63:0] write_data,
output [63:0] read_data_1,
output [63:0] read_data_2
);
integer i;
reg [63:0] register [1:31]; // x1 - x31, x0 keeps zero
assign read_data_1 = (we & write_addr == read_addr_1 & write_addr != 0) ? write_data : (read_addr_1 ? register[read_addr_1] : 0); // read
assign read_data_2 = (we & write_addr == read_addr_2 & write_addr != 0) ? write_data : (read_addr_2 ? register[read_addr_2] : 0); // read
always @(posedge clk or posedge rst) begin
if (rst == 1) for (i = 1; i <= 31; i = i + 1) register[i] <= 0; // reset
else if (we == 1 && write_addr != 0) register[write_addr] <= write_data ; // write register
end
endmodule
1.2 根据 3.2 的方法将 AXI4-lite 框架补充完整¶
1.2.1 将 PipelineCPU 封装成 Core 和 RAM¶
1.2.2 编写 FSM, Core 的接口和 RaceController¶
在 Core 中的修改主要是把提前一拍传入 Bram 的信号(主要是 pc 和 mem 阶段 )移回对应的阶段以及重写 RaceController,并且在做地址的接出时不做截断,传完整地址。
起始状态 | 目标状态 | 条件 | 任务 |
---|---|---|---|
IDLE | DATA | wen_cpu | ren_cpu (MEM请求) | mem_stall 信号和 if_stall 信号 |
IDLE | INST | MEM阶段未发送请求, IF 阶段发送请求 | 发送 IF 请求信息给 CoreAxi_lite ,开启 if_stall 信号 |
DATA | DATA | CoreAxi_lite 未返回 valid 信号 | 等待,保持 mem_stall 和 if_stall 信号 |
DATA | IDLE | CoreAxi_lite 返回 valid 信号 | 关闭 mem_stall 信号,关闭给 CoreAxi_lite 的请求 |
DATA | INST | 不会发生 | 不会发生 |
INST | INST | CoreAxi_lite 未返回 valid 信号 | 等待,保持 if_stall 信号 |
INST | IDLE | CoreAxi_lite 返回 valid 信号 | 关闭 if_stall 信号,关闭给 CoreAxi_lite 的请求 |
INST | DATA | 不会发生 | 不会发生 |
由于不存在 MEM 阶段和 IF 阶段都没有发起请求的情况,我在实际设置状态机时没有使用到 if_request
信号,在 core 中默认该信号一直开启。
assign mem_request = wen_cpu | ren_cpu
always @(posedge clk or negedge rnst) begin
if (~rstn) begin
state <= IDLE;
keep_if_stall = 1;
end
else if (state == IDLE) begin
if (mem_request) begin
state <= DATA;
keep_if_stall = 1;
end
else begin // 由于不存在两种请求都没有的情况,直接else了
state <= INST;
keep_if_stall = 1;
end
end
else if (state == INST) begin
if (valid_mem) begin
state <= IDLE;
keep_if_stall = 0;
end
else bigin
state <= INST;
keep_if_stall = 1;
end
end
else if (state == DATA) begin
if (valid_mem) begin
state <= IDLE;
end
else begin
state <= DATA;
keep_if_stall = 1;
end
end
end
assign rdata_cpu = rdata_mem;
assign inst = pc[2] ? rdata_mem[63:32] : rdata_mem[31:0];
assign wmask_mem = wmask_cpu;
assign wdata_mem = wdata_cpu;
assign address_mem = state == INST ? pc : address_cpu;
assign if_stall = keep_if_stall;
assign mem_stall = mem_requets & ~valid_mem;
assign ren_mem = (state == DATA & ren_cpu) | (state == INST);
assign wen_mem = state == DATA & wen_cpu;
RaceControl 的传入信号中,jump
信号接入 decode_exe[19] & branch_flag_exe
,即确认跳转信号。
module racecontrol_Axi(
input jump,
input [4:0] rs1_exe,
input [4:0] rs2_exe,
input [4:0] rd_mem,
input re_mem_mem,
input we_mem_mem,
input we_mem_exe,
input if_stall,
input mem_stall,
output stall_PC,
output stall_IFID,
output stall_IDEXE,
output stall_EXEMEM,
output stall_MEMWB,
output flush_IFID,
output flush_IDEXE,
output flush_EXEMEM,
output flush_MEMWB
);
wire data_race_if = ((rs1_exe == rd_mem)|(rs2_exe == rd_mem)) & (re_mem_mem | we_mem_mem); // MEM 为 Load
wire predict_flush = jump;
assign stall_PC = mem_stall | if_stall;
assign stall_IFID = mem_stall | data_race_if;
assign flush_IFID = ~stall_IFID & (if_stall | predict_flush);
assign stall_IDEXE = predict_flush & if_stall | mem_stall | data_race_if;
assign flush_IDEXE = ~stall_IDEXE & predict_flush;
assign stall_EXEMEM = mem_stall;
assign flush_EXEMEM = ~stall_EXEMEM & (data_race_if | predict_flush & if_stall);
assign stall_MEMWB = 1'b0;
assign flush_MEMWB = mem_stall;
endmodule
1.3 进行仿真测试,以检验 CPU 基本功能¶
![[lab2simpassed.png]]
1.4 进行上板测试,以检验 CPU 设计规范¶
跳到 9A8,通过验收。
2 思考题¶
2.1 在引入 Forwarding 机制后,是否意味着 stall 机制就不再需要了?为什么?¶
Answer: 引入 Forwarding 机制并不意味着不再需要stall机制。Forwarding 机制主要解决数据相关性的问题,通过将计算结果直接转发给需要使用的指令,避免了通过寄存器等中间存储器件的访问延迟。而在遇到结构冲突或控制冲突的时候,需要 stall 机制通过暂停流水线的执行来等待资源可用。并且当所有数据冲突的情况都通过 Forwarding 写回的时候,电路变得复杂,导致需要的时钟周期变长,使用 stall 更能提升性能。
2.2 你认为 Forwarding 机制在实际的电路设计中是否存在一定的弊端?和单纯的 stall 相比它有什么缺点?¶
Answer: Forwarding 机制在实际的电路设计中可能存在一定的弊端。首先,引入Forwarding 机制会增加电路的复杂性和功耗。为了实现数据转发,需要添加额外的电路和逻辑来检测和选择需要转发的数据。其次,Forwarding 机制可能会引入更多的时序问题和设计难度,特别是在处理复杂的数据相关性时。相比之下,stall 机制相对简单直观,但需要牺牲一部分性能。
2.3 不考虑 AXI4-lite 总线的影响,引入 Forwarding 机制之后 cpi 和 stall 相比提升了那些?¶
- CPI(Cycles Per Instruction)的降低:通过数据前递,由于数据依赖性导致的 stall 可以通过Forwarding机制解决,避免了流水线的停顿,提高了指令的执行效率。
2.4 计算加入 AXI4-lite 总线之后的 cpi,思考 cpi 的值受到什么因素的制约,考虑可能的提升方法?¶
如果访问外部存储器的延迟较高,内存访问延迟使处理器可能需要等待数据返回,导致流水线暂停,增加CPI。 加入总线之后的 CPI 首 if_stall 信号和 mem_stall 信号影响,可以通过优化内存访问方式,减少内存访问延迟;也可以提高总线的线宽,增加每个时钟周期的数据传输量来提升 CPI。
2.5 尝试分析添加 Forwarding 后整体设计的关键路径。¶
![[lab2implementtimereport.png]]
报告显示从 core 中的 mem 阶段从 alu 出来的结果到得到选择后 pc 的时间最长