数字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口写入。
支持片选,读写请求,要求代码可综合。

  1. 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
  1. 通过两个分别独立的端口data_adata_brdena_nwrenb_n分别控制读写请求信号。
    当置位信号置0时,对存储器register 进行初始化0000_1111
    写信号有效时,给一个写进内存的地址addr_b以及要写进该地址的数data_b。
    读信号有效时,给一个写进内存的地址addr_a以及要读出该地址的数data_a。

    register[addr_b] <= data_b; //写入
    data_a <= register[addr_a]; //读出

  2. 测试代码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

其中测试过程中在连续的时钟周期内,产生一串读写地址:
在这里插入图片描述

  1. 测试结果:
    在这里插入图片描述
    下图可以看到仿真过程中存储器 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
  1. 两者的唯一区别就是时钟的不一样(异步时钟,一般设计中避免使用)
  2. 仿真测试结果:
//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

仿真测试代码在此处略去,和之前基本相似。有兴趣可以自己动手编写仿真。

  • 17
    点赞
  • 266
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论
我可以为您提供一个基本的参数化同步端口SRAMVerilog实现示例。以下是一个简单的例子,您可以根据自己的需求进行修改和扩展: ```verilog module DualPortRAM # ( parameter DATA_WIDTH = 8, // 数据位宽 parameter ADDR_WIDTH = 10 // 地址位宽 ) ( input wire clk, // 时钟信号 input wire reset, // 复位信号 input wire [ADDR_WIDTH-1:0] addrA, // 端口A地址输入 input wire [ADDR_WIDTH-1:0] addrB, // 端口B地址输入 input wire [DATA_WIDTH-1:0] dataA, // 端口A数据输入 input wire [DATA_WIDTH-1:0] dataB, // 端口B数据输入 input wire writeA, // 端口A写使能信号 input wire writeB, // 端口B写使能信号 output wire [DATA_WIDTH-1:0] qA, // 端口A数据输出 output wire [DATA_WIDTH-1:0] qB // 端口B数据输出 ); // 内部存储器数组 reg [DATA_WIDTH-1:0] mem [0:(2**ADDR_WIDTH)-1]; // 读写逻辑 always @(posedge clk or posedge reset) begin if (reset) mem <= '0; // 复位存储器内容为全0 if (writeA) mem[addrA] <= dataA; // 端口A写操作 if (writeB) mem[addrB] <= dataB; // 端口B写操作 qA <= mem[addrA]; // 端口A读操作 qB <= mem[addrB]; // 端口B读操作 end endmodule ``` 这是一个简单的端口SRAM,具有参数化的数据位宽和地址位宽。您可以根据需要调整参数值,并根据自己的需求进行修改和扩展。请注意,这只是一个示例,实际应用中可能需要添加更多的功能和控制信号。 希望对您有所帮助!如果您有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摆渡沧桑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值