从零开始设计riscv cpu记录之三
regs.v寄存器32个寄存器两个读端口读采用组合逻辑一个写端口写采用时序逻辑就这样简单。一个值得注意的是reg_we_i写使能信号容易被忽略为什么不能没有使能信号因为寄存器堆就无法判断当前周期是不是“真的要写寄存器”从而可能发生误写和错误旁路。代码timescale1ns/1ps includedefines.vmoduleregs(input wire clk,input wire rst,input wire[4:0]reg1_raddr_i,input wire[4:0]reg2_raddr_i,output wire[DW-1:0]reg1_rdata_o,output wire[DW-1:0]reg2_rdata_o,input wire reg_we_i,input wire[4:0]reg_waddr_i,input wire[DW-1:0]reg_wdata_i);reg[DW-1:0]REGS[0:31];integer i;always(posedge clk)beginif(rstRstEnable)beginfor(i1;i32;ii1)begin REGS[i]ZeroWord;end endelseif(reg_waddr_i!ZeroRegreg_we_i)begin REGS[reg_waddr_i]reg_wdata_i;end end assign reg1_rdata_o(rst!RstEnable)?(reg1_raddr_iZeroReg)?ZeroWord:(reg_we_ireg1_raddr_ireg_waddr_i)?reg_wdata_i:REGS[reg1_raddr_i]:ZeroWord;assign reg2_rdata_o(rst!RstEnable)?(reg2_raddr_iZeroReg)?ZeroWord:(reg_we_ireg2_raddr_ireg_waddr_i)?reg_wdata_i:REGS[reg2_raddr_i]:ZeroWord;endmodule代码很短从上到下可以分成三部分一是实例化REGS数组二是写逻辑对应中间的always块三是读逻辑对应assign语句。另一个值得关注的地方就是读逻辑中加了判断读地址是0输出0为什么读逻辑中要加判断读地址是0输出0也就是代码为什么不是assign reg1_rdata_o(rst!RstEnable)?(reg_we_ireg1_raddr_ireg_waddr_i)?reg_wdata_i:REGS[reg1_raddr_i]:ZeroWord;两种写法的区别在于采用的写法是强制0号寄存器输出0;而上面给出的代码是要去读REGS[0]里边的内容区别就在这里万一0号寄存器中不是0就出错了。所以采用强制输出是正确的。