CPU 速通系列 2-2:regfile 模块
代码
Path src/cpu/regfile.sv
1`include "defines.svh"
2
3module regfile (
4
5 input logic clk,
6 input logic rst,
7
8 //写端口
9 input logic we,
10 input logic [`RegAddrBus] waddr,
11 input logic [ `RegBus] wdata,
12
13 //读端口 1
14 input logic re1,
15 input logic [`RegAddrBus] raddr1,
16 output logic [ `RegBus] rdata1,
17
18 //读端口 2
19 input logic re2,
20 input logic [`RegAddrBus] raddr2,
21 output logic [ `RegBus] rdata2
22
23);
24
25 logic [`RegBus] regs[0:`RegNum-1];
26
27 always_ff @(posedge clk) begin
28 integer i;
29 if (rst == `RstEnable) begin
30 for (i = 0; i < `RegNum; i = i + 1) begin
31 regs[i] <= '0;
32 end
33 end
34 end
35
36 always_ff @(posedge clk) begin
37 if (rst == `RstDisable) begin
38 if ((we == `WriteEnable) && (waddr != `RegNumLog2'h0)) begin
39 regs[waddr] <= wdata;
40 end
41 end
42 end
43
44 always_comb begin
45 if (rst == `RstEnable) begin
46 rdata1 = `ZeroWord;
47 end else if (raddr1 == `RegNumLog2'h0) begin
48 rdata1 = `ZeroWord;
49 end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin
50 rdata1 = wdata;
51 end else if (re1 == `ReadEnable) begin
52 rdata1 = regs[raddr1];
53 end else begin
54 rdata1 = `ZeroWord;
55 end
56 end
57
58 always_comb begin
59 if (rst == `RstEnable) begin
60 rdata2 = `ZeroWord;
61 end else if (raddr2 == `RegNumLog2'h0) begin
62 rdata2 = `ZeroWord;
63 end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
64 rdata2 = wdata;
65 end else if (re2 == `ReadEnable) begin
66 rdata2 = regs[raddr2];
67 end else begin
68 rdata2 = `ZeroWord;
69 end
70 end
71
72endmodule
解读
实现了一个寄存器文件(Register File,或许叫做寄存器堆更贴切)模块,用于存储和读取数据。
设计意图
简要而言,这个模块包含了一个寄存器数组,可以通过写端口写入数据,并通过读端口读取数据
具体而言,这个寄存器文件模块具有以下功能:
-
通过写端口(we)可以写入数据,包括写入地址(waddr)和写入数据(wdata)。
-
通过读端口 1(re1)可以读取数据,包括读取地址(raddr1)和读取数据结果(rdata1)。
-
通过读端口 2(re2)可以读取数据,包括读取地址(raddr2)和读取数据结果(rdata2)。
-
在上升沿时钟(clk)触发下,根据写入使能信号(we)、写入地址(waddr)和写入数据(wdata)更新寄存器数组。
-
在组合逻辑中,根据读取使能信号(re1/re2)和读取地址(raddr1/raddr2)生成相应的读取数据(rdata1/rdata2)。
端口
-
clk
:输入时钟信号 -
rst
:复位信号 -
we
:写使能信号 -
waddr
:写地址 -
wdata
:写数据 -
re1
:读使能信号 1 -
raddr1
:读地址 1 -
rdata1
:读数据 1 -
re2
:读使能信号 2 -
raddr2
:读地址 2 -
rdata2
:读数据 2
实现思路
-
使用了一个
logic
类型的数组regs
,数组大小为 0 到RegNum-1
。 -
在时钟上升沿触发的
always_ff
块中,当复位信号(rst)为使能状态时,遍历整个寄存器数组,并将所有寄存器的值设置为 0。 -
在时钟上升沿触发的另一个
always_ff
块中,当复位信号(rst)为禁用状态时,当写使能信号(we)为使能状态且写地址(waddr)不等于 0 时,将写数据(wdata)写入到指定的寄存器中。 -
在组合逻辑的
always_comb
块中,根据不同的情况设置读取数据(rdata1/rdata2)的值:-
如果复位信号(rst)为使能状态,则将读取数据设置为零(
ZeroWord
)。 -
如果读地址(raddr1/raddr2)为 0,则将读取数据设置为零(
ZeroWord
)。 -
如果读地址(raddr1/raddr2)等于写地址(waddr),且写使能信号(we)为使能状态且读使能信号(re1/re2)为使能状态,则将读取数据设置为写入数据(wdata)。
-
如果只有读使能信号(re1/re2)为使能状态,则从寄存器数组中读取相应地址(raddr1/raddr2)处的数据作为读取数据(rdata1/rdata2)。
-
如果以上条件都不满足,则将读取数据设置为零(
ZeroWord
)。
-
FaQ
这个 regfile 的容量有多大?
在给定的代码中,RegBus
和 RegNum
是宏定义,决定了 regfile 的大小。
-
RegBus
表示每个寄存器中存储的数据的位宽(bit width)。该宏定义决定了每个寄存器能够存储的数据的大小。 -
RegNum
表示寄存器文件中寄存器的数量。该宏定义决定了寄存器文件中可以存储的寄存器的个数。
如果写和读同时进行,会发生冒险吗?
不会。写操作不会优先于读操作执行,而是在读操作之前执行。
在组合逻辑的 always_comb
块中,针对读端口1和读端口2的逻辑如下:
1always_comb begin
2 if (rst == `RstEnable) begin
3 rdata1 = `ZeroWord;
4 end else if (raddr1 == `RegNumLog2'h0) begin
5 rdata1 = `ZeroWord;
6 end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin
7 rdata1 = wdata;
8 end else if (re1 == `ReadEnable) begin
9 rdata1 = regs[raddr1];
10 end else begin
11 rdata1 = `ZeroWord;
12 end
13end
1always_comb begin
2 if (rst == `RstEnable) begin
3 rdata2 = `ZeroWord;
4 end else if (raddr2 == `RegNumLog2'h0) begin
5 rdata2 = `ZeroWord;
6 end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
7 rdata2 = wdata;
8 end else if (re2 == `ReadEnable) begin
9 rdata2 = regs[raddr2];
10 end else begin
11 rdata2 = `ZeroWord;
12 end
13end
根据以上代码,读操作的逻辑优先于写操作的逻辑。首先,会检查是否需要进行复位操作(rst ==
RstEnable),如果是,则读取数据设置为零。接下来,会检查读地址是否为0(
raddr1 == RegNumLog2'h0
和 raddr2 ==
RegNumLog2’h0),如果是,则读取数据设置为零。然后,会检查是否有读写操作同时进行且读写地址相同的情况,如果是,则读取数据设置为写入数据(
wdata`)。最后,如果只有读使能信号为使能状态,那么从寄存器数组中读取相应地址处的数据作为读取数据。
综上所述,根据给定的代码逻辑,读操作会在写操作之后执行,并且如果读写操作的地址相同,读操作会读取到写入的数据。
参考资料
本文与 AI 合作完成。