第一个bug
debug_wb_pc = 0xxxxxxxxx
可看到信号为X,X是不定值,而这种错误往往是下面两种原因之一导致的:
-
RTL里声明为reg型的变量从未被赋值
-
RTL里存在多驱动的代码
首先,从第一种情况开始排查,由变量名debug_wb_pc可知该变量应该位于写回阶段里,打开WB_stage.v,查找变量debug_wb_pc,
发现:
assign debug_wb_pc = ws_pc;
可知debug_wb_pc的值由ws_pc得到,所以下一步就是查找wc_pc的值是从哪里得到的,发现:
assign {ws_gr_we , //69:69
ws_dest , //68:64
ws_final_result, //63:32
ws_pc //31:0
} = ms_to_ws_bus_r;
ws_pc的值与ms_to_ws_bus_r有关,继续查找ms_to_ws_bus_r的值是从哪里得到的,发现:
always @(posedge clk) begin
if (reset) begin
ws_valid <= 1'b0;
end
else if (ws_allowin) begin
ws_valid <= ms_to_ws_valid;
end
if (ms_to_ws_valid && ws_allowin) begin
ms_to_ws_bus_r = ms_to_ws_bus;
end
end
发现ms_to_ws_bus_r的值与ms_to_ws_bus有关,实际上这里有一个小bug,在always语句里应该使用非阻塞赋值,而这里使用的是阻塞赋值,将ms_to_ws_bus_r = ms_to_ws_bus;修改为ms_to_ws_bus_r <= ms_to_ws_bus;我原本以为这就是第一个bug,结果我运行仿真的时候发现还是报debug_wb_pc = 0xxxxxxxxx这个错,那说明真的的bug我还没找到,所以就继续以ms_to_ws_bus往下找,由名字ms_to_ws_bus可看出它与访存阶段和写回阶段有关,写回阶段没发现什么问题,所以去访存阶段里找,打开MEM_stage.v,发现:
assign ms_to_ws_bus = {ms_gr_we , //69:69
ms_dest , //68:64
ms_final_result, //63:32
ms_pc //31:0
};
ms_to_ws_bus与ms_gr_we,ms_dest,ms_final_result,ms_pc 有关,接着继续找ms_gr_we,ms_dest,ms_final_result,ms_pc这些变量和谁有关,发现:
assign {ms_res_from_mem, //70:70
ms_gr_we , //69:69
ms_dest , //68:64
ms_alu_result , //63:32
ms_pc //31:0
} = es_to_ms_bus_r;
与es_to_ms_bus_r有关,而:
es_to_ms_bus_r <= es_to_ms_bus;
所以接下来要排查的变量便是es_to_ms_bus;到这里就可以发现接下来的步骤就和上面排查ms_to_ws_bus的步骤一样了。打开EXE.stage.v,可发现es_to_ms_bus与ds_to_es_bus_r有关,而ds_to_es_bus_r的值又与ds_to_es_bus相关,所以下一步就是打开ID_stage.v继续排查,步骤还是和上面一样,首先发现ds_to_es_bus与fs_to_ds_bus_r有关,到这里终于发现了问题:
always @(posedge clk) begin
if (fs_to_ds_valid && ds_allowin) begin
fs_to_ds_bus_r <= fs_to_ds_bus;
end
end
发现在这个always中没有对ds_valid进行赋值,ds_valid是译码级的有效位,当他的值为1时表示该流水线上当前时钟周期存在有效的数据,值为0时表示该流水线上当前时钟周期为空。具体可看CPU设计实战P55页。
将代码修改为:
always @(posedge clk) begin
//第一个bug
if(reset) begin
ds_valid = 1'b0;
end
if(ds_allowin) begin
ds_valid <= fs_to_ds_valid;
end
//
if (fs_to_ds_valid && ds_allowin) begin
fs_to_ds_bus_r <= fs_to_ds_bus;
end
end
解决第一个bug。