1 双端口ram设计
设计一个512x8的双端口,ram宽度为8位,深度为512,地址位宽为9位。
双端口ram: 独立的读写时钟,独立读写地址和数据端口,并具有读和写的使能信号。
read_clk write_clk
read_addr write_addr
read_data write_data
read_allow write_allow
parameter ram_width = 8
parameter ram_depth = 512
parameter addr_width = 9
module DUALRAM(read_clk,write_clk,read_addr,write_addr,read_data,write_data,read_allow,write_allow);
parameter DLY = 1;
parameter RAM_WIDTH = 8;
parameter RAM_DEPTH = 512;
parameter ADDR_WIDTH = 9;
input read_clk,write_clk,read_allow,write_allow;
input [RAM_WIDTH-1:0] write_data;
output [RAM_WIDTH-1:0] read_data
input [RAM_ADDR-1:0] read_addr;
input [RAM_ADDR-1:0] write_addr;
reg [RAM_WIDTH-1:0] read_data;
reg [RAM_WIDTH-1:0] MEM [RAM_DEPTH-1:0];
always@(posedge write_clk) begin
if(write_allow)
MEM[write_addr] <= #DLY write_data;
end
always@(posedge read_clk) begin
if(read_allow)
read_data <= #DLY MEM[read_addr];
end
endmodule
2 verilog代码中使用#1延迟
为了修正非阻塞赋值的问题,要求加上#1 delay。因此在一般的非阻塞赋值中经常会看到#1的延迟,工程师的解释是为了防止非阻塞赋值崩溃。实际上,加不加#1都不会导致非阻塞赋值崩溃。
在非阻塞赋值的RHS加#1延迟,既有好的原因,也有很多坏的原因。
在非阻塞赋值上加#1,输出变化会有一个时间单位的延迟,便于查看波形。
许多高性能触发器的hold时间是在0~800ps之间。在那些驱动门级模型的RTL模型上加上#1延迟后,通常就可以修正很多与RTL和门级混合仿真相关的问题。
非阻塞赋值加#1的延迟能够提供一些仿真上的便利,但是这个延迟对仿真性能有很大的影响,虽然VCS的+nbaopt编译选项可以对#1延迟的非阻塞赋值提高仿真性能,但是还是没有达到与不使用延迟的非阻塞赋值一样的性能。
3 同步fifo
full 和 empty 1 fifo 对双口ram,通过read_allow,write_allow 实现数据的存储和读取
2 read_allow 和 write_allow 通过full和empty,read_enable和write_enable来实现。
3 空满信号通过Fcount计数器来控制
4 Fcount的更新
输入 复位信号和时钟信号,以及写入数据,读写使能信号。
输出 读到的数据 空满信号,计数器计数来间接控制空满信号。
输入: fifo_rst,clk,read_enable,write_enable,write_data
输出: read_data,full,empty,Fcounter
// Fcounter 计数数据深度。
module SYNCFIFO(Fifo_rst,Clock,Read_enable,Write_enable,Write_data,Read_data,Full,Empty,Fcounter);
parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 9;
input Fifo_rst;
input Clock;
input Read_enable;
input Write_enable;
input [DATA_WIDTH-1:0] Write_data;
output [DATA_WIDTH-1:0] Read_data;
output Full;
output Empty;
output [ADDR_WIDTH-1:0] Fcounter;
reg [DATA_WIDTH-1:0] Read_data;
reg Full;
reg Empty;
reg [ADDR_WIDTH-1:0] Fcounter;
reg [ADDR_WIDTH-1:0] Read_addr;
reg [ADDR_WIDTH-1:0] Write_addr;
wire Read_allow = (Read_enable && !empty); // 空不能读
wire Write_allow = (Write_enable && !Full); //满不能写
DUALRAM U_RAM(
.Read_clock(Clock),
.Write_clock(Clock),
.Read_allow(Read_allow),
.Write_allow(Write_allow),
.Read_addr(Read_addr),
.Write_addr(Write_addr),
.Write_data(Write_data),
.Read_data(Read_data)
);
// 空满信号的产生
always@(posedge Clock or posedge Fifo_rst)
if(Fifo_rst)
Empty <= 1'b1;
else
Empty <= (!Write_enable && (Fcount[8:1] == 8'b0) && ((Fcount[0] == 1'b0) || Read_enable));
always@(posedge Clock or posedge Fifo_rst)
if(Fifo_rst)
Full <= 1'b1;
else
Full <= (!Read_enable && (Fcount[8:1] == 8'hFF) && ((Fcount[0] == 1'b1) || Write_enable));
// 计数器
always@(posedge Clock or posedge Fifo_rst)
if(Fifo_rst)
Fcount <= 1'b0;
else if(read_enable && write_enable)
Fcount <= Fcount;
else if(Read_allow)
Fcount <= Fcount - 1'b1;
else if(Write_allow)
Fcount <= Fcount + 1'b1;
else
Fcount <= Fcount;
// 写数据 读数据控制。 通过Read_allow和Write_allow和双口ram联系起来
always@(posedge Clock or posedge Fifo_rst)
if(Fifo_rst)
Read_addr <= 1'b0;
else if(Read_allow)
Read_addr <= Read_addr + 1'b1;
always@(posedge Clock or posedge Fifo_rst)
if(Fifo_rst)
Write_addr <= 1'b0;
else if(Write_allow)
Write_addr <= Write_addr + 1'b1;
endmodule