一、寄存器堆介绍
对于RISC-V基础指令集中共包含32个通用寄存器(x0~x31)。寄存器堆可以说是整个cpu的核心,我们可以回想RISC-V指令集,几乎所有类型的指令都要与寄存器打交道的(个人理解)。
注意:x0默认为0x00000000,是一个只读寄存器。
二、寄存器堆功能
上图是一个寄存器module,我可以如果先不思考寄存器内部如何实现,只是把寄存器堆想成一个黑匣子,我们可以思考它到底要干什么那?
我个人理解,寄存器堆实际上就是在系统时钟的控制下,从寄存器堆中对于位置读取数据,或者向对应位置写入数据。如果这样理解的话我们就可以思考出一个寄存器堆都需要什么信号了。
1.系统时钟,复位信号(这个相信大家都理解)
2.读写地址信号。既然要向寄存器堆中读写数据,而一个寄存器堆共有32个寄存器,怎么确定要读写那个寄存器呀?事实上每个寄存器在寄存器堆中都有对应的地址,我们只需要给出地址,就可以找到对应地址。(为啥为5位地址:32个寄存器,如果要表示就要用5位二进制数来表示)
3.读写数据信号。如果上面这个地址信号理解了,那么寄存器堆已经知道了要读写的地址,那么这个地址到底要写入啥,或者读出了什么,则一定需要有信号表示读出的数据或者写入的数据。
4.是否写信号。如果对RISC-V指令系统了解的清楚的话,我们就知道有些类型数指令需要向寄存器堆中写入数据(如:I型 R型),而有些类型指令则不需要向寄存器堆中写入数据(如:S型)。因此一定需要一个信号来高速寄存器堆是否读写。
具体如下:
可以看到输入信号有:时钟,复位,rs1,rs2,rd,wen,busw
而输出信号则是:从对应寄存器中读出的数据:BusA,BusB。
三、verilog代码实现
module RegFile(
input clk,
input rstn,
input [4:0]Rs1, //第一个寄存器地址
input [4:0]Rs2, //第二个寄存器地址
input [4:0]Rd, //写入的寄存器地址
input Wen, //控制信号
output [31:0]BusA, //输出第一个寄存器中的值
output [31:0]BusB, //输出第二个寄存器中的值
input [31:0]BusW //写个的寄存器的值
);
reg [31:0]DataReg[31:0]; //定义出来这个寄存器
reg [31:0]BusA1,BusB1; //输出的中间变量
reg [31:0]Waddr;
always@(Rd)begin
case(Rd)
5'b00000 : Waddr=32'h0000_0001;
5'b00001 : Waddr=32'h0000_0002;
5'b00010 : Waddr=32'h0000_0004;
5'b00011 : Waddr=32'h0000_0008;
5'b00100 : Waddr=32'h0000_0010;
5'b00101 : Waddr=32'h0000_0020;
5'b00110 : Waddr=32'h0000_0040;
5'b00111 : Waddr=32'h0000_0080;
5'b01000 : Waddr=32'h0000_0100;
5'b01001 : Waddr=32'h0000_0200;
5'b01010 : Waddr=32'h0000_0400;
5'b01011 : Waddr=32'h0000_0800;
5'b01100 : Waddr=32'h0000_1000;
5'b01101 : Waddr=32'h0000_2000;
5'b01110 : Waddr=32'h0000_4000;
5'b01111 : Waddr=32'h0000_8000;
5'b10000 : Waddr=32'h0001_0000;
5'b10001 :Waddr=32'h0002_0000;
5'b10010 : Waddr=32'h0004_0000;
5'b10011 : Waddr=32'h0008_0000;
5'b10100 : Waddr=32'h0010_0000;
5'b10101 : Waddr=32'h0020_0000;
5'b10110 : Waddr=32'h0040_0000;
5'b10111 : Waddr=32'h0080_0000;
5'b11000 : Waddr=32'h0100_0000;
5'b11001 : Waddr=32'h0200_0000;
5'b11010 : Waddr=32'h0400_0000;
5'b11011 : Waddr=32'h0800_0000;
5'b11100 : Waddr=32'h1000_0000;
5'b11101 : Waddr=32'h2000_0000;
5'b11110 : Waddr=32'h4000_0000;
5'b11111 : Waddr=32'h8000_0000;
endcase
end
wire [31:0] W_en;
assign W_en=Waddr&{32{Wen}};
//向第1个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[0]<=32'd0;
else if(W_en[0]==1'b1) DataReg[0]<=32'd0;
else DataReg[0]<=32'd0;
//向第2个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[1]<=32'd0;
else if(W_en[1]==1'b1) DataReg[1]<=BusW;
else DataReg[1]<=DataReg[1];
//向第3个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[2]<=32'h7fff_fff0;
else if(W_en[2]==1'b1) DataReg[2]<=BusW;
else DataReg[2]<=DataReg[2];
//向第4个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[3]<=32'h1000_0000;
else if(W_en[3]==1'b1) DataReg[3]<=BusW;
else DataReg[3]<=DataReg[3];
//向第5个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[4]<=32'd0;
else if(W_en[4]==1'b1) DataReg[4]<=BusW;
else DataReg[4]<=DataReg[4];
//向第6个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[5]<=32'd0;
else if(W_en[5]==1'b1) DataReg[5]<=BusW;
else DataReg[5]<=DataReg[5];
//向第7个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[6]<=32'd0;
else if(W_en[6]==1'b1) DataReg[6]<=BusW;
else DataReg[6]<=DataReg[6];
//向第8个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[7]<=32'd0;
else if(W_en[7]==1'b1) DataReg[7]<=BusW;
else DataReg[7]<=DataReg[7];
//向第9个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[8]<=32'd0;
else if(W_en[8]==1'b1) DataReg[8]<=BusW;
else DataReg[8]<=DataReg[8];
//向第10个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[9]<=32'd0;
else if(W_en[9]==1'b1) DataReg[9]<=BusW;
else DataReg[9]<=DataReg[9];
//向第11个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[10]<=32'd0;
else if(W_en[10]==1'b1) DataReg[10]<=BusW;
else DataReg[10]<=DataReg[10];
//向第12个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[11]<=32'd0;
else if(W_en[11]==1'b1) DataReg[11]<=BusW;
else DataReg[11]<=DataReg[11];
//向第13个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[12]<=32'd0;
else if(W_en[12]==1'b1) DataReg[12]<=BusW;
else DataReg[12]<=DataReg[12];
//向第14个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[13]<=32'd0;
else if(W_en[13]==1'b1) DataReg[13]<=BusW;
else DataReg[13]<=DataReg[13];
//向第15个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[14]<=32'd0;
else if(W_en[14]==1'b1) DataReg[14]<=BusW;
else DataReg[14]<=DataReg[14];
//向第16个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[15]<=32'd0;
else if(W_en[15]==1'b1) DataReg[15]<=BusW;
else DataReg[15]<=DataReg[15];
//向第17个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[16]<=32'd0;
else if(W_en[16]==1'b1) DataReg[16]<=BusW;
else DataReg[16]<=DataReg[16];
//向第18个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[17]<=32'd0;
else if(W_en[17]==1'b1) DataReg[17]<=BusW;
else DataReg[17]<=DataReg[17];
//向第19个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[18]<=32'd0;
else if(W_en[18]==1'b1) DataReg[18]<=BusW;
else DataReg[18]<=DataReg[18];
//向第20个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[19]<=32'd0;
else if(W_en[19]==1'b1) DataReg[19]<=BusW;
else DataReg[19]<=DataReg[19];
//向第21个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[20]<=32'd0;
else if(W_en[20]==1'b1) DataReg[20]<=BusW;
else DataReg[20]<=DataReg[20];
//向第22个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[21]<=32'd0;
else if(W_en[21]==1'b1) DataReg[21]<=BusW;
else DataReg[21]<=DataReg[21];
//向第23个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[22]<=32'd0;
else if(W_en[22]==1'b1) DataReg[22]<=BusW;
else DataReg[22]<=DataReg[22];
//向第24个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[23]<=32'd0;
else if(W_en[23]==1'b1) DataReg[23]<=BusW;
else DataReg[23]<=DataReg[23];
//向第25个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[24]<=32'd0;
else if(W_en[24]==1'b1) DataReg[24]<=BusW;
else DataReg[24]<=DataReg[24];
//向第26个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[25]<=32'd0;
else if(W_en[25]==1'b1) DataReg[25]<=BusW;
else DataReg[25]<=DataReg[25];
//向第27个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[26]<=32'd0;
else if(W_en[26]==1'b1) DataReg[26]<=BusW;
else DataReg[26]<=DataReg[26];
//向第28个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[27]<=32'd0;
else if(W_en[27]==1'b1) DataReg[27]<=BusW;
else DataReg[27]<=DataReg[27];
//向第29个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[28]<=32'd0;
else if(W_en[28]==1'b1) DataReg[28]<=BusW;
else DataReg[28]<=DataReg[28];
//向第30个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[29]<=32'd0;
else if(W_en[29]==1'b1) DataReg[29]<=BusW;
else DataReg[29]<=DataReg[29];
//向第31个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[30]<=32'd0;
else if(W_en[30]==1'b1) DataReg[30]<=BusW;
else DataReg[30]<=DataReg[30];
//向第32个寄存器写入
always@(posedge clk or negedge rstn)
if(!rstn) DataReg[31]<=32'd0;
else if(W_en[31]==1'b1) DataReg[31]<=BusW;
else DataReg[31]<=DataReg[31];
//读取寄存器a的值
always@(Rs1 or BusW or Rd)begin
case(Rs1)
5'b00000 : BusA1=DataReg[0];
5'b00001 : BusA1=DataReg[1];
5'b00010 : BusA1=DataReg[2];
5'b00011 : BusA1=DataReg[3];
5'b00100 : BusA1=DataReg[4];
5'b00101 : BusA1=DataReg[5];
5'b00110 : BusA1=DataReg[6];
5'b00111 : BusA1=DataReg[7];
5'b01000 : BusA1=DataReg[8];
5'b01001 : BusA1=DataReg[9];
5'b01010 : BusA1=DataReg[10];
5'b01011 : BusA1=DataReg[11];
5'b01100 : BusA1=DataReg[12];
5'b01101 : BusA1=DataReg[13];
5'b01110 : BusA1=DataReg[14];
5'b01111 : BusA1=DataReg[15];
5'b10000 : BusA1=DataReg[16];
5'b10001 :BusA1=DataReg[17];
5'b10010 : BusA1=DataReg[18];
5'b10011 : BusA1=DataReg[19];
5'b10100 : BusA1=DataReg[20];
5'b10101 : BusA1=DataReg[21];
5'b10110 : BusA1=DataReg[22];
5'b10111 : BusA1=DataReg[23];
5'b11000 : BusA1=DataReg[24];
5'b11001 : BusA1=DataReg[25];
5'b11010 : BusA1=DataReg[26];
5'b11011 : BusA1=DataReg[27];
5'b11100 : BusA1=DataReg[28];
5'b11101 : BusA1=DataReg[29];
5'b11110 : BusA1=DataReg[30];
5'b11111 : BusA1=DataReg[31];
endcase
end
always@(Rs2 or BusW or Rd)begin
case(Rs2)
5'b00000 : BusB1=DataReg[0];
5'b00001 : BusB1=DataReg[1];
5'b00010 : BusB1=DataReg[2];
5'b00011 : BusB1=DataReg[3];
5'b00100 : BusB1=DataReg[4];
5'b00101 : BusB1=DataReg[5];
5'b00110 : BusB1=DataReg[6];
5'b00111 : BusB1=DataReg[7];
5'b01000 : BusB1=DataReg[8];
5'b01001 : BusB1=DataReg[9];
5'b01010 : BusB1=DataReg[10];
5'b01011 : BusB1=DataReg[11];
5'b01100 : BusB1=DataReg[12];
5'b01101 : BusB1=DataReg[13];
5'b01110 : BusB1=DataReg[14];
5'b01111 : BusB1=DataReg[15];
5'b10000 : BusB1=DataReg[16];
5'b10001 :BusB1=DataReg[17];
5'b10010 : BusB1=DataReg[18];
5'b10011 : BusB1=DataReg[19];
5'b10100 : BusB1=DataReg[20];
5'b10101 : BusB1=DataReg[21];
5'b10110 : BusB1=DataReg[22];
5'b10111 : BusB1=DataReg[23];
5'b11000 : BusB1=DataReg[24];
5'b11001 : BusB1=DataReg[25];
5'b11010 : BusB1=DataReg[26];
5'b11011 : BusB1=DataReg[27];
5'b11100 : BusB1=DataReg[28];
5'b11101 : BusB1=DataReg[29];
5'b11110 : BusB1=DataReg[30];
5'b11111 : BusB1=DataReg[31];
endcase
end
assign BusA=BusA1;
assign BusB=BusB1;
endmodule
另一种方法
在后面的学习以及和同学交流中我发现其实实现这个寄存器可以不需要这样麻烦代码如下
module Regfile(
input clk_n,
input rst_n,
input [4:0]Rs1, //第一个寄存器地址
input [4:0]Rs2, //第二个寄存器地址
input [4:0]Rd, //写入的寄存器地址
input Wen, //控制信号
output [31:0]BusA, //输出第一个寄存器中的值
output [31:0]BusB, //输出第二个寄存器中的值
input [31:0]BusW //写个的寄存器的值
);
reg [31:0]DataReg[31:0];
//写
always@(negedge clk_n or negedge rst_n)begin
if(!rst_n)begin
DataReg[0]<=32'd0;
DataReg[1]<=32'd0;
DataReg[2]<=32'd0;
DataReg[3]<=32'd0;
DataReg[4]<=32'd0;
DataReg[5]<=32'd0;
DataReg[6]<=32'd0;
DataReg[7]<=32'd0;
DataReg[8]<=32'd0;
DataReg[9]<=32'd0;
DataReg[10]<=32'd0;
DataReg[11]<=32'd0;
DataReg[12]<=32'd0;
DataReg[13]<=32'd0;
DataReg[14]<=32'd0;
DataReg[15]<=32'd0;
DataReg[16]<=32'd0;
DataReg[17]<=32'd0;
DataReg[18]<=32'd0;
DataReg[19]<=32'd0;
DataReg[20]<=32'd0;
DataReg[21]<=32'd0;
DataReg[22]<=32'd0;
DataReg[23]<=32'd0;
DataReg[24]<=32'd0;
DataReg[25]<=32'd0;
DataReg[26]<=32'd0;
DataReg[27]<=32'd0;
DataReg[28]<=32'd0;
DataReg[29]<=32'd0;
DataReg[30]<=32'd0;
DataReg[31]<=32'd0;
end
else if(Wen&Rd!=5'd0) DataReg[Rd]<=BusW;
end
//读
assign BusA=DataReg[Rs1];
assign BusB=DataReg[Rs2];
endmodule
四、总结
代码我是把每一种情况罗列出来的,当然了其实也是可以使用循环的。代码一定要注意x0这个寄存器是只读寄存器,且内部一直时0x00000000(我自己在这里快把自己坑死了)。
代码有什么疑问欢迎私信讨论,一起进步。
内容中存在什么错误也欢迎大家指出。