寄存器堆实验
参考设计
参考设计的虚拟面板如图 1所示,其中包含了一个锁存器和一个触发器。 例 1是N位触发器模块,在计算机中通常用做数据寄存器,在以后的实验中将会多次使用该模块。 例 2是N位锁存器模块。 例 3 VirtualBoard模块实例化上面两个模块以及前面实验完成的七段数码管模块。 通过验证理解触发器和锁存器的区别。
module DataReg
#(parameter N = 4)
( output reg [N-1:0] oQ,
input wire [N-1:0] iD,
input wire iClk,
input wire iLoad,
input wire iReset
);
always_ff @(posedge iClk or posedge iReset)
begin
if (iReset)
oQ <= 0;
else if (iLoad)
oQ <= iD;
end
endmodule
module DataLatch
#(parameter N = 4)
( output reg [N-1:0] oQ,
input wire [N-1:0] iD,
input wire iEn
);
always_latch
begin
if (iEn)
oQ <= iD;
end
endmodule
/****** Replace input ports with internal signals *******/
wire reset = PB[0];
wire clk = PB[1];
wire [3:0] data = S[3:0];
wire load = S[6];
/************* The logic of this experiment *************/
localparam N = 4; //寄存器字长
// flip-flop instantiation
wire [N-1:0] Q_ff;
DataReg #(N) flip_flop(.oQ(Q_ff), .iD(data), .iClk(clk), .iLoad(load), .iReset(reset));
// latch instantiation
wire [N-1:0] Q_latch;
DataLatch #(N) latch(.oQ(Q_latch), .iD(data), .iEn(load));
/***** Internal signals assignment to output ports ******/
SevenSegDecode ssdecoder0(.iData(Q_ff), .oSeg(SD0));
SevenSegDecode ssdecoder1(.iData(Q_latch), .oSeg(SD1));
assign L[0] = load;
assign L[1] = load;
注: 为节省篇幅,省略了模块的端口声明。完整代码可从开源项目托管网站下载,下载方法见下载实验材料。
实验原理
CPU内部通常包含若干个通用寄存器(Genral Register),以暂存参加运算的数据和中间结果。这些寄存器的集合就称为寄存器组(Register Set)、寄存器文件(Register File)或寄存器堆。为方便访问其中的寄存器,对寄存器堆中的寄存器进行统一编码,称为寄存器号(Index)或者寄存器地址。单端口是指读出和写入共用同一组地址或数据端口,所以读出和写入不能同时进行,通常应用于多周期的数据通路;而三端口的读、写端口各自独立,在一个周期中既可以读出也可以写入,通常应用于单周期数据通路。本实验完成三端口寄存器堆。
图 2是具有2个读端口和1个写端口的三端口寄存器堆的电路结构。其中RA表示读端口地址(Read Address),RD表示读端口数据(Read Data);WA表示写端口地址(Write Address),WD表示写端口数据(Write Data);WE是写使能(Write Enable)。
实验任务
理解图 2结构,采用模块化和层次化的方法设计该寄存器堆。具体要求如下。
-
使用已经经过验证的模块。
-
2-4译码器模块使用译码器实验的参考设计;
-
七段译码器模块使用译码器实验中自己设计完成的;
-
寄存器模块使用例 1触发器的参考设计。
-
-
设计4选1多路器模块,端口声明如下。
module Mux4_1 #(parameter N=4) ( input wire [1:0] iSel, input wire [N-1:0] iDin0, input wire [N-1:0] iDin1, input wire [N-1:0] iDin2, input wire [N-1:0] iDin3, output logic [N-1:0] oDout );
功能描述要求使用case语句,而不是嵌套的if语句或条件表达式。
-
编写VirtualBoard模块
-
端口连接应与虚拟面板对应,可通过实验系统查看虚拟元件的序号;为节省查对时间,例 4给出了与虚拟面板一致的端口连接。与输入、输出端口连接的内部变量的名称可自行定义,不必与示例相同。
例 4. 三端口寄存器堆虚拟面板对应的端口连接/****** Replace input ports with internal signals *******/ wire clk = PB[1]; wire [1:0] read_addr1 = S[1:0]; wire [1:0] read_addr2 = S[3:2]; wire [1:0] write_addr = S[5:4]; wire write_enable = S[6]; wire [3:0] write_data = S[11:8]; /************* The logic of this experiment *************/ localparam N = 4; //寄存器字长 // 实例化2-4译码器 // 实例化寄存器 // 实例化4-1多路器 /***** Internal signals assignment to output ports ******/ assign L[0] = load0; assign L[1] = load1; assign L[2] = load2; assign L[3] = load3; SevenSegDecode ssdecode_inst0(.iData(R0_Q), .oSeg(SD0)); SevenSegDecode ssdecode_inst1(.iData(R1_Q), .oSeg(SD1)); SevenSegDecode ssdecode_inst2(.iData(R2_Q), .oSeg(SD2)); SevenSegDecode ssdecode_inst3(.iData(R3_Q), .oSeg(SD3)); SevenSegDecode ssdecode_inst4(.iData(read_data1), .oSeg(SD4)); SevenSegDecode ssdecode_inst5(.iData(read_data2), .oSeg(SD5));
-
实例化2-4译码器、4个(或3个)数据寄存器、两个4选1多路器模块,并按照图 2连接各模块。
-
用
localparam
定义寄存器字长N,并且在实例化寄存器和4选1多路器时使用该参数。 -
R0寄存器读出值恒为0。
有多种方法实现R0恒为零。如4选1多路器的“0”输入端连接常数“0”,则无需实例化R0;或者实例化R0,但Reset端口固定连接为
1’b1
,使其一直处于复位状态。 -
寄存器堆不具有复位功能,可以在实例化时将DataReg模块的Reset端口固定连接为
1’b0
。
-
关于写时读
从某一个读端口读出某一个寄存器的内容时,如果写端口同时也在向这个寄存器写入新数据,那么读出的是以前的旧数据还是正在写入的新数据?这两种方案对后面CPU实验流水线数据冲突的解决方案有影响。我们采用的方案是读出之前存入寄存器的旧数据。 在一些教材上也有采用读出新数据的方案,其具体实现方法有两种。一种是在前半个周期写入,后半个周期从已经写入的寄存器中读出,这种方法需要有足够的周期长度;另一种方法是“旁路”,如果读出的寄存器号与写入的寄存器号相等,写寄存器的同时将要写入的数据直接送到读端口。 |