第七章、万事开头难吗—一切从取指令开始(2)
7.3 蜂鸟E200处理器的取指实现
7.3.1 IFU总体设计思路
功能
- 对取回的地址进行简单译码
- 简单的分支预测
- 生成取指的PC
- 根据PC的地址访问ITCM或BIU
地址判断和ICB总线控制模块控制访问ITCM和BIU。蜂鸟E200面向嵌入式场景,代码量不大,假设所有代码加载在ITCM中执行,可实现单周期取指。若需要从外部存储器读取,则通过BIU访问。取值回来后在同一时钟周期进行译码,若为分支跳转指令,则simple-BPU在同一周期进行预测。最后使用译码结果和分支预测结果生成下一条指令PC。
7.3.2 Mini-Decode
mini-Decode模块不需要译码所有信息,而只需要获取这条指令是否分支跳转、分支跳转类型和细节。
例化一个完整decode,对某些信号忽略从而使综合工具将无关逻辑优化掉
`include "e203_defines.v"
module e203_ifu_minidec(
//////////////////////////////////////////////////////////////
// The IR stage to Decoder
input [`E203_INSTR_SIZE-1:0] instr,
//////////////////////////////////////////////////////////////
// The Decoded Info-Bus
output dec_rs1en,
output dec_rs2en,
output [`E203_RFIDX_WIDTH-1:0] dec_rs1idx,
output [`E203_RFIDX_WIDTH-1:0] dec_rs2idx,
output dec_mulhsu,
output dec_mul ,
output dec_div ,
output dec_rem ,
output dec_divu ,
output dec_remu ,
output dec_rv32,//指示当前指令为16位或32位
output dec_bjp,//指示当前指令是否为分支跳转指令
output dec_jal,//是否为jal指令
output dec_jalr,//是否为jalr指令
output dec_bxx,//是否为条件分支指令
output [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx,
output [`E203_XLEN-1:0] dec_bjp_imm
);
e203_exu_decode u_e203_exu_decode(
.i_instr(instr),
.i_pc(`E203_PC_SIZE'b0),//不用的输入信号接0
.i_prdt_taken(1'b0),
.i_muldiv_b2b(1'b0),
.i_misalgn (1'b0),
.i_buserr (1'b0),
.dbg_mode (1'b0),
.dec_misalgn(),//不用的输出信号悬空
.dec_buserr(),
.dec_ilegl(),
.dec_rs1x0(),
.dec_rs2x0(),
.dec_rs1en(dec_rs1en),
.dec_rs2en(dec_rs2en),
.dec_rdwen(),
.dec_rs1idx(dec_rs1idx),
.dec_rs2idx(dec_rs2idx),
.dec_rdidx(),
.dec_info(),
.dec_imm(),
.dec_pc(),
.dec_mulhsu(dec_mulhsu),
.dec_mul (dec_mul ),
.dec_div (dec_div ),
.dec_rem (dec_rem ),
.dec_divu (dec_divu ),
.dec_remu (dec_remu ),
.dec_rv32(dec_rv32),
.dec_bjp (dec_bjp ),
.dec_jal (dec_jal ),
.dec_jalr(dec_jalr),
.dec_bxx (dec_bxx ),
.dec_jalr_rs1idx(dec_jalr_rs1idx),
.dec_bjp_imm (dec_bjp_imm )
);
endmodule
7.3.3 Simple-BPU分支预测
`include "e203_defines.v"
module e203_ifu_litebpu(
// Current PC
input [`E203_PC_SIZE-1:0] pc,
// The mini-decoded info
input dec_jal,
input dec_jalr,
input dec_bxx,
input [`E203_XLEN-1:0] dec_bjp_imm,
input [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx,
// The IR index and OITF status to be used for checking dependency
input oitf_empty,
input ir_empty,
input ir_rs1en,
input jalr_rs1idx_cam_irrdidx,
// The add op to next-pc adder
output bpu_wait,
output prdt_taken,
output [`E203_PC_SIZE-1:0] prdt_pc_add_op1,
output [`E203_PC_SIZE-1:0] prdt_pc_add_op2,
input dec_i_valid,
// The RS1 to read regfile
output bpu2rf_rs1_ena,
input ir_valid_clr,
input [`E203_XLEN-1:0] rf2bpu_x1,
input [`E203_XLEN-1:0] rf2bpu_rs1,
input clk,
input rst_n
);
// The JAL and JALR is always jump, bxxx backward is predicted as taken
assign prdt_taken = (dec_jal | dec_jalr | (dec_bxx & dec_bjp_imm[`E203_XLEN-1])); //dec_bjp_imm为条件跳转指令的offset,若为负数,则为向后跳转
// The JALR with rs1 == x1 have dependency or xN have dependency
wire dec_jalr_rs1x0 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd0);//判定rs1索引号
wire dec_jalr_rs1x1 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd1);
wire dec_jalr_rs1xn = (~dec_jalr_rs1x0) & (~dec_jalr_rs1x1);
wire jalr_rs1x1_dep = dec_i_valid & dec_jalr & dec_jalr_rs1x1 & ((~oitf_empty) | (jalr_rs1idx_cam_irrdidx));//判定是否存在RAW数据相关性
wire jalr_rs1xn_dep = dec_i_valid & dec_jalr & dec_jalr_rs1xn & ((~oitf_empty) | (~ir_empty));
// If only depend to IR stage (OITF is empty), then if IR is under clearing, or
// it does not use RS1 index, then we can also treat it as non-dependency
wire jalr_rs1xn_dep_ir_clr = (jalr_rs1xn_dep & oitf_empty & (~ir_empty)) & (ir_valid_clr | (~ir_rs1en));
wire rs1xn_rdrf_r;
wire rs1xn_rdrf_set = (~rs1xn_rdrf_r) & dec_i_valid & dec_jalr & dec_jalr_rs1xn & ((~jalr_rs1xn_dep) | jalr_rs1xn_dep_ir_clr);
wire rs1xn_rdrf_clr = rs1xn_rdrf_r;
wire rs1xn_rdrf_ena = rs1xn_rdrf_set | rs1xn_rdrf_clr;
wire rs1xn_rdrf_nxt = rs1xn_rdrf_set | (~rs1xn_rdrf_clr);
sirv_gnrl_dfflr #(1) rs1xn_rdrf_dfflrs(rs1xn_rdrf_ena, rs1xn_rdrf_nxt, rs1xn_rdrf_r, clk, rst_n);
assign bpu2rf_rs1_ena = rs1xn_rdrf_set;
assign bpu_wait = jalr_rs1x1_dep | jalr_rs1xn_dep | rs1xn_rdrf_set;//若存在数据相关性,则等待
assign prdt_pc_add_op1 = (dec_bxx | dec_jal) ? pc[`E203_PC_SIZE-1:0]
: (dec_jalr & dec_jalr_rs1x0) ? `E203_PC_SIZE'b0
: (dec_jalr & dec_jalr_rs1x1) ? rf2bpu_x1[`E203_PC_SIZE-1:0]
: rf2bpu_rs1[`E203_PC_SIZE-1:0];
assign prdt_pc_add_op2 = dec_bjp_imm[`E203_PC_SIZE-1:0]; //生成两个操作数送给加法器进行计算
endmodule
可以看到代码中的组合逻辑全部使用assign完成,涉及到的触发器使用模板进行例化,有利于综合工具实现作者想要的电路。
7.3.4 PC生成
PC自增:若当前指令为32位,则下一条指令PC+4,否则+2。数据输入共享的加法器进行计算。
7.3.5 访问ITCM和BIU
剩余缓存技术
- 64位SRAM每次读出64bit,但是IFU每次取32bit。下次再取可以不开SRAM使能,直接读。
- 如果地址非对齐,可以将SRAM当前最高16位存入剩余缓存,并发起新的读操作,然后新读出的低16位和剩余缓存中的数据拼接
- 如果非顺序取指时地址不对齐,采用两个周期来读,会造成一个周期性能浪费,在所难免。
代码有点多,理不清,先跳了吧
7.3.6 ITCM
64位ITCM能获取更低功耗开销
- 容量不大的SRAM,64位比32位更紧凑。因此同样容量,64位数据宽度面积更小。
- 一次取出64位能消耗更少的动态功耗
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】