蜂鳥E203的傳遞與寫回機制
在經典的五級流水線模型中并沒有傳遞的概念,在這裡傳遞(Commit)指的是該指令不再是預測執行(Speculative)狀态,而是被判定為可以真正地在處理器中被執行
傳遞的反義詞就是“取消”(Cancel),表示該指令最後被判定為需要取消
如果處理器流水線需要将沒有傳遞的後續指令全部取消時,就會導緻“流水線沖刷”的産生,下面依次來介紹
傳遞與流水線沖刷
通常情況下傳遞都是順序判定,理論上隻有前一條指令完成傳遞之後才會輪到後一條指令傳遞
以下因素會映像指令傳遞:
-
中斷、異常、分支預測指令
它們往往會導緻流水線沖刷,将後續所有預取的指令都取消掉
-
條件碼
在一些指令集架構(比如典型的ARM架構)中,對于每條指令,隻有其條件碼滿足條件為真才能真正傳遞,否則就會被取消,這裡的取消隻是取消它自己,并不會産生流水線沖刷
根據處理器性能不同,可以選擇低性能的一個周期傳遞一條指令或者高性能的一個周期傳遞多條指令;并且傳遞的位置可以有所不同,常見的方案如下:
-
執行階段傳遞
在執行階段将分支預測指令的結果解析完成并進行傳遞
-
寫回階段傳遞
由于有些指令需要多個周期的執行以後才能寫回,并且可能産生錯誤異常,是以有些微架構将傳遞放在寫回階段
-
重排序傳遞隊列(Re-Order Commit Queue)
對于高性能的超标量處理器而言,往往是亂序執行亂序寫回,在寫回階段往往使用ROB或純實體寄存器的方式,同時會配備一個較深的重排序傳遞隊列來緩存亂序執行的指令資訊,并對其進行按序傳遞
對于RISC-V而言,指令沒有條件碼、所有運算指令都不會産生異常這兩個固有特點大幅簡化了傳遞的硬體實作
在RISC-V的處理器核中隻需要處理
- 分支預測指令錯誤預測造成的後續指令流取消
- 中斷和異常造成的後續指令流取消
蜂鳥E203的傳遞實作
蜂鳥E203處理器将傳遞安排在執行階段,并且可以保證:隻要前序的指令沒有發生分支預測錯誤、中斷或異常就可以判定該指令能夠被成功傳遞。對于分支預測錯誤的指令自身和遭遇了中斷或者異常的指令自身而言,仍然是屬于成功傳遞的指令,因為他們自身已經被真正執行并對處理器狀态真正地産生了影響
對于分支預測指令,蜂鳥E203使用IFU中進行預測的分支指令為帶條件跳轉指令類型,有以下幾個條件:
beq:兩個整數操作數相等則跳轉
bne:兩個整數不相等則跳轉
blt:有符号數小于則跳轉
bltu:無符号數大于則跳轉
bge:有符号數大于則跳轉
bgeu:無符号數大于則跳轉
這些跳轉條件的決定由ALU完成,是以具體的跳轉操作是在ALU之後完成的
相關代碼位于rtl/e203/core/e203_exu_alu_bjp.v
ALU在計算出結果後會發送給傳遞子產品,傳遞子產品根據預測結果和真實結果進行判斷,如果預測和真實的結果相符則預測成功,不會進行流水線沖刷,否則就進行流水線沖刷
相關子產品位于rtl/e203/core/e203_exu_commit.v和e203_exu_branchslv.v(這是e203_exu_commit的子子產品,用于對分支預測指令 的結果進行判斷)
對于多周期的長指令,傳遞同樣在執行階段完成,但是寫回則需要在後續的周期内進行,并且對于特殊的長指令在寫回時産生的錯誤異常會被當作異步異常進行處理,是以說并不會引起不必要的流水線沖刷
寫回與寫回仲裁
蜂鳥E203采用了因地制宜的混合政策,兼顧了面積最小化的原則和較好的性能,核心思想就是“分類讨論”:将指令劃分為單周期指令和長指令,将長指令的傳遞和寫回分開,使得即使執行了多周期長指令,仍然不會阻塞流水線。
寫回部分主要由最終寫回仲裁、長指令寫回仲裁和OITF組成
最終寫回仲裁
位于rtl/e203/core/e203_exu_wbck.v
E203具有兩級寫回仲裁子產品,第一個就是最終寫回仲裁子產品(Final Write-Back Arbitration,FWBA)
該子產品主要用于仲裁所有來自ALU的單周期指令的寫回和所有來自長指令寫回仲裁子產品的長指令的寫回
也就是說FWBA用于所有指令最終寫回的判斷
仲裁采用優先級仲裁的方式,由于長指令的寫回比正在協會的ALU指令在程式流中處于更早的位置,長指令就具有更高的寫回優先級,這就導緻了以下情形發生:如果在長指令完成執行準備寫回時,有單周期指令正在寫回,它會被“打斷”(指的是在上一條寫回完成後不會繼續寫回下一條單周期指令,而是會轉而寫回長指令),長指令得到寫回;如果在沒有長指令寫回的空閑周期,來自ALU的單周期指令則可以随便寫回,這也就意味着在程式流中處于更遲位置的單周期指令可以比更早位置的長指令先寫回寄存器組。這就是的蜂鳥E203處理器具有亂序寫回的能力
代碼片段如下:
module e203_exu_wbck(
// ALU寫回接口
input alu_wbck_i_valid, // valid信号
output alu_wbck_i_ready, // ready信号
input [`E203_XLEN-1:0] alu_wbck_i_wdat, // 寫回的資料值
input [`E203_RFIDX_WIDTH-1:0] alu_wbck_i_rdidx, // 寫回的寄存器索引值
//如果ALU出錯,就不會生成wback_valid信号到寫回子產品
//是以這裡不需要alu_wbck_i_err報錯信号
// 長指令寫回接口
input longp_wbck_i_valid, // valid信号
output longp_wbck_i_ready, // ready信号
input [`E203_FLEN-1:0] longp_wbck_i_wdat, // 寫回的資料值
input [5-1:0] longp_wbck_i_flags, // 寫回标志
input [`E203_RFIDX_WIDTH-1:0] longp_wbck_i_rdidx, // 寫回的寄存器索引
input longp_wbck_i_rdfpu, // 寫回到FPU的資料
// 仲裁後寫回寄存器組的接口
output rf_wbck_o_ena, // 寫使能
output [`E203_XLEN-1:0] rf_wbck_o_wdat, // 寫回的資料值
output [`E203_RFIDX_WIDTH-1:0] rf_wbck_o_rdidx, // 寫回的寄存器索引
input clk,
input rst_n
);
// 使用優先級仲裁
// 如果兩種指令同時寫回,則長指令擁有更高的優先級
// 隻有當沒有長指令時,ALU單周期指令才能寫回
wire wbck_ready4alu = (~longp_wbck_i_valid);
wire wbck_sel_alu = alu_wbck_i_valid & wbck_ready4alu;
// 因為長指令優先級更高,是以可以優先寫回
wire wbck_ready4longp = 1'b1;
wire wbck_sel_longp = longp_wbck_i_valid & wbck_ready4longp;
// 最終仲裁寫回接口
wire rf_wbck_o_ready = 1'b1; // 寄存器組因為隻有單總線是以總是可以寫回的
wire wbck_i_ready;
wire wbck_i_valid;
wire [`E203_FLEN-1:0] wbck_i_wdat;
wire [5-1:0] wbck_i_flags;
wire [`E203_RFIDX_WIDTH-1:0] wbck_i_rdidx;
wire wbck_i_rdfpu;
assign alu_wbck_i_ready = wbck_ready4alu & wbck_i_ready;
assign longp_wbck_i_ready = wbck_ready4longp & wbck_i_ready;
assign wbck_i_valid = wbck_sel_alu ? alu_wbck_i_valid : longp_wbck_i_valid;
`ifdef E203_FLEN_IS_32//{
assign wbck_i_wdat = wbck_sel_alu ? alu_wbck_i_wdat : longp_wbck_i_wdat;
`else//}{
assign wbck_i_wdat = wbck_sel_alu ? {{`E203_FLEN-`E203_XLEN{1'b0}},alu_wbck_i_wdat} : longp_wbck_i_wdat;
`endif//}
assign wbck_i_flags = wbck_sel_alu ? 5'b0 : longp_wbck_i_flags;
assign wbck_i_rdidx = wbck_sel_alu ? alu_wbck_i_rdidx : longp_wbck_i_rdidx;
assign wbck_i_rdfpu = wbck_sel_alu ? 1'b0 : longp_wbck_i_rdfpu;
//長指令寫回異常産生部分,見下文
//如果長指令出錯或者因為某些原因沒能寫回,他就會被在執行階段被取消掉
//是以這個子產品總是需要在這裡将資料寫回寄存器組
assign wbck_i_ready = rf_wbck_o_ready;
wire rf_wbck_o_valid = wbck_i_valid;
wire wbck_o_ena = rf_wbck_o_valid & rf_wbck_o_ready;
assign rf_wbck_o_ena = wbck_o_ena & (~wbck_i_rdfpu);
assign rf_wbck_o_wdat = wbck_i_wdat[`E203_XLEN-1:0];
assign rf_wbck_o_rdidx = wbck_i_rdidx;
endmodule
長指令寫回仲裁與OITF
E203具有兩級寫回仲裁子產品,第二個就是長指令寫回仲裁子產品(Long-Pipes Instructions Write-Back Arbitration,LPIWBA)
OITF和長指令寫回仲裁子產品協同合作完成所有長指令的寫回操作,長指令寫回仲裁主要用于仲裁不同長指令之間的寫回,因為這些指令來自不同執行單元、執行的周期數不同、執行的順序不同、寫回的地方不一樣,就需要記錄這些指令的先後關系,這就用到了OITF
OITF在之前的執行部分已經介紹過,它本質上是一個記錄還未寫回但是已經在執行的長指令的FIFO。每個被派遣的長指令都會在OITF中配置設定一個表項(Entry),這個表項的FIFO指針就作為這個長指令的ITAG,長指令不管被派遣到任何運算單元都會攜帶這個ITAG,同時寫回時也要帶着相同的ITAG
OITF的深度就決定了能夠派遣的滞外(Outstanding,也就是OITF中的“O”)長指令的個數。為了硬體實作的簡潔,蜂鳥E203采用嚴格按照OITF的順序寫回到方法——OITF的讀指針會指向最先進入此FIFO的表項,通過使用此讀指針作為長指令寫回仲裁的選擇參考,就可以保證不同長指令的寫回順序和派遣順序嚴格一緻。
每次長指令寫回仲裁子產品成功寫回一個長指令後,對應地OITF表項就被從FIFO中退出了
由于有些長指令可能發生執行錯誤,是以需要産生異常——長指令寫回仲裁子產品會和傳遞子產品産生接口觸發異常,如果長指令産生異常,則不會真正寫回,而是在接口部分就被丢棄。
有關FWBA的代碼部分見上面的源碼
長指令寫回仲裁和OITF部分的源碼位于rtl/e203/core/e203_exu_disp.v、rtl/e203/core/e203_exu_oitf.v、rtl/e203/core/e203_exu_longwbck.v三個檔案
這裡直接複制粘貼了全部源碼,推薦系統地看一下三個檔案的代碼來更好地了解實作思路
/* e203_exu_disp */
module e203_exu_disp(
input wfi_halt_exu_req,
output wfi_halt_exu_ack,
input oitf_empty,
input amo_wait,
//
// The operands and decode info from dispatch
input disp_i_valid, // Handshake valid
output disp_i_ready, // Handshake ready
// The operand 1/2 read-enable signals and indexes
input disp_i_rs1x0,
input disp_i_rs2x0,
input disp_i_rs1en,
input disp_i_rs2en,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rs1idx,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rs2idx,
input [`E203_XLEN-1:0] disp_i_rs1,
input [`E203_XLEN-1:0] disp_i_rs2,
input disp_i_rdwen,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rdidx,
input [`E203_DECINFO_WIDTH-1:0] disp_i_info,
input [`E203_XLEN-1:0] disp_i_imm,
input [`E203_PC_SIZE-1:0] disp_i_pc,
input disp_i_misalgn,
input disp_i_buserr ,
input disp_i_ilegl ,
//
// Dispatch to ALU
output disp_o_alu_valid,
input disp_o_alu_ready,
input disp_o_alu_longpipe,
output [`E203_XLEN-1:0] disp_o_alu_rs1,
output [`E203_XLEN-1:0] disp_o_alu_rs2,
output disp_o_alu_rdwen,
output [`E203_RFIDX_WIDTH-1:0] disp_o_alu_rdidx,
output [`E203_DECINFO_WIDTH-1:0] disp_o_alu_info,
output [`E203_XLEN-1:0] disp_o_alu_imm,
output [`E203_PC_SIZE-1:0] disp_o_alu_pc,
output [`E203_ITAG_WIDTH-1:0] disp_o_alu_itag,
output disp_o_alu_misalgn,
output disp_o_alu_buserr ,
output disp_o_alu_ilegl ,
//
// Dispatch to OITF
input oitfrd_match_disprs1,
input oitfrd_match_disprs2,
input oitfrd_match_disprs3,
input oitfrd_match_disprd,
input [`E203_ITAG_WIDTH-1:0] disp_oitf_ptr ,
output disp_oitf_ena,
input disp_oitf_ready,
output disp_oitf_rs1fpu,
output disp_oitf_rs2fpu,
output disp_oitf_rs3fpu,
output disp_oitf_rdfpu ,
output disp_oitf_rs1en ,
output disp_oitf_rs2en ,
output disp_oitf_rs3en ,
output disp_oitf_rdwen ,
output [`E203_RFIDX_WIDTH-1:0] disp_oitf_rs1idx,
output [`E203_RFIDX_WIDTH-1:0] disp_oitf_rs2idx,
output [`E203_RFIDX_WIDTH-1:0] disp_oitf_rs3idx,
output [`E203_RFIDX_WIDTH-1:0] disp_oitf_rdidx ,
output [`E203_PC_SIZE-1:0] disp_oitf_pc ,
input clk,
input rst_n
);
wire [`E203_DECINFO_GRP_WIDTH-1:0] disp_i_info_grp = disp_i_info [`E203_DECINFO_GRP];
// Based on current 2 pipe stage implementation, the 2nd stage need to have all instruction
// to be commited via ALU interface, so every instruction need to be dispatched to ALU,
// regardless it is long pipe or not, and inside ALU it will issue instructions to different
// other longpipes
//wire disp_alu = (disp_i_info_grp == `E203_DECINFO_GRP_ALU)
// | (disp_i_info_grp == `E203_DECINFO_GRP_BJP)
// | (disp_i_info_grp == `E203_DECINFO_GRP_CSR)
// `ifdef E203_SUPPORT_SHARE_MULDIV //{
// | (disp_i_info_grp == `E203_DECINFO_GRP_MULDIV)
// `endif//E203_SUPPORT_SHARE_MULDIV}
// | (disp_i_info_grp == `E203_DECINFO_GRP_AGU);
wire disp_csr = (disp_i_info_grp == `E203_DECINFO_GRP_CSR);
wire disp_alu_longp_prdt = (disp_i_info_grp == `E203_DECINFO_GRP_AGU)
;
wire disp_alu_longp_real = disp_o_alu_longpipe;
// Both fence and fencei need to make sure all outstanding instruction have been completed
wire disp_fence_fencei = (disp_i_info_grp == `E203_DECINFO_GRP_BJP) &
( disp_i_info [`E203_DECINFO_BJP_FENCE] | disp_i_info [`E203_DECINFO_BJP_FENCEI]);
// Since any instruction will need to be dispatched to ALU, we dont need the gate here
// wire disp_i_ready_pos = disp_alu & disp_o_alu_ready;
// assign disp_o_alu_valid = disp_alu & disp_i_valid_pos;
wire disp_i_valid_pos;
wire disp_i_ready_pos = disp_o_alu_ready;
assign disp_o_alu_valid = disp_i_valid_pos;
//
// The Dispatch Scheme Introduction for two-pipeline stage
// #1: The instruction after dispatched must have already have operand fetched, so
// there is no any WAR dependency happened.
// #2: The ALU-instruction are dispatched and executed in-order inside ALU, so
// there is no any WAW dependency happened among ALU instructions.
// Note: LSU since its AGU is handled inside ALU, so it is treated as a ALU instruction
// #3: The non-ALU-instruction are all tracked by OITF, and must be write-back in-order, so
// it is like ALU in-ordered. So there is no any WAW dependency happened among
// non-ALU instructions.
// Then what dependency will we have?
// * RAW: This is the real dependency
// * WAW: The WAW between ALU an non-ALU instructions
//
// So #1, The dispatching ALU instruction can not proceed and must be stalled when
// ** RAW: The ALU reading operands have data dependency with OITF entries
// *** Note: since it is 2 pipeline stage, any last ALU instruction have already
// write-back into the regfile. So there is no chance for ALU instr to depend
// on last ALU instructions as RAW.
// Note: if it is 3 pipeline stages, then we also need to consider the ALU-to-ALU
// RAW dependency.
// ** WAW: The ALU writing result have no any data dependency with OITF entries
// Note: Since the ALU instruction handled by ALU may surpass non-ALU OITF instructions
// so we must check this.
// And #2, The dispatching non-ALU instruction can not proceed and must be stalled when
// ** RAW: The non-ALU reading operands have data dependency with OITF entries
// *** Note: since it is 2 pipeline stage, any last ALU instruction have already
// write-back into the regfile. So there is no chance for non-ALU instr to depend
// on last ALU instructions as RAW.
// Note: if it is 3 pipeline stages, then we also need to consider the non-ALU-to-ALU
// RAW dependency.
wire raw_dep = ((oitfrd_match_disprs1) |
(oitfrd_match_disprs2) |
(oitfrd_match_disprs3));
// Only check the longp instructions (non-ALU) for WAW, here if we
// use the precise version (disp_alu_longp_real), it will hurt timing very much, but
// if we use imprecise version of disp_alu_longp_prdt, it is kind of tricky and in
// some corner case. For example, the AGU (treated as longp) will actually not dispatch
// to longp but just directly commited, then it become a normal ALU instruction, and should
// check the WAW dependency, but this only happened when it is AMO or unaligned-uop, so
// ideally we dont need to worry about it, because
// * We dont support AMO in 2 stage CPU here
// * We dont support Unalign load-store in 2 stage CPU here, which
// will be triggered as exception, so will not really write-back
// into regfile
// * But it depends on some assumption, so it is still risky if in the future something changed.
// Nevertheless: using this condition only waiver the longpipe WAW case, that is, two
// longp instruction write-back same reg back2back. Is it possible or is it common?
// after we checking the benmark result we found if we remove this complexity here
// it just does not change any benchmark number, so just remove that condition out. Means
// all of the instructions will check waw_dep
//wire alu_waw_dep = (~disp_alu_longp_prdt) & (oitfrd_match_disprd & disp_i_rdwen);
wire waw_dep = (oitfrd_match_disprd);
wire dep = raw_dep | waw_dep;
// The WFI halt exu ack will be asserted when the OITF is empty
// and also there is no AMO oustanding uops
assign wfi_halt_exu_ack = oitf_empty & (~amo_wait);
wire disp_condition =
// To be more conservtive, any accessing CSR instruction need to wait the oitf to be empty.
// Theoretically speaking, it should also flush pipeline after the CSR have been updated
// to make sure the subsequent instruction get correct CSR values, but in our 2-pipeline stage
// implementation, CSR is updated after EXU stage, and subsequent are all executed at EXU stage,
// no chance to got wrong CSR values, so we dont need to worry about this.
(disp_csr ? oitf_empty : 1'b1)
// To handle the Fence: just stall dispatch until the OITF is empty
& (disp_fence_fencei ? oitf_empty : 1'b1)
// If it was a WFI instruction commited halt req, then it will stall the disaptch
& (~wfi_halt_exu_req)
// No dependency
& (~dep)
// If dispatch to ALU as long pipeline, then must check
// the OITF is ready
& ((disp_alu & disp_o_alu_longpipe) ? disp_oitf_ready : 1'b1);
// To cut the critical timing path from longpipe signal
// we always assume the LSU will need oitf ready
& (disp_alu_longp_prdt ? disp_oitf_ready : 1'b1);
assign disp_i_valid_pos = disp_condition & disp_i_valid;
assign disp_i_ready = disp_condition & disp_i_ready_pos;
wire [`E203_XLEN-1:0] disp_i_rs1_msked = disp_i_rs1 & {`E203_XLEN{~disp_i_rs1x0}};
wire [`E203_XLEN-1:0] disp_i_rs2_msked = disp_i_rs2 & {`E203_XLEN{~disp_i_rs2x0}};
// Since we always dispatch any instructions into ALU, so we dont need to gate ops here
//assign disp_o_alu_rs1 = {`E203_XLEN{disp_alu}} & disp_i_rs1_msked;
//assign disp_o_alu_rs2 = {`E203_XLEN{disp_alu}} & disp_i_rs2_msked;
//assign disp_o_alu_rdwen = disp_alu & disp_i_rdwen;
//assign disp_o_alu_rdidx = {`E203_RFIDX_WIDTH{disp_alu}} & disp_i_rdidx;
//assign disp_o_alu_info = {`E203_DECINFO_WIDTH{disp_alu}} & disp_i_info;
assign disp_o_alu_rs1 = disp_i_rs1_msked;
assign disp_o_alu_rs2 = disp_i_rs2_msked;
assign disp_o_alu_rdwen = disp_i_rdwen;
assign disp_o_alu_rdidx = disp_i_rdidx;
assign disp_o_alu_info = disp_i_info;
// Why we use precise version of disp_longp here, because
// only when it is really dispatched as long pipe then allocate the OITF
assign disp_oitf_ena = disp_o_alu_valid & disp_o_alu_ready & disp_alu_longp_real;
assign disp_o_alu_imm = disp_i_imm;
assign disp_o_alu_pc = disp_i_pc;
assign disp_o_alu_itag = disp_oitf_ptr;
assign disp_o_alu_misalgn= disp_i_misalgn;
assign disp_o_alu_buserr = disp_i_buserr ;
assign disp_o_alu_ilegl = disp_i_ilegl ;
`ifndef E203_HAS_FPU//{
wire disp_i_fpu = 1'b0;
wire disp_i_fpu_rs1en = 1'b0;
wire disp_i_fpu_rs2en = 1'b0;
wire disp_i_fpu_rs3en = 1'b0;
wire disp_i_fpu_rdwen = 1'b0;
wire [`E203_RFIDX_WIDTH-1:0] disp_i_fpu_rs1idx = `E203_RFIDX_WIDTH'b0;
wire [`E203_RFIDX_WIDTH-1:0] disp_i_fpu_rs2idx = `E203_RFIDX_WIDTH'b0;
wire [`E203_RFIDX_WIDTH-1:0] disp_i_fpu_rs3idx = `E203_RFIDX_WIDTH'b0;
wire [`E203_RFIDX_WIDTH-1:0] disp_i_fpu_rdidx = `E203_RFIDX_WIDTH'b0;
wire disp_i_fpu_rs1fpu = 1'b0;
wire disp_i_fpu_rs2fpu = 1'b0;
wire disp_i_fpu_rs3fpu = 1'b0;
wire disp_i_fpu_rdfpu = 1'b0;
`endif//}
assign disp_oitf_rs1fpu = disp_i_fpu ? (disp_i_fpu_rs1en & disp_i_fpu_rs1fpu) : 1'b0;
assign disp_oitf_rs2fpu = disp_i_fpu ? (disp_i_fpu_rs2en & disp_i_fpu_rs2fpu) : 1'b0;
assign disp_oitf_rs3fpu = disp_i_fpu ? (disp_i_fpu_rs3en & disp_i_fpu_rs3fpu) : 1'b0;
assign disp_oitf_rdfpu = disp_i_fpu ? (disp_i_fpu_rdwen & disp_i_fpu_rdfpu ) : 1'b0;
assign disp_oitf_rs1en = disp_i_fpu ? disp_i_fpu_rs1en : disp_i_rs1en;
assign disp_oitf_rs2en = disp_i_fpu ? disp_i_fpu_rs2en : disp_i_rs2en;
assign disp_oitf_rs3en = disp_i_fpu ? disp_i_fpu_rs3en : 1'b0;
assign disp_oitf_rdwen = disp_i_fpu ? disp_i_fpu_rdwen : disp_i_rdwen;
assign disp_oitf_rs1idx = disp_i_fpu ? disp_i_fpu_rs1idx : disp_i_rs1idx;
assign disp_oitf_rs2idx = disp_i_fpu ? disp_i_fpu_rs2idx : disp_i_rs2idx;
assign disp_oitf_rs3idx = disp_i_fpu ? disp_i_fpu_rs3idx : `E203_RFIDX_WIDTH'b0;
assign disp_oitf_rdidx = disp_i_fpu ? disp_i_fpu_rdidx : disp_i_rdidx;
assign disp_oitf_pc = disp_i_pc;
endmodule
/* e203_exu_oitf */
module e203_exu_oitf (
output dis_ready,
input dis_ena,
input ret_ena,
output [`E203_ITAG_WIDTH-1:0] dis_ptr,
output [`E203_ITAG_WIDTH-1:0] ret_ptr,
output [`E203_RFIDX_WIDTH-1:0] ret_rdidx,
output ret_rdwen,
output ret_rdfpu,
output [`E203_PC_SIZE-1:0] ret_pc,
input disp_i_rs1en,
input disp_i_rs2en,
input disp_i_rs3en,
input disp_i_rdwen,
input disp_i_rs1fpu,
input disp_i_rs2fpu,
input disp_i_rs3fpu,
input disp_i_rdfpu,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rs1idx,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rs2idx,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rs3idx,
input [`E203_RFIDX_WIDTH-1:0] disp_i_rdidx,
input [`E203_PC_SIZE -1:0] disp_i_pc,
output oitfrd_match_disprs1,
output oitfrd_match_disprs2,
output oitfrd_match_disprs3,
output oitfrd_match_disprd,
output oitf_empty,
input clk,
input rst_n
);
wire [`E203_OITF_DEPTH-1:0] vld_set;
wire [`E203_OITF_DEPTH-1:0] vld_clr;
wire [`E203_OITF_DEPTH-1:0] vld_ena;
wire [`E203_OITF_DEPTH-1:0] vld_nxt;
wire [`E203_OITF_DEPTH-1:0] vld_r;
wire [`E203_OITF_DEPTH-1:0] rdwen_r;
wire [`E203_OITF_DEPTH-1:0] rdfpu_r;
wire [`E203_RFIDX_WIDTH-1:0] rdidx_r[`E203_OITF_DEPTH-1:0];
// The PC here is to be used at wback stage to track out the
// PC of exception of long-pipe instruction
wire [`E203_PC_SIZE-1:0] pc_r[`E203_OITF_DEPTH-1:0];
wire alc_ptr_ena = dis_ena;
wire ret_ptr_ena = ret_ena;
wire oitf_full ;
wire [`E203_ITAG_WIDTH-1:0] alc_ptr_r;
wire [`E203_ITAG_WIDTH-1:0] ret_ptr_r;
generate
if(`E203_OITF_DEPTH > 1) begin: depth_gt1//{
wire alc_ptr_flg_r;
wire alc_ptr_flg_nxt = ~alc_ptr_flg_r;
wire alc_ptr_flg_ena = (alc_ptr_r == ($unsigned(`E203_OITF_DEPTH-1))) & alc_ptr_ena;
sirv_gnrl_dfflr #(1) alc_ptr_flg_dfflrs(alc_ptr_flg_ena, alc_ptr_flg_nxt, alc_ptr_flg_r, clk, rst_n);
wire [`E203_ITAG_WIDTH-1:0] alc_ptr_nxt;
assign alc_ptr_nxt = alc_ptr_flg_ena ? `E203_ITAG_WIDTH'b0 : (alc_ptr_r + 1'b1);
sirv_gnrl_dfflr #(`E203_ITAG_WIDTH) alc_ptr_dfflrs(alc_ptr_ena, alc_ptr_nxt, alc_ptr_r, clk, rst_n);
wire ret_ptr_flg_r;
wire ret_ptr_flg_nxt = ~ret_ptr_flg_r;
wire ret_ptr_flg_ena = (ret_ptr_r == ($unsigned(`E203_OITF_DEPTH-1))) & ret_ptr_ena;
sirv_gnrl_dfflr #(1) ret_ptr_flg_dfflrs(ret_ptr_flg_ena, ret_ptr_flg_nxt, ret_ptr_flg_r, clk, rst_n);
wire [`E203_ITAG_WIDTH-1:0] ret_ptr_nxt;
assign ret_ptr_nxt = ret_ptr_flg_ena ? `E203_ITAG_WIDTH'b0 : (ret_ptr_r + 1'b1);
sirv_gnrl_dfflr #(`E203_ITAG_WIDTH) ret_ptr_dfflrs(ret_ptr_ena, ret_ptr_nxt, ret_ptr_r, clk, rst_n);
assign oitf_empty = (ret_ptr_r == alc_ptr_r) & (ret_ptr_flg_r == alc_ptr_flg_r);
assign oitf_full = (ret_ptr_r == alc_ptr_r) & (~(ret_ptr_flg_r == alc_ptr_flg_r));
end//}
else begin: depth_eq1//}{
assign alc_ptr_r =1'b0;
assign ret_ptr_r =1'b0;
assign oitf_empty = ~vld_r[0];
assign oitf_full = vld_r[0];
end//}
endgenerate//}
assign ret_ptr = ret_ptr_r;
assign dis_ptr = alc_ptr_r;
// If the OITF is not full, or it is under retiring, then it is ready to accept new dispatch
assign dis_ready = (~oitf_full) | ret_ena;
// To cut down the loop between ALU write-back valid --> oitf_ret_ena --> oitf_ready ---> dispatch_ready --- > alu_i_valid
// we exclude the ret_ena from the ready signal
assign dis_ready = (~oitf_full);
wire [`E203_OITF_DEPTH-1:0] rd_match_rs1idx;
wire [`E203_OITF_DEPTH-1:0] rd_match_rs2idx;
wire [`E203_OITF_DEPTH-1:0] rd_match_rs3idx;
wire [`E203_OITF_DEPTH-1:0] rd_match_rdidx;
genvar i;
generate //{
for (i=0; i<`E203_OITF_DEPTH; i=i+1) begin:oitf_entries//{
assign vld_set[i] = alc_ptr_ena & (alc_ptr_r == i);
assign vld_clr[i] = ret_ptr_ena & (ret_ptr_r == i);
assign vld_ena[i] = vld_set[i] | vld_clr[i];
assign vld_nxt[i] = vld_set[i] | (~vld_clr[i]);
sirv_gnrl_dfflr #(1) vld_dfflrs(vld_ena[i], vld_nxt[i], vld_r[i], clk, rst_n);
//Payload only set, no need to clear
sirv_gnrl_dffl #(`E203_RFIDX_WIDTH) rdidx_dfflrs(vld_set[i], disp_i_rdidx, rdidx_r[i], clk);
sirv_gnrl_dffl #(`E203_PC_SIZE ) pc_dfflrs (vld_set[i], disp_i_pc , pc_r[i] , clk);
sirv_gnrl_dffl #(1) rdwen_dfflrs(vld_set[i], disp_i_rdwen, rdwen_r[i], clk);
sirv_gnrl_dffl #(1) rdfpu_dfflrs(vld_set[i], disp_i_rdfpu, rdfpu_r[i], clk);
assign rd_match_rs1idx[i] = vld_r[i] & rdwen_r[i] & disp_i_rs1en & (rdfpu_r[i] == disp_i_rs1fpu) & (rdidx_r[i] == disp_i_rs1idx);
assign rd_match_rs2idx[i] = vld_r[i] & rdwen_r[i] & disp_i_rs2en & (rdfpu_r[i] == disp_i_rs2fpu) & (rdidx_r[i] == disp_i_rs2idx);
assign rd_match_rs3idx[i] = vld_r[i] & rdwen_r[i] & disp_i_rs3en & (rdfpu_r[i] == disp_i_rs3fpu) & (rdidx_r[i] == disp_i_rs3idx);
assign rd_match_rdidx [i] = vld_r[i] & rdwen_r[i] & disp_i_rdwen & (rdfpu_r[i] == disp_i_rdfpu ) & (rdidx_r[i] == disp_i_rdidx );
end//}
endgenerate//}
assign oitfrd_match_disprs1 = |rd_match_rs1idx;
assign oitfrd_match_disprs2 = |rd_match_rs2idx;
assign oitfrd_match_disprs3 = |rd_match_rs3idx;
assign oitfrd_match_disprd = |rd_match_rdidx ;
assign ret_rdidx = rdidx_r[ret_ptr];
assign ret_pc = pc_r [ret_ptr];
assign ret_rdwen = rdwen_r[ret_ptr];
assign ret_rdfpu = rdfpu_r[ret_ptr];
endmodule
/* e203_exu_longwbck */
module e203_exu_longpwbck(
//
// The LSU Write-Back Interface
input lsu_wbck_i_valid, // Handshake valid
output lsu_wbck_i_ready, // Handshake ready
input [`E203_XLEN-1:0] lsu_wbck_i_wdat,
input [`E203_ITAG_WIDTH -1:0] lsu_wbck_i_itag,
input lsu_wbck_i_err , // The error exception generated
input lsu_cmt_i_buserr ,
input [`E203_ADDR_SIZE -1:0] lsu_cmt_i_badaddr,
input lsu_cmt_i_ld,
input lsu_cmt_i_st,
//
// The Long pipe instruction Wback interface to final wbck module
output longp_wbck_o_valid, // Handshake valid
input longp_wbck_o_ready, // Handshake ready
output [`E203_FLEN-1:0] longp_wbck_o_wdat,
output [5-1:0] longp_wbck_o_flags,
output [`E203_RFIDX_WIDTH -1:0] longp_wbck_o_rdidx,
output longp_wbck_o_rdfpu,
//
// The Long pipe instruction Exception interface to commit stage
output longp_excp_o_valid,
input longp_excp_o_ready,
output longp_excp_o_insterr,
output longp_excp_o_ld,
output longp_excp_o_st,
output longp_excp_o_buserr , // The load/store bus-error exception generated
output [`E203_ADDR_SIZE-1:0] longp_excp_o_badaddr,
output [`E203_PC_SIZE -1:0] longp_excp_o_pc,
//
//The itag of toppest entry of OITF
input oitf_empty,
input [`E203_ITAG_WIDTH -1:0] oitf_ret_ptr,
input [`E203_RFIDX_WIDTH-1:0] oitf_ret_rdidx,
input [`E203_PC_SIZE-1:0] oitf_ret_pc,
input oitf_ret_rdwen,
input oitf_ret_rdfpu,
output oitf_ret_ena,
`ifdef E203_HAS_NICE//{
input nice_longp_wbck_i_valid ,
output nice_longp_wbck_i_ready ,
input [`E203_XLEN-1:0] nice_longp_wbck_i_wdat ,
input [`E203_ITAG_WIDTH-1:0] nice_longp_wbck_i_itag ,
input nice_longp_wbck_i_err,
`endif//}
input clk,
input rst_n
);
// The Long-pipe instruction can write-back only when it's itag
// is same as the itag of toppest entry of OITF
wire wbck_ready4lsu = (lsu_wbck_i_itag == oitf_ret_ptr) & (~oitf_empty);
wire wbck_sel_lsu = lsu_wbck_i_valid & wbck_ready4lsu;
`ifdef E203_HAS_NICE//{
wire wbck_ready4nice = (nice_longp_wbck_i_itag == oitf_ret_ptr) & (~oitf_empty);
wire wbck_sel_nice = nice_longp_wbck_i_valid & wbck_ready4nice;
`endif//}
//assign longp_excp_o_ld = wbck_sel_lsu & lsu_cmt_i_ld;
//assign longp_excp_o_st = wbck_sel_lsu & lsu_cmt_i_st;
//assign longp_excp_o_buserr = wbck_sel_lsu & lsu_cmt_i_buserr;
//assign longp_excp_o_badaddr = wbck_sel_lsu ? lsu_cmt_i_badaddr : `E203_ADDR_SIZE'b0;
assign {
longp_excp_o_insterr
,longp_excp_o_ld
,longp_excp_o_st
,longp_excp_o_buserr
,longp_excp_o_badaddr } =
({`E203_ADDR_SIZE+4{wbck_sel_lsu}} &
{
1'b0,
lsu_cmt_i_ld,
lsu_cmt_i_st,
lsu_cmt_i_buserr,
lsu_cmt_i_badaddr
})
;
//
// The Final arbitrated Write-Back Interface
wire wbck_i_ready;
wire wbck_i_valid;
wire [`E203_FLEN-1:0] wbck_i_wdat;
wire [5-1:0] wbck_i_flags;
wire [`E203_RFIDX_WIDTH-1:0] wbck_i_rdidx;
wire [`E203_PC_SIZE-1:0] wbck_i_pc;
wire wbck_i_rdwen;
wire wbck_i_rdfpu;
wire wbck_i_err ;
assign lsu_wbck_i_ready = wbck_ready4lsu & wbck_i_ready;
assign wbck_i_valid = ({1{wbck_sel_lsu}} & lsu_wbck_i_valid)
`ifdef E203_HAS_NICE//{
| ({1{wbck_sel_nice}} & nice_longp_wbck_i_valid)
`endif//}
;
`ifdef E203_FLEN_IS_32 //{
wire [`E203_FLEN-1:0] lsu_wbck_i_wdat_exd = lsu_wbck_i_wdat;
`else//}{
wire [`E203_FLEN-1:0] lsu_wbck_i_wdat_exd = {{`E203_FLEN-`E203_XLEN{1'b0}},lsu_wbck_i_wdat};
`endif//}
`ifdef E203_HAS_NICE//{
wire [`E203_FLEN-1:0] nice_wbck_i_wdat_exd = {{`E203_FLEN-`E203_XLEN{1'b0}},nice_longp_wbck_i_wdat};
`endif//}
assign wbck_i_wdat = ({`E203_FLEN{wbck_sel_lsu}} & lsu_wbck_i_wdat_exd )
`ifdef E203_HAS_NICE//{
| ({`E203_FLEN{wbck_sel_nice}} & nice_wbck_i_wdat_exd )
`endif//}
;
assign wbck_i_flags = 5'b0
;
`ifdef E203_HAS_NICE//{
wire nice_wbck_i_err = nice_longp_wbck_i_err;
`endif//}
assign wbck_i_err = wbck_sel_lsu & lsu_wbck_i_err
;
assign wbck_i_pc = oitf_ret_pc;
assign wbck_i_rdidx = oitf_ret_rdidx;
assign wbck_i_rdwen = oitf_ret_rdwen;
assign wbck_i_rdfpu = oitf_ret_rdfpu;
// If the instruction have no error and it have the rdwen, then it need to
// write back into regfile, otherwise, it does not need to write regfile
wire need_wbck = wbck_i_rdwen & (~wbck_i_err);
// If the long pipe instruction have error result, then it need to handshake
// with the commit module.
wire need_excp = wbck_i_err
`ifdef E203_HAS_NICE//{
& (~ (wbck_sel_nice & nice_wbck_i_err))
`endif//}
;
assign wbck_i_ready =
(need_wbck ? longp_wbck_o_ready : 1'b1)
& (need_excp ? longp_excp_o_ready : 1'b1);
assign longp_wbck_o_valid = need_wbck & wbck_i_valid & (need_excp ? longp_excp_o_ready : 1'b1);
assign longp_excp_o_valid = need_excp & wbck_i_valid & (need_wbck ? longp_wbck_o_ready : 1'b1);
assign longp_wbck_o_wdat = wbck_i_wdat ;
assign longp_wbck_o_flags = wbck_i_flags ;
assign longp_wbck_o_rdfpu = wbck_i_rdfpu ;
assign longp_wbck_o_rdidx = wbck_i_rdidx;
assign longp_excp_o_pc = wbck_i_pc;
assign oitf_ret_ena = wbck_i_valid & wbck_i_ready;
`ifdef E203_HAS_NICE//{
assign nice_longp_wbck_i_ready = wbck_ready4nice & wbck_i_ready;
`endif//}
endmodule
綜上所述,蜂鳥E203的執行結構是一種混合的政策:
- 單周期指令:順序發射、順序執行、順序寫回
- 長指令:順序發射、亂序執行、順序寫回
- 所有指令混雜:順序發射、亂序執行、亂序寫回
在其中最核心的思想就是取得“更高的性能-面積比”,這套解決思路還是比較巧妙的