2021龙芯杯 第二次实验:Reg_file寄存器堆

实验任务书

基础实验包:reg_file

 

龙芯杯比赛所设计的CPU是基于MIPS指令集的,所以我们应该先熟悉MIPS指令集CPU的设计规范。

关于寄存器这块,我找到了下列资料。

 

MIPS通用寄存器

MIPS指令集简明手册:MIPS汇编语言学习笔记03:print HelloWorld,强烈推荐!!文章内有一个pdf手册,即MIPS简明手册。

建议先学MIPS汇编,这样会对MIPS体系有着较为深刻的理解。MIPS学习笔记

 

MIPS寄存器模块特点

寄存器堆的功能:提供32个32位寄存器,并提供“一写两读”功能。

写寄存器

如果我们想把一个内容写入寄存器,那么至少先需要复位失效(rst == Disable)并且需要时钟信号来临(posedge clk)。

将指定内容写入到寄存器,需要:

  1. 写入的数据本身(写数据)
  2. 写入编号为几的寄存器(写地址)

因为对于写寄存器来说,我们至少需要两个input端口完成。

为了方便对写寄存器的控制,我们引入了控制信号wen(写使能)。

PS:

  1. MIPS规定0号寄存器不能被写入,所以我们还需要添加判断
  2. 第0号寄存器如果没有被写入任何值,那么它是一个非稳态数值(即 32’hx)。需要在rst信号有效的时候将其先行赋值0

 

读寄存器

目标:实现两读功能。

为方便译码阶段立即取得寄存器的数据,不同于写数据阶段使用时序逻辑,在读阶段使用的是组合电路,也就是说读阶段不受始终约束

读取的话就比较简单,只需要:

  1. 读1的数据
  2. 读1的地址
  3. 读2的数据
  4. 读2的地址

 

注意:无论是写寄存器还是读寄存器,testbench给的测试代码是非常无语的,甚至有给-8寄存器赋值的代码验证。所以我们无论是读还是写寄存器都需要对寄存器编号进行判断!

更正:这里好像不是testbench问题,我把Wave显示格式调成二进制后发现正常的。

调成十进制就会是负数,我不知道为什么?

 

寄存器堆verilog实现代码

  1. `timescale 10 ns / 1 ns
  2. `define DATA_WIDTH 32
  3. `define ADDR_WIDTH 5
  4. module reg_file(
  5. input clk,
  6. input rst,
  7. input wire [`ADDR_WIDTH - 1:0] waddr, // 写地址
  8. input wire [`ADDR_WIDTH - 1:0] raddr1,
  9. input wire [`ADDR_WIDTH - 1:0] raddr2,
  10. input wire wen, // 写使能
  11. input wire [`DATA_WIDTH - 1:0] wdata, // 写数据
  12. output reg [`DATA_WIDTH - 1:0] rdata1,
  13. output reg [`DATA_WIDTH - 1:0] rdata2
  14. );
  15. reg [31:0] r [31:0];
  16. always @ (posedge clk) // 时钟上升沿来临
  17. begin
  18. if(rst==1)
  19. begin // when rst is high, reset everthing to 0
  20. r[0] <= 0;
  21. end
  22. if(wen==1)
  23. begin //when wen is high, write register
  24. if (waddr >= 1 && waddr <= 31) begin
  25. r[waddr] <= wdata;
  26. end
  27. // TODO: Please add your logic code here
  28. end
  29. end
  30. always @(*) begin
  31. begin // code for output rdata1 and rdata2
  32. if (raddr1 >= 0 && raddr1 <= 31) begin
  33. rdata1 <= r[raddr1];
  34. end
  35. if (raddr2 >= 0 && raddr2 <= 31) begin
  36. rdata2 <= r[raddr2];
  37. end
  38. end
  39. end
  40. endmodule
`timescale 10 ns / 1 ns

`define DATA_WIDTH 32
`define ADDR_WIDTH 5

module reg_file(
	input clk,
	input rst,
    
	input wire [`ADDR_WIDTH - 1:0] waddr,    // 写地址
	input wire [`ADDR_WIDTH - 1:0] raddr1,
	input wire [`ADDR_WIDTH - 1:0] raddr2,
	input wire wen,                          // 写使能
	input wire [`DATA_WIDTH - 1:0] wdata,    // 写数据
    
	output reg [`DATA_WIDTH - 1:0] rdata1,
	output reg [`DATA_WIDTH - 1:0] rdata2
);

reg [31:0] r [31:0];


always @ (posedge clk)      // 时钟上升沿来临
begin

	if(rst==1)
	begin  // when rst is high, reset everthing to 0
        r[0] <= 0;
	end

	if(wen==1)
	begin  //when wen is high, write register
        if (waddr >= 1 && waddr <= 31) begin
            r[waddr] <= wdata;
        end
		// TODO: Please add your logic code here
	end
	
end

always @(*) begin
    begin  // code for output rdata1 and rdata2
        if (raddr1 >= 0 &&  raddr1 <= 31) begin
            rdata1 <= r[raddr1];
        end
        if (raddr2 >= 0 &&  raddr2 <= 31) begin
            rdata2 <= r[raddr2];
        end
    end
end

endmodule

 

参考文献:

https://github.com/gzhy5111/cpu/blob/master/codes/Chapter7/regfile.v

作者: 高志远

高志远,24岁,男生

发表评论

邮箱地址不会被公开。