ISE14.7 True Dual-port RAM 仿真学习

正文

学习使用Xilinx FPGA的双端口RAM,其框图如图所示。
在这里插入图片描述

IP核的配置

BRAM的配置按照寻求配置即可,唯一需要注意的地方就是输出寄存器的配置,这里,A端口都不选择,B端口都选择,对比查看两者的区别。

在这里插入图片描述

整体信息如下图所示

  • A端口具有使能信号,输入数据位宽为8位
  • B端口一直有使能信号,数据位宽为16位
  • A端口的读取Latency为1个时钟,B端口的读取Latency为3个时钟,原因在于B端口多了两个输出寄存器。

在这里插入图片描述

仿真验证

仿真需要验证一下几个问题:

  1. 读写时序如何
  2. 使能对读写的影响
  3. 不同位宽之间如何转换

数据写入

其写入时序可参考数据手册

在这里插入图片描述

写入ram的数据来源于提前生成的txt文件,先将该文件读取到一个内存中,在将其写入BRAM

integer index;

reg [7:0] data_in_int[0:511];


initial begin
    index = 0;
    $readmemh("../verilog/sim/data_in.txt",data_in_int);
    $stop;
end

读写RAM通过一个task完成

task wr_memory;
    input [8:0] start_addr;//写起始地址
    input [8:0] len;//写数据长度
    input wr_en;//1-->write,0-->read
    input [8:0] en_nclk;//N*clk
begin
    //a 端口 使能
    ena = 1; 
    for(index=start_addr;index<(len + start_addr) ;index=index+1'b1)begin
        if(index-start_addr > en_nclk)
            ena = 0;

        @(posedge clka);

        addra = index;
        wea   = wr_en;
        if(wr_en == 1)
            dina  = data_in_int[index][7:0];
        else
            dina = 8'hff;
    end
    @(posedge clka);
    wea = 0;
end
endtask
   //从0地址开始写16个字节数据,使能持续16个clk,虽然使能持续17个,但是数据长度只有16个
   wr_memory(0,16,1,17);

写入16个字节完成后,通过modesim仿真,可查看写入内存的数据,和txt中的数据完全一致

在这里插入图片描述

读取数据

读取数据和写入数据一样,只是将写使能信号wea拉低即可。

//从8地址开始读4个字节数据
wr_memory(8,4,0,4);

读取数据仿真如下图所示,可以看到,在给出地址时,数据并非立马出现,而是相对于地址晚了一个时钟,这便是读取Latency,这里A端口为1个CLK。

在这里插入图片描述

使能信号

再看一下使能信号对写入数据的影响

wr_memory(16,8,1,4);
#(PERIOD*10);
wr_memory(2,4,0,2);

仿真中,向16地址写入8个字节数据,其中,使能信号持续8个时钟,写使能信号持续4个时钟。

可以看到,从地址16(0x10)开始,只写入了四个数据。

在这里插入图片描述

读取时序如下图所示,使能信号持续2个时钟。

可以看到,两个时钟后,使能为低,地址改变,数据也不会变化

在这里插入图片描述

对于使能信号,数据手册中这么解释。

在这里插入图片描述

也就是说,使能的优先级最高,如果没给使能信号,那么读、写、复位都将不会生效。

位宽转换

  • 本仿真中,A端口数据位宽为8位,B端口位宽为16位,那么8位到16位的字节序是怎样转换的?

需要注意一点的是,对于A端口来说,其写入20个数据,相应的地址从0~19

但是对于B端口来说,其位宽是A的两倍,因此对B来说,只有10个数据,那么其地址范围为0~9

这也是为什么B端口的地址位宽比A少一位的原因。

task random_read_b;
    input   [8:0]   start_addr;
    input   [8:0]   end_addr;
    input   [8:0]   rcnt;

    for(index = 0;index < rcnt ; index = index + 1'b1) begin
        @(posedge clkb);
        addrb = {$random} % (end_addr - start_addr);
    end

endtask
   //一共写入20个字节数据,换算成16位只有10个
    random_read_b(0,9,4);

仿真中,随机变换B端口地址,从时序中可以得到以下信息:

  1. 读取数据相对于地址延迟3个CLK,符合IP核中的Latency为3个时钟。

  2. 读取数据时,低字节在低地址,高字节在高地址,典型的小端模式。

  3. B端口的地址,可以等效为 A端口地址右移一位。以仿真为例,B端口读取地址为0x08,其对于数据为A端口的0x10的数据。

    具体关系如下伪代码:

    wire [7:0] addra;
    wire [6:0] addrb;
    
    addra = addrb << 1;
    addrb = addra >>1;
    
    //或者
    addra = {addrb[6:0],1'b0};
    addrb = addra[7:1];
    

在这里插入图片描述

关于地址映射关系,在手册中也有体现。

总结一句话就是,低字节在低地址。

在这里插入图片描述

附录

软件版本

modelsim 使用10.4版本。

ISE为14.7版本。

在这里插入图片描述

参考资料

PG058 - LogiCORE IP Block Memory Generator

仿真代码

module tb_dpram;

// mem Parameters
parameter PERIOD  = 10;


// mem Inputs
reg             clka                        = 0 ;
reg             ena                         = 0 ;
reg   [0 : 0]   wea                         = 0 ;
reg   [11 : 0]  addra                       = 0 ;
reg   [7 : 0]   dina                        = 0 ;
reg   			clkb                        = 0 ;
reg   [0 : 0]   web                         = 0 ;
reg   [10 : 0]  addrb                       = 0 ;
reg   [15 : 0]  dinb                        = 0 ;

// mem Outputs
wire  [7 : 0]   douta                           ;
wire  [15 : 0]  doutb                           ;


reg             rst_n                       = 0 ;

initial
begin
    forever #(PERIOD)  clka=~clka;
end

initial
begin
    #(PERIOD/2);
    forever #(PERIOD/2)  clkb=~clkb;
end

initial
begin
    #(PERIOD*20) rst_n  =  1;
end

//a port write adn read
//b port only read
mem  u_mem (
    .clka                    ( clka            ),
    .ena                     ( ena             ),
    .wea                     ( wea    [0 : 0]  ),
    .addra                   ( addra  [11 : 0] ),
    .dina                    ( dina   [7 : 0]  ),
    .clkb                    ( clkb            ),
    .web                     ( 'b0             ),
    .addrb                   ( addrb  [10 : 0] ),
    .dinb                    ( 'd0             ),

    .douta                   ( douta  [7 : 0]  ),
    .doutb                   ( doutb  [15 : 0] )
);


integer index;

reg [7:0] data_in_int[0:511];


initial begin
    index = 0;
    $readmemh("../verilog/sim/data_in.txt",data_in_int);
    $stop;
end

task wr_memory;
    input [8:0] start_addr;//写起始地址
    input [8:0] len;//写数据长度
    input wr_en;//1-->write,0-->read
    input [8:0] en_nclk;//N*clk
begin
    //a 端口 使能
    ena = 1; 
    for(index=start_addr;index<(len + start_addr) ;index=index+1'b1)begin
        if(index-start_addr > en_nclk)
            ena = 0;

        @(posedge clka);

        addra = index;
        wea   = wr_en;
        if(wr_en == 1)
            dina  = data_in_int[index][7:0];
        else
            dina = 8'hff;
    end
    @(posedge clka);
    wea = 0;
end
endtask

task random_read_b;
    input   [8:0]   start_addr;
    input   [8:0]   end_addr;
    input   [8:0]   rcnt;

    for(index = 0;index < rcnt ; index = index + 1'b1) begin
        @(posedge clkb);
        addrb = {$random} % (end_addr - start_addr);
    end

endtask

initial
begin

    @(posedge rst_n);
    #(PERIOD*10);



    //从0地址开始写16个字节数据,使能持续16个clk,虽然使能持续17个,但是数据长度只有16个
    wr_memory(0,16,1,17);

    //从8地址开始读4个字节数据
    wr_memory(8,4,0,4);

    wr_memory(16,8,1,4);
    #(PERIOD*10);
    wr_memory(2,4,0,2);

    #(PERIOD*10);

    //一共写入20个字节数据,换算成16位只有10个
    random_read_b(0,9,4);

    #(PERIOD*10);
    $stop;
end


endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值