远程FPGA虚拟实验平台用SystemVerilog HDL实现寄存器组和堆
原理
单端口寄存器组
单端口寄存器组在实验平台的图如下:
由图可知,单端口寄存器组的单端口指的是写入和读取的地址用的是来自一个端口的信号,在Load=1时,可以通过D,Index和时钟信号写入寄存器的值,同时,由于只有一个端口,四选一多路器读取的值是和写入的值同步的。
三端口寄存器堆
三端口寄存器堆的原理如下:
由图可知,三端口寄存器组较单端口寄存器堆多了一个多路选择器和输出。同时多了两个输入地址的端口RA1和RA2,用以控制两个多路选择器读哪个寄存器的值,这使得它可以在写入1个寄存器的同时读出2个不同寄存器的值。
源代码
单端口寄存器组
VirtalBoard模块
实验面板:
`default_nettype none
module VirtualBoard (
input logic CLOCK, // 10 MHz Input Clock
input logic [19:0] PB, // 20 Push Buttons, logical 1 when pressed
input logic [35:0] S, // 36 Switches
output logic [35:0] L, // 36 LEDs, drive logical 1 to light up
output logic [7:0] SD7, // 8 common anode Seven-segment Display
output logic [7:0] SD6,
output logic [7:0] SD5,
output logic [7:0] SD4,
output logic [7:0] SD3,
output logic [7:0] SD2,
output logic [7:0] SD1,
output logic [7:0] SD0
);
一堆七段译码器的实例化:
logic [3:0] HD[7:0]; // 8 hexadecimal display
SevenSegDecode ssdecode_inst7(.iData(HD[7]), .oSeg(SD7));
SevenSegDecode ssdecode_inst6(.iData(HD[6]), .oSeg(SD6));
SevenSegDecode ssdecode_inst5(.iData(HD[5]), .oSeg(SD5));
SevenSegDecode ssdecode_inst4(.iData(HD[4]), .oSeg(SD4));
SevenSegDecode ssdecode_inst3(.iData(HD[3]), .oSeg(SD3));
SevenSegDecode ssdecode_inst2(.iData(HD[2]), .oSeg(SD2));
SevenSegDecode ssdecode_inst1(.iData(HD[1]), .oSeg(SD1));
SevenSegDecode ssdecode_inst0(.iData(HD[0]), .oSeg(SD0));
单端口需要的线路:
/** The input port is replaced with an internal signal **/
wire clk = PB[1];//时钟信号
wire [3:0] Data = S[3:0];//数据
wire [1:0] Index = S[5:4];//地址
wire Load = S[6];//使能信号
实验逻辑部分:
localparam N = 4;//参数化设计
// 2-4 decode 二四译码器
logic load3, load2, load1, load0;
always_comb begin
if (Load)
case (Index)//用四种状态控制四个寄存器的使能信号load
2'b00: {load3, load2, load1, load0} = 4'b0001;
2'b01: {load3, load2, load1, load0} = 4'b0010;
2'b10: {load3, load2, load1, load0} = 4'b0100;
2'b11: {load3, load2, load1, load0} = 4'b1000;
default: {load3, load2, load1, load0} = 4'bx;
endcase
else
{load3, load2, load1, load0} = 4'b0000;
end
// register instantiation 译码器的实例化
logic [N-1:0] R0_Q, R1_Q, R2_Q, R3_Q;
DataReg #(N) R0(.oQ(R0_Q), .iD(Data), .Clk(clk), .Load(load0), .Reset(1'b0));//由于本实验没有用到复位信号,所以reset恒为0,在待会的三端口寄存器堆里可以将第一个寄存器的信号设置为1使它无法存值
DataReg #(N) R1(.oQ(R1_Q), .iD(Data), .Clk(clk), .Load(load1), .Reset(1'b0));
DataReg #(N) R2(.oQ(R2_Q), .iD(Data), .Clk(clk), .Load(load2), .Reset(1'b0));
DataReg #(N) R3(.oQ(R3_Q), .iD(Data), .Clk(clk), .Load(load3), .Reset(1'b0));
// 4-1 MUX 四选一多路器
logic [N-1:0] GRS_Q;//总线的输出
always_comb begin
case (Index)//根据地址信号选择输出哪个寄存器的值
2'b00: GRS_Q = R0_Q;
2'b01: GRS_Q = R1_Q;
2'b10: GRS_Q = R2_Q;
2'b11: GRS_Q = R3_Q;
endcase
end
/****** Internal signal assignment to output port *******/
assign HD[0] = R0_Q;//把一堆信号和面板的灯什么的连上
assign HD[1] = R1_Q;
assign HD[2] = R2_Q;
assign HD[3] = R3_Q;
assign HD[4] = GRS_Q;
assign L[0] = load0;
assign L[1] = load1;
assign L[2] = load2;
assign L[3] = load3;
endmodule//->用来把面板模块写完
SevenSegDecode模块
沿用七段译码器实验的就好了。
module SevenSegDecode(
input logic [3:0]iData,
output logic [7:0]oSeg
);
always_comb
case(iData)
4'b0000: oSeg = 8'b11000000;
4'b0001: oSeg = 8'b11111001;
4'b0010: oSeg = 8'b10100100;
4'b0011: oSeg = 8'b10110000;
4'b0100: oSeg = 8'b10011001;
4'b0101: oSeg = 8'b10010010;
4'b0110: oSeg = 8'b10000010;
4'b0111: oSeg = 8'b11111000;
4'b1000: oSeg = 8'b10000000;
4'b1001: oSeg = 8'b10010000;
4'b1010: oSeg = 8'b10001000;
4'b1011: oSeg = 8'b10000011;
4'b1100: oSeg = 8'b11000110;
4'b1101: oSeg = 8'b10100001;
4'b1110: oSeg = 8'b10000110;
4'b1111: oSeg = 8'b10001110;
default: oSeg = 8'b11111111;
endcase
endmodule
DataReg模块
根据实验要求,下载DataReg.v文件,然后建议把它放到工程文件夹内的resource文件夹中,然后把文件加入工程,如图所示:
接下来就可以编译啦。
DataReg文件内容如下:
module DataReg
#(parameter N = 4)
( output reg [N-1:0] oQ,
input wire [N-1:0] iD,
input wire Clk,
input wire Load,
input wire Reset
);
always @(posedge Clk or posedge Reset)
begin
if (Reset)
oQ <= 0;
else if (Load)
oQ <= iD;
end
endmodule
三端口寄存器堆
VirtalBoard模块
在单端口寄存器组的基础上修改一下,就可以得到三端口寄存器堆,修改内容如下:
需要用到的线:
/** The input port is replaced with an internal signal **/
wire clk = PB[1];
wire [3:0] Data = S[11:8];
wire [1:0] WIndex = S[5:4];//这个是写入的地址
wire [1:0] RIndex1 = S[1:0];//加了读取的地址
wire [1:0] RIndex2 = S[3:2];
wire Load = S[6];
实验逻辑部分:
localparam N = 4;
// 2-4 decode
logic load3, load2, load1, load0;
always_comb begin
if (Load)
case (WIndex)//改了名字来的
2'b00: {load3, load2, load1, load0} = 4'b0001;
2'b01: {load3, load2, load1, load0} = 4'b0010;
2'b10: {load3, load2, load1, load0} = 4'b0100;
2'b11: {load3, load2, load1, load0} = 4'b1000;
default: {load3, load2, load1, load0} = 4'bx;
endcase
else
{load3, load2, load1, load0} = 4'b0000;
end
// register instantiation
logic [N-1:0] R0_Q, R1_Q, R2_Q, R3_Q;
DataReg #(N) R0(.oQ(R0_Q), .iD(Data), .Clk(clk), .Load(load0), .Reset(1'b1));//这个reset改了
DataReg #(N) R1(.oQ(R1_Q), .iD(Data), .Clk(clk), .Load(load1), .Reset(1'b0));
DataReg #(N) R2(.oQ(R2_Q), .iD(Data), .Clk(clk), .Load(load2), .Reset(1'b0));
DataReg #(N) R3(.oQ(R3_Q), .iD(Data), .Clk(clk), .Load(load3), .Reset(1'b0));
// 4-1 MUX 现在有两个多路器
logic [N-1:0] GRS_Q1, GRS_Q2;
always_comb begin
case (RIndex1)
2'b00: GRS_Q1 = R0_Q;
2'b01: GRS_Q1 = R1_Q;
2'b10: GRS_Q1 = R2_Q;
2'b11: GRS_Q1 = R3_Q;
endcase
case (RIndex2)
2'b00: GRS_Q2 = R0_Q;
2'b01: GRS_Q2 = R1_Q;
2'b10: GRS_Q2 = R2_Q;
2'b11: GRS_Q2 = R3_Q;
endcase
end
DataReg模块
与单端口的一样,但如果在VirtualBoard的实例化DataReg中没有将它的复位信号赋为1’b1,那么可以在DataReg进行修改,如:(不建议这样做,花里胡哨的,就是写一写)
always @(posedge Clk or posedge Reset)
begin
oQ <= 0;
end
endmodule
测试/保存/提交
测试一下没问题之后就可以提交了,需要注意的是本次实验平台上的数据回放有点问题,把代码放上去之后不能做记录。如果记录了,单端口寄存器组就会变成20分,三端口寄存器堆就会变成66.7分,搞不明白,等老师说吧。(更新一下,新的改分好像是好了)