注意:学习笔记,参考《从零开始写RISC-V处理器》
一共有32个通用寄存器x0~x31,其中寄存器x0是只读寄存器并且其值固定为0。
通用寄存器的输入输出信号如下表所示:
序号 | 信号名 | 输入/输出 | 位宽(bits) | 说明 |
---|---|---|---|---|
1 | clk | 输入 | 1 | 时钟输入 |
2 | rst | 输入 | 1 | 复位输入 |
3 | we_i | 输入 | 1 | 来自执行模块的写使能 |
4 | waddr_i | 输入 | 5 | 来自执行模块的写地址 |
5 | wdata_i | 输入 | 32 | 来自执行模块的写数据 |
6 | jtag_we_i | 输入 | 1 | 来自jtag模块的写使能 |
7 | jtag_addr_i | 输入 | 5 | 来自jtag模块的写地址 |
8 | jtag_data_i | 输入 | 32 | 来自jtag模块的写数据 |
9 | raddr1_i | 输入 | 5 | 来自译码模块的寄存器1读地址 |
10 | rdata1_o | 输出 | 32 | 寄存器1读数据 |
11 | raddr2_i | 输入 | 5 | 来自译码模块的寄存器2读地址 |
12 | rdata2_o | 输出 | 32 | 寄存器2读数据 |
13 | jtag_data_o | 输出 | 32 | jtag读数据 |
注意,这里的寄存器1不是指x1寄存器,寄存器2也不是指x2寄存器。而是指一条指令里涉及到的两个寄存器(源寄存器1和源寄存器2)。一条指令可能会同时读取两个寄存器的值,所以有两个读端口。又因为jtag模块也会进行寄存器的读操作,所以一共有三个读端口。
读寄存器操作来自译码模块,并且读出来的寄存器数据也会返回给译码模块。写寄存器操作来自执行模块。
// 读寄存器1
always @ (*) begin
if (rst == `RstEnable) begin
rdata1_o = `ZeroWord;
end else if (raddr1_i == `RegNumLog2'h0) begin//如果是读寄存器0(x0),那么直接返回0就可以
rdata1_o = `ZeroWord;
// 如果读地址等于写地址,并且正在写操作,则直接返回写数据
end else if (raddr1_i == waddr_i && we_i == `WriteEnable) begin
rdata1_o = wdata_i;
end else begin
rdata1_o = regs[raddr1_i];
end
// 写寄存器
always @ (posedge clk) begin
if (rst == `RstDisable) begin
// 优先ex模块写操作
if ((we_i == `WriteEnable) && (waddr_i != `RegNumLog2'h0)) begin
//如果执行模块写使能并且要写的寄存器不是x0寄存器
regs[waddr_i] <= wdata_i;
end else if ((jtag_we_i == `WriteEnable) && (jtag_addr_i != `RegNumLog2'h0)) begin
//jtag模块的写操作
regs[jtag_addr_i] <= jtag_data_i;
end
end
end