少女祈祷中...

RF4x8模块硬件描述

基本功能描述:

  • 读写全双工。写入数据需要①拉高一位使能信号en_wr并②给定地址Addr_wr和③数据Data_wr;读取数据需要给定地址Addr_rd_X,并支持两路读取(X=A/B)。
  • 数据位宽:8bit;地址位宽:4bit,地址总数2^4=16个。即本RF4x8中可以存放16个8bit数据
  • 时序:clk上升沿检测钟控;rst下降沿检测,异步复位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module RF4x8 #( parameter
DATA_WIDTH = 8,
ADDR_WIDTH = 4
)(
input wire clk,
input wire rst,

input wire en_wr,
input wire[DATA_WIDTH-1:0] Data_wr, //写数据:位宽为8bit
input wire[ADDR_WIDTH-1:0] Addr_wr, //写地址:地址位宽为4bit,因此总地址数量为2^4 = 16

input wire[ADDR_WIDTH-1:0] Addr_rd_A, //读地址A
input wire[ADDR_WIDTH-1:0] Addr_rd_B, //读地址B
output reg[DATA_WIDTH-1:0] Data_rd_A,
output reg[DATA_WIDTH-1:0] Data_rd_B
);

reg[2**ADDR_WIDTH-1:0] addr;
reg[DATA_WIDTH-1:0] data[0:2**ADDR_WIDTH-1]; //[7:0] data[0:15] -> 16个地址,每个地址下存储一个8bit数据

//reset:清空数据
integer i;
always@(posedge clk or negedge rst)
begin
if(!rst) begin
for(i=0; i<2**ADDR_WIDTH; i=i+1) data[i] <= 8'b0000_0000;
end
end

//写数据
always@(posedge clk)
begin
if(en_wr) //写使能en_wr==1'b1
data[Addr_wr] <= Data_wr;
else
data[Addr_wr] <= data[Addr_wr];
end

//读数据
always@(posedge clk)
begin
Data_rd_A <= data[Addr_rd_A]; //A通道
Data_rd_B <= data[Addr_rd_B]; //B通道
end

endmodule

RF4x8仿真分析

仿真模块RF4x8_tb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
`timescale 1ns / 10ps
module RF4x8_tb();

parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 4;
reg clk,rst;
reg en_wr;
reg[DATA_WIDTH-1:0] Data_wr;
reg[ADDR_WIDTH-1:0] Addr_wr;
reg[ADDR_WIDTH-1:0] Addr_rd_A,Addr_rd_B;
wire[DATA_WIDTH-1:0] Data_rd_A,Data_rd_B;

initial begin
clk <= 1'b0;
rst <= 1'b1;
en_wr <= 1'b0;
end
always #10 clk <= ~clk;

initial begin
$monitor("%t Data_rd_A=%b Data_rd_B=%b",$time,Data_rd_A,Data_rd_B);
#10 rst <= 1'b0;
#10 rst <= 1'b1;
//测试写入
#20 en_wr <= 1'b1; Addr_wr <= 4'b0000; Data_wr <= 8'b1001_0011;
#20 en_wr <= 1'b1; Addr_wr <= 4'b0001; Data_wr <= 8'b1111_0000;
#20 en_wr <= 1'b1; Addr_wr <= 4'b1100; Data_wr <= 8'b0000_1011;
#20 en_wr <= 1'b1; Addr_wr <= 4'b1111; Data_wr <= 8'b0100_1111;
//测试读取
en_wr <= 1'b0;
#20 Addr_rd_A <= 4'b0000; Addr_rd_B <= 4'b0000;
#20 Addr_rd_A <= 4'b0000; Addr_rd_B <= 4'b0001;
#20 Addr_rd_A <= 4'b1111; Addr_rd_B <= 4'b1100;
//测试读写双全工
#20 en_wr <= 1'b1; Addr_wr <= 4'b0101; Data_wr <= 8'b1111_1111;
#20 Addr_rd_A <= 4'b0101; Addr_rd_B <= 4'b0101;
#50
//测试复位
#10 rst <= 1'b0; en_wr <= 1'b0; Addr_wr <= 4'bx; Data_wr <= 8'bx;
#10 rst <= 1'b1;
#20 Addr_rd_A <= 4'b0101; Addr_rd_B <= 4'b0101;
#20
$finish;
end


RF4x8 RF4x8_for_simultion(
.clk(clk),
.rst(rst),

.en_wr(en_wr),
.Data_wr(Data_wr),
.Addr_wr(Addr_wr),

.Addr_rd_A(Addr_rd_A),
.Addr_rd_B(Addr_rd_B),
.Data_rd_A(Data_rd_A),
.Data_rd_B(Data_rd_B)
);

endmodule

仿真结果时序分析

  • 40ns,60ns,80ns,100ns时写数据:拉高写使能信号en_wr,分别向地址0000,0001,1100和1111写入数据10010011,11110000,00001011和01001111。写地址和写数据信号分别在50ns,70ns,90ns和110ns时刻的clk上升沿被捕捉到。
  • 120ns时读数据:给出A/B端口读数据地址0000和0000,并在130ns的clk上升沿被捕捉(图1中粉色虚线所在位置),Data_rd_A/Data_rd_B分别读取到地址0000处的数据10010011。
  • 130ns时读数据:改变B端口读地址为0001,在140ns的clk上升沿被捕捉,Data_rd_B读取到地址0001处的数据11110000。
  • 260ns时发生复位:rst被拉低(图2中黄色实线所在位置),此时再读地址0101的数据,发现被重置为00000000。

  • 本测试例中,读地址给出后需要等待半个时钟周期才能读出数据,这是因为读地址信号刚好和clk错开(读地址信号给出时正好是clk下降沿),需要等待clk到达上升沿才能捕捉到读地址信号。如果读写信号再延时半个时钟周期,那么不会出现这样的情况。
  • 关于写使能信号en_wr:虽然支持读写双工,但在读数据时最好要把en_wr拉低。否则当RF与其他模块级联时,可能会导致RF在被读的同时被写入不正确的数据,这在后面的RF_ALU级联测试中会提到。