遇到这种情况,以前是用sram,所以一clock肯定能读出数据,所以后面的流水线都是一拍一拍的走,遇到转移指令就stall和清流水线。
reg [31:0] pc_cur ;
wire [31:0] pc_next ;
wire pc_next_en;
wire [29:0] pc_seq ;
assign bt_pc = pc_cur;
assign pc_next = wb_cancel ? wb_target :
br_cancel ? br_target :
pr_cancel ? pr_target :
bt_cancel ? bt_target : {pc_seq,2'h0};
assign pc_next_en = pc_go || pr_cancel || br_cancel || wb_cancel;
assign pc_seq = pc_cur[4:2] >= 3'h4 ? {pc_cur[31:5] + 27'h0000001,3'h0} : {pc_cur[31:5],pc_cur[4:2] + 3'h4};
assign fe_target = fe_valid ? fe_cur[31:2] : pc_cur[31:2];
assign inst_addr = pc_cur;
always@(posedge clock)
begin
if(reset)
begin
pc_cur<=pc_init;
end
else
if(pc_next_en)
begin
pc_cur<=pc_next;
end
end
比如龙芯里这段代码,pc_cur是reg
其实是一个带enable的dff, dffe_s()
只有当pc_next_en为1时,pc_cur寄存器才会更新。而pc_next_en需要等pc_go或者br_branch等其中任何一种情况发生才更新pc_cur,而不是每拍pc_cur都要走。
在OpenSPARC T1里也有这样的用法,只是不是在pc path里,而是在instruction path。
// Thread Next Instruction Register
wire clk_nir0;
`ifdef FPGA_SYN_CLK_EN
`else
bw_u1_ckenbuf_6x ckennir0(.rclk (rclk),
.clk (clk_nir0),
.en_l (fcl_fdp_thr_s1_l[0]),
.tm_l (~se));
`endif
`ifdef FPGA_SYN_CLK_DFF
dffe_s #(33) t0nir_reg(.din (icd_fdp_topdata_s1[32:0]),
.q (t0nir),
.en (~(fcl_fdp_thr_s1_l[0])), .clk(rclk), .se(se), .si(), .so());
`else
dff_s #(33) t0nir_reg(.din (icd_fdp_topdata_s1[32:0]),
.q (t0nir),
.clk (clk_nir0), .se(se), .si(), .so());
`endif
这里还发现个有意思的事,如果是在fpga里,就用dffe。而正式版本里是用信号控制一个clk_nir0,然后这个clk再控制dff。