数字IC设计——SRAM的Verilog语言实现(双端口SRAM)
Posted 摆渡沧桑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数字IC设计——SRAM的Verilog语言实现(双端口SRAM)相关的知识,希望对你有一定的参考价值。
数字IC设计——SRAM的Verilog语言实现(三)(双端口SRAM)
在FPGA设计中,经常会用到RAM,这里的RAM一般指的是静态的RAM(SRAM)。一般FPGA(如xilinx)中就有所谓的block RAM, 它就是现成的RAM资源,我们如果合理编写verilog代码,就可以使我们想要的RAM被综合成block RAM,从而节省逻辑资源,而且性能更优。
主要设计如下ram:
1)用Verilog实现一个同步双端口sram,深度16,位宽8bit。A口读出,B口写入。支持片选,读写请求,要求代码可综合。
2)用Verilog实现一个异步双端口sram,深度16,位宽8bit。A口读出,B口写入。支持片选,读写请求,要求代码可综合。
3)用Verilog实现一个同步双端口sram,深度16,位宽8bit。A口可读可写,B口可读可写。支持片选,读写请求,要求代码可综合。
一、用Verilog实现一个同步双端口sram,深度16,位宽8bit
要求:A口读出,B口写入。
支持片选,读写请求,要求代码可综合。
- Verilog 代码实现
module mini_sp_ram #(
parameter ADDR_WIDTH=4,
parameter DATA_WIDTH=8,
parameter DATA_DEPTH=16
)(
input clk,
input rst_n,
input csen_n, //片选信号
//port A signal
input [ ADDR_WIDTH-1:0] addr_a, //读地址
input rdena_n, //读信号
output reg [ DATA_WIDTH-1:0] data_a,
//port B signal
input [ ADDR_WIDTH-1:0] addr_b, //写地址
input wrenb_n, //写信号
input [ DATA_WIDTH-1:0] data_b
);
integer i;
reg [DATA_WIDTH-1:0] register [DATA_DEPTH-1:0]; //定义一个深度为16,位宽为8的存储器
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin //初始化
for(i=0; i<DATA_DEPTH;i=i+1) begin
register[i] <= 8'b0000_1111;
end
end
else if (wrenb_n == 1'b0 && csen_n == 1'b0) begin //写信号低有效
register[addr_b] <= data_b; //写入
end
end
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin //输出初始化
data_a <= 8'h0;
end
else if (rdena_n == 1'b0 && csen_n == 1'b0) begin //读信号低有效
data_a <= register[addr_a]; //读出
end
else begin
data_a <= data_a;
end
end
endmodule
-
通过两个分别独立的端口data_a和data_b,rdena_n和wrenb_n分别控制读写请求信号。
当置位信号置0时,对存储器register 进行初始化0000_1111
写信号有效时,给一个写进内存的地址addr_b以及要写进该地址的数data_b。
读信号有效时,给一个写进内存的地址addr_a以及要读出该地址的数data_a。register[addr_b] <= data_b; //写入
data_a <= register[addr_a]; //读出 -
测试代码testbench:
module mem_tb(
);
reg clk,rst_n,csen_n;
reg rdena_n,wrenb_n;
reg [3:0] addr_a,addr_b;
reg [7:0] data_b;
wire [7:0] data_a;
integer i;
mini_sp_ram #(.ADDR_WIDTH(4),
.DATA_WIDTH(8),
.DATA_DEPTH(16)
) u_ram(
.clk(clk),
.rst_n(rst_n),
.csen_n(csen_n),
.wrenb_n(wrenb_n),
.rdena_n(rdena_n),
.addr_a(addr_a),
.addr_b(addr_b),
.data_a(data_a),
.data_b(data_b)
);
//clk generate
always #5 clk = ~clk;
initial begin
clk = 1'b1;
rst_n = 1'b0;
csen_n = 1'b1;
wrenb_n = 1'b1;
rdena_n = 1'b1;
addr_a = 'b0;
#30
rst_n = 1'b1;
csen_n = 1'b0;
#5
@(posedge clk)
wrenb_n = 1'b0; //写信号有效
for (i=0;i<16;i=i+1)begin
@(posedge clk) begin
addr_b = i; //存储器每一个地址号
data_b = 8'b0000_0000+i; //每个循环向存储器中写入一个数据
end
end
#30
wrenb_n = 1'b1;
@(posedge clk)
rdena_n = 1'b0; //读信号有效
for (i=0;i<16;i=i+1)begin
@(posedge clk)begin
addr_a = i; //每个循环向外部data_a读出一个存储器地址内的数值
end
end
#30
csen_n = 1'b1;
//200s stop
#100 $stop;
end
endmodule
其中测试过程中在连续的时钟周期内,产生一串读写地址:
- 测试结果:
下图可以看到仿真过程中存储器 register 中数据值。
二、用Verilog实现一个异步双端口ram,深度16,位宽8bit
要求:
A口读出,B口写入。
支持片选,读写请求,要求代码可综合。
module Dual_Port_Sram
#(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 16
)
(
input clka,
input clkb,
input rst_n,
input csen_n,
//Port A Signal
input [ADDR_WIDTH-1:0] addra,
output reg [DATA_WIDTH-1:0] data_a,
input rdena_n,
//Port B Signal
input [ADDR_WIDTH-1:0] addrb,
input wrenb_n,
input [DATA_WIDTH-1:0] data_b
);
integer i;
reg [DATA_WIDTH-1:0] register[DATA_DEPTH-1:0];
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < DATA_DEPTH; i = i + 1)
register[i] <= 'b0000_1111;
end
else if(wrenb_n == 1'b0 && csen_n == 1'b0)
register[addrb] <= data_b;
end
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_a <= 0;
end
else if(rdena_n == 1'b0 && csen_n == 1'b0)
data_a <= register[addra];
else
data_a <= data_a;
end
endmodule
- 两者的唯一区别就是时钟的不一样(异步时钟,一般设计中避免使用)
- 仿真测试结果:
//clk generate
always #5 clk1 = ~clk1;
initial begin
clk2 = 1'b1;
#2
forever #5 clk2 = ~clk2;
end
三、用Verilog实现一个同步双端口sram,深度16,位宽8bit。
要求:A口可读可写,B口可读可写。
支持片选,读写请求,要求代码可综合。
module M_Dual_Port_RAM(
input clk,
input rst_n,
input cs_n,
//Port A Signal
input [7:0] dina,
input [3:0] addra,
output reg [7:0] douta,
input wra_n,
input rda_n,
//Port B Signal
input [7:0] dinb,
input [3:0] addrb,
output reg [7:0] doutb,
input wrb_n,
input rdb_n
);
integer i;
reg [7:0] register[15:0];
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < 16; i = i + 1)begin
register[i] = 0; //存储器初始化
end
end
else if(wra_n == 1'b0 && cs_n == 1'b0) //a写入,给定一个addr值,写入一个数据进存储器
register[addra] <= dina;
else if(wrb_n == 1'b0 && cs_n == 1'b0) //b写入
register[addrb] <= dinb;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin //输出端口初始化
douta <= 0;
doutb <= 0;
end
else if(rda_n == 1'b0 && cs_n == 1'b0)begin
douta <= register[addra]; //a读出
doutb <= doutb;
end
else if(rdb_n == 1'b0 && cs_n == 1'b0)begin
doutb <= register[addrb]; //b读出
douta <= douta;
end
else begin
douta <= douta;
doutb <= doutb;
end
end
endmodule
仿真测试代码在此处略去,和之前基本相似。有兴趣可以自己动手编写仿真。
以上是关于数字IC设计——SRAM的Verilog语言实现(双端口SRAM)的主要内容,如果未能解决你的问题,请参考以下文章