AHB SRAM控制器设计

本项目用Verilog HDL语言设计了AHB总线上的SRAM控制器,SRAM存储器在AHB总线上作为AHB slave存在,该SRAM控制器具有以下特性:

  1. 支持单周期的SRAM读写操作

  2. 支持低功耗工作
    SRAM存储体由两个Bank组成,系统根据地址选中一块/多块Bank,未被选中的Bank将处于low-power standby模式以降低功耗

  3. 支持DFT功能
    DFT(Design for Test,可测性设计),指通过在芯片原始设计中插入各种用于提高芯片可测试性(包括可控制性和可观测性)的硬件逻辑,从而使芯片变得容易测试,大幅度节省芯片测试的成本。
    本项目中,DFT功能通过BIST(Build-in Self Test,内建自测试)实现,采用March C-作为检测算法

最后,在Vivado平台上对本项目进行了逻辑仿真与验证

1. SRAM数据读写功能的实现

1.1 顶层设计架构

下面给出本项目的顶层设计架构,其中sram_top为顶层模块,其下包含sram_interface模块以及SRAM_core两个子模块

sram_interface模块:本质是AHB总线上的slave接口,起到连接总线与SRAM存储体的作用,具体来说:

  1. 将HCLK,HRESETn,HTRANS,HBURST,HWRITE,HWDATA这些来自于AHB总线的信号转化为存储器接口信号
  2. 接收存储器8位读数据SRAM_q,并根据总线给出的地址,整理成为32位HRDATA,然后返回给AHB总线

sram_core模块:包含两块32位SRAM存储体Bank,其中每块Bank包含4个8k×8的单端口SRAM,本项目中通过例化Vivado中的IP核生成,实际芯片生产应用中常通过Memory Compiler生成

sram_bist模块:使用SRAM读写功能时,可看做8k×8的单端口SRAM;当BIST功能被使能时,将会由sram_bist内部的内建自测试电路生成Pattern对SRAM进行DFT测试。在本项目中,BIST功能将基于March C-算法设计,具体将在本文的第二章中介绍。在第一章中,我们将每个sram_bist模块视为8k×8的单端口SRAM即可

在上图中标注出了模块的主要信号,其中红色、蓝色的信号分别代表了两个不同的数据通路

红色数据通路:正常使用SRAM读写功能时的信号,interface接收来自于AHB总线的信号,并将其转化为SRAM所需要的控制信号和写数据,然后再由interface将SRAM的读数据整理后返回AHB总线

蓝色数据通路:使用DFT功能时的信号。BIST_en = 1时,DFT功能被使能,此时红色信号全部被屏蔽。该功能用于芯片生产完毕之后,对每块芯片进行DFT测试以检测是否有生产故障,该数据通路对于SRAM的逻辑功能来说,属于冗余的部分,但是在实际芯片生产中却是必不可少的

在本章中,我们关注红色数据通路的电路设计,而DFT功能设计将在第二章中进行介绍

1.2 AHB SRAM读写时序

AHB总线时序

其中来自AHB总线的control信号包括HTRANS,HBURST,HWRITE

SRAM接口时序
写时序

读时序

读时序与写时序的区别主要在于SRAM_ADDR的处理上:

对于写操作,为了将地址与数据对齐,sram_interface模块会将总线上的地址与控制信号写入寄存器,

而对于读操作,为了实现总线上的单周期读出,会直接将地址送到SRAM端口(注意:SRAM的时钟为AHB总线时钟信号HCLK)。这样,在数据周期刚开始时,读数据就可以返回HRDATA,

这样的设计具有一些局限性:由于SRAM端口上的读地址相比于写地址要滞后一个周期,因此当写操作的下一个周期切换为读操作时,会产生地址冲突

于是,SRAM控制器会将HREADY拉低一个周期,进行缓冲,下一个周期才会重新拉高HREADY并且返回相应的读数据,

在连续执行多个读操作/连续执行多个写操作时,则不会有这样的问题,可以以AHB总线所允许的最高的速度进行SRAM读写访问,

由于在实际应用中,存储器访问一般不会频繁地在读与写之间切换,因此这样设计对于访问速度带来的代价并不大,

而这样设计的好处,则在于可以避免为SRAM引入额外的时钟源

在明确了AHB SRAM读写的设计需求和读写时序后,我们来看看具体的硬件电路是怎样用Verilog实现的:

1.3 sram_top

首先是顶层模块,主要内容是对两个子模块进行了例化,其中涉及到的信号均已经在架构图中标明,这里不再赘述

module sram_top (
  
  // AHB Signal
  input HCLK, 
  input HRESETn,
  input           HSEL  ,
  input  [1:0]    HTRANS,    
  input  [2:0]    HBURST,
  input  [2:0]    HSIZE ,
  input           HWRITE,
  input  [15:0]   HADDR ,
  input  [31:0]   HWDATA,
  output          HREADY,
  output [1:0]    HRESP ,
  output [31:0]   HRDATA,
  
  // DFT Signal
  input  BIST_en   ,     
  output BIST_done ,
  output BIST_fail
  
  );

  // Wires Between SRAM_interface and SRAM_core
  wire        SRAM_WEN_BANK0;
  wire        SRAM_WEN_BANK1;
  wire [12:0] SRAM_ADDR     ;
  wire [3:0]  SRAM_CSN_BANK0;  
  wire [3:0]  SRAM_CSN_BANK1;  
  wire [31:0] SRAM_WDATA    ;
  
  wire [7:0] SRAM0_q;
  wire [7:0] SRAM1_q;
  wire [7:0] SRAM2_q;
  wire [7:0] SRAM3_q;
  wire [7:0] SRAM4_q;
  wire [7:0] SRAM5_q;
  wire [7:0] SRAM6_q;
  wire [7:0] SRAM7_q;

  /*————————————————————————————————————————————————————————————————————————*\
  /                    SRAM Interface Instantiation                          \
  \*————————————————————————————————————————————————————————————————————————*/
  sram_interface u_interface(

    //---------------AHB SIGNAL--------------
            //in
    .iHCLK   (HCLK   ),
    .iHRESETn(HRESETn),
    .iHSEL   (HSEL   ),
    .iHBURST (HBURST ),
    .iHWRITE (HWRITE ),
    .iHTRANS (HTRANS ),
    .iHSIZE  (HSIZE  ),
    .iHWDATA (HWDATA ),
    .iHADDR  (HADDR  ),
            //out
    .oHRESP  (HRESP  ),
    .oHREADY (HREADY ),
    .oHRDATA (HRDATA ),

    //--------------SRAM SIGNAL--------------
            //in
    .iSRAM0_q(SRAM0_q),
    .iSRAM1_q(SRAM1_q),
    .iSRAM2_q(SRAM2_q),
    .iSRAM3_q(SRAM3_q),
    .iSRAM4_q(SRAM4_q),
    .iSRAM5_q(SRAM5_q),
    .iSRAM6_q(SRAM6_q),
    .iSRAM7_q(SRAM7_q),
            //out
    .oSRAM_CLK      (SRAM_CLK      ),
    .oSRAM_WEN_BANK0(SRAM_WEN_BANK0),
    .oSRAM_WEN_BANK1(SRAM_WEN_BANK1),
    .oSRAM_CSN_BANK0(SRAM_CSN_BANK0),
    .oSRAM_CSN_BANK1(SRAM_CSN_BANK1),
    .oSRAM_ADDR     (SRAM_ADDR),
    .oSRAM_WDATA    (SRAM_WDATA)  

  ); 
  /*————————————————————————————————————————————————————————————————————————*\
  /                        SRAM Core Instantiation                           \
  \*————————————————————————————————————————————————————————————————————————*/
  sram_core u_core(
    //-----------  From AHB  ------------
    .iHCLK          (HCLK   ),
    .iHRESETn       (HRESETn),  
    //--------- From Interface  ---------
    .iSRAM_WEN_BANK0 (SRAM_WEN_BANK0),
    .iSRAM_WEN_BANK1(SRAM_WEN_BANK1),
    .iSRAM_ADDR     (SRAM_ADDR     ),
    .iSRAM_CSN_BANK0(SRAM_CSN_BANK0),  
    .iSRAM_CSN_BANK1(SRAM_CSN_BANK1),  
    .iSRAM_WDATA    (SRAM_WDATA    ),
    //----------  To Interface  ---------
    .oSRAM0_q       (SRAM0_q),
    .oSRAM1_q       (SRAM1_q),
    .oSRAM2_q       (SRAM2_q),
    .oSRAM3_q       (SRAM3_q),
    .oSRAM4_q       (SRAM4_q),
    .oSRAM5_q       (SRAM5_q),
    .oSRAM6_q       (SRAM6_q),
    .oSRAM7_q       (SRAM7_q),
    //--------------  DFT  --------------
    .iBIST_en       (BIST_en  ),
    .oBIST_done     (BIST_done),
    .oBIST_fail     (BIST_fail)
  );

endmodule

  

1.4 sram_interface

其次是sram_interface模块,该模块是本项目中重点模块之一,负责寄存AHB总线控制信号、AHB总线地址信号,

然后根据AHB信号对SRAM存储体进行读写访问,

那么SRAM的接口信号是如何生成的呢?

CLK:直接采用AHB总线时钟HCLK作为存储器时钟

CSN:片选,当地址对应BANK0时,sram0、sram1、sram2、sram3被选中,而sram3~sram7则对应BANK1。具体来说,当总线地址HADDR的值在0x0000—0x7FFF之间时地址指向BANK0,而在0x8000—0xFFFF之间时地址指向BANK1

注:8K *  4 = 32KB 是2^15, 所以是 0111 1111 1111 1111 , 【15:0】,16h'7FFF

WEN:写使能,当HWRITE = 1时,总线对SRAM发起write操作,WEN将被拉高;
当HWRITE = 0时,总线对SRAM发起read操作,WEN将被拉低,以保证读地址的数据不会被改写

ADDR:地址。

根据AHB SRAM读写时序中所介绍,当执行SRAM写操作时,interface模块会将存储器地址通过寄存器打一拍,然后在下一个周期和写数据一起送到相应的存储器端口上;

而执行SRAM读操作时,我们为了实现单周期读写,会直接将地址送到存储器端口,
这样,就可以在下个时钟上升沿顺利地拿到存储器返回的读数据,并送回AHB总线。

以上两种情况分别对应生成了SRAM_ADDR_write[12:0]SRAM_ADDR_read[12:0]两个地址信号,我们通过iHWRITE_r判断当前周期的任务是read还是write,由此决定将哪个地址作真正被送往SRAM的地址端口:

    wire [12:0] SRAM_ADDR_write;

    wire [12:0] SRAM_ADDR_read;

    assign SRAM_ADDR_write = iHADDR_r[14:2]; // WRITE:addr have to wait a T , sent together 
                                             //with data to SRAM_CORE
  
    assign SRAM_ADDR_read  = iHADDR  [14:2]; // READ :addr send to MEM at once 
  
    assign oSRAM_ADDR = (iHWRITE_r == 1'b1) ? SRAM_ADDR_write : SRAM_ADDR_read;

WDATA:SRAM写数据,来自于总线上的HWDATA[31:0],sram_interface将32位的HWDATA按照下图顺序分配给8位SRAM,作为SRAM的写数据SRAM_WDATA

q:SRAM读数据,每个被选中的sram_bist将返回一个8位数据,sram_interface会将每个Bank中的4个单Byte读数据,组合为一个完整的32位读数据,返回到总线上的HRDATA

HWDATA与SRAM_WDATA的对应关系,
HRDATA与SRAM_q的对应关系,
如下图所示(以Bank0为例):

sram_interface模块的RTL代码如下:

module  sram_interface (
    //---------------AHB SIGNAL--------------
    //in
    input        iHCLK   ,
    input        iHRESETn,
    input        iHSEL   ,
    input        iHWRITE ,
    input [2:0]  iHBURST ,
    input [1:0]  iHTRANS ,
    input [2:0]  iHSIZE  ,
    input [31:0] iHWDATA ,
    input [15:0] iHADDR  ,
    //out 
    output [1:0]  oHRESP  ,
    output        oHREADY ,
    output [31:0] oHRDATA ,

    //--------------SRAM SIGNAL--------------
    //in
    input [7:0] iSRAM0_q,
    input [7:0] iSRAM1_q,
    input [7:0] iSRAM2_q,
    input [7:0] iSRAM3_q,
    input [7:0] iSRAM4_q,
    input [7:0] iSRAM5_q,
    input [7:0] iSRAM6_q,
    input [7:0] iSRAM7_q,
    //out
    output        oSRAM_CLK,
    output        oSRAM_WEN_BANK0,
    output        oSRAM_WEN_BANK1,
    output [3:0]  oSRAM_CSN_BANK0,
    output [3:0]  oSRAM_CSN_BANK1,
    output [12:0] oSRAM_ADDR,
    output [31:0] oSRAM_WDATA    
    
  ); 

  /*————————————————————————————————————————————————————————————————————————*\
  /                            AHB Signal Register                           \
  \*————————————————————————————————————————————————————————————————————————*/
  reg         iHSEL_r   ;
  reg         iHWRITE_r ;
  reg         iHWRITE_2r;
  reg  [2:0]  iHBURST_r ;
  reg  [1:0]  iHTRANS_r ;
  reg  [2:0]  iHSIZE_r  ;
  reg  [31:0] iHWDATA_r ;
  reg  [15:0] iHADDR_r  ;
  reg  [15:0] iHADDR_2r ;
  
  always@( posedge iHCLK)  begin
    if(!iHRESETn) begin
      iHSEL_r    <= 1'b0; 
      iHWRITE_r  <= 1'b0; 
      iHWRITE_2r <= 1'b0;
      iHBURST_r  <= 3'b0; 
      iHTRANS_r  <= 2'b0; 
      iHSIZE_r   <= 3'b0;
      iHWDATA_r  <= 32'b0; 
      iHADDR_r   <= 16'b0; 
      iHADDR_2r  <= 16'b0;
    end
    else begin
      iHSEL_r    <= iHSEL; 
      iHWRITE_r  <= iHWRITE; 
      iHWRITE_2r <= iHWRITE_r; 
      iHBURST_r  <= iHBURST; 
      iHTRANS_r  <= iHTRANS; 
      iHSIZE_r   <= iHSIZE;
      iHWDATA_r  <= iHWDATA; 
      iHADDR_r   <= iHADDR; 
      iHADDR_2r  <= iHADDR_r;
    end
  end

  /*————————————————————————————————————————————————————————————————————————*\
  /                    AHB BUS  →  Interface  →  SRAM Core                   \
  \*————————————————————————————————————————————————————————————————————————*/
  // SRAM Write Enable
  assign oSRAM_WEN_BANK0  = ( iHWRITE_r == 1'b1 &&  iHADDR_r[15] == 1'b0) ? 1'b1 : 1'b0;
  assign oSRAM_WEN_BANK1  = ( iHWRITE_r == 1'b1 &&  iHADDR_r[15] == 1'b1) ? 1'b1 : 1'b0;

  // SRAM Bank CSN            select for read ↓                    select for write ↓
  assign oSRAM_CSN_BANK0 =  ( iHADDR_r[15] == 1'b0    ||   (iHADDR[15] == 1'b0 && iHWRITE == 1'b0) ) ? 4'b1111 : 4'b0000;
  assign oSRAM_CSN_BANK1 =  ( iHADDR_r[15] == 1'b1    ||   (iHADDR[15] == 1'b1 && iHWRITE == 1'b0) ) ? 4'b1111 : 4'b0000;

  // SRAM Addr
  wire [12:0] SRAM_WRITE_ADDR;
  wire [12:0] SRAM_READ_ADDR; 
  assign SRAM_WRITE_ADDR      = iHADDR_r[14:2];   // WRITE:addr have to wait a T , sent together with data to SRAM_CORE
  assign SRAM_READ_ADDR       = iHADDR  [14:2];   // READ :addr send to MEM at once 
  assign oSRAM_ADDR = (iHWRITE_r == 1'b1) ? SRAM_WRITE_ADDR : SRAM_READ_ADDR;

  // SRAM Write Data
  assign oSRAM_WDATA = iHWDATA;

  /*————————————————————————————————————————————————————————————————————————*\
  /                    AHB BUS  ←  Interface  ←  SRAM Core                   \
  \*————————————————————————————————————————————————————————————————————————*/
  // response to AHB MASTER
  assign oHREADY = (iHSEL_r == 1'b1 && (iHWRITE_r == 1'b1 || iHWRITE_2r == 1'b0)) ? 1'b1 : 1'b0 ;
  assign oHRESP  = (iHSEL_r == 1'b1) ? 2'b00 : 2'b00; //OKAY = 2'b00
  
  // sram read data
  assign oHRDATA = (iHSEL_r == 1'b1 && iHWRITE_r == 1'b0 && iHADDR_r[15] == 1'b0) ? {iSRAM3_q, iSRAM2_q, iSRAM1_q, iSRAM0_q}: 
                   (iHSEL_r == 1'b1 && iHWRITE_r == 1'b0 && iHADDR_r[15] == 1'b1) ? {iSRAM7_q, iSRAM6_q, iSRAM5_q, iSRAM4_q}: 
                   32'bz;

endmodule

1.5 sram_core

接下来是顶层模块下的sram_core,主要内容是将sram_bist模块进行了8次例化,

因此,sram_core实际上是将这8个SRAM拼成了一个16k×32的SRAM,

sram_core的地址共15位,地址范围为0x0000-0xFFFFF,

其中,Bank0对应0x0000-0x7FFFF;Bank1对应0x8000~0xFFFFF,

而每个sram_bist端口上的地址为sram_core上的地址右移两位得到,共13位,地址范围为0x0000~0x1FFF,

除此之外,sram_core将每个8k×8 SRAM的内建自测试的输出结果BIST_done_x,BIST_fail_x(x=0~7)进行了逻辑与/或以得到整块sram_core存储体的DFT测试结果,

在执行SRAM数据读写功能的时候,sram_bist可以看做8k×8的单端口SRAM。

sram_core模块的RTL代码如下:

module sram_core (

  // From AHB 
  input iHCLK   , 
  input iHRESETn,

  // From sram_interface
  input        iSRAM_WEN_BANK0,
  input        iSRAM_WEN_BANK1,
  input [12:0] iSRAM_ADDR     ,
  input [3:0]  iSRAM_CSN_BANK0,  
  input [3:0]  iSRAM_CSN_BANK1,  
  input [31:0] iSRAM_WDATA    ,
  
  // To sram_interface 
  output [7:0] oSRAM0_q,
  output [7:0] oSRAM1_q,
  output [7:0] oSRAM2_q,
  output [7:0] oSRAM3_q,
  output [7:0] oSRAM4_q,
  output [7:0] oSRAM5_q,
  output [7:0] oSRAM6_q,
  output [7:0] oSRAM7_q,
  
  // BIST Signals
  input   iBIST_en,
  output  oBIST_done,
  output  oBIST_fail

);

  /*————————————————————————————————————————————————————————————————————————*\
  /                          BIST Ouput Logic                                \
  \*————————————————————————————————————————————————————————————————————————*/
  wire BIST_done_0;
  assign oBIST_done = BIST_done_0 && BIST_done_1 && BIST_done_2 && BIST_done_3
                   && BIST_done_4 && BIST_done_5 && BIST_done_6 && BIST_done_7; // done if every sram_bist dones

  assign oBIST_fail = BIST_done_0 || BIST_done_1 || BIST_done_2 || BIST_done_3
                   || BIST_done_4 || BIST_done_5 || BIST_done_6 || BIST_done_7; // fail if any sram_bist fails

  /*————————————————————————————————————————————————————————————————————————*\
  /                        BANK 0 Instantiation                              \
  \*————————————————————————————————————————————————————————————————————————*/
  sram_bist u_bank0_sram0 (
    // Function Mode IO
    .iSRAM_CLK  (iHCLK             ),
    .iSRAM_CSN  (iSRAM_CSN_BANK0[0]),
    .iSRAM_WEN  (iSRAM_WEN_BANK0   ),
    .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
    .iSRAM_WDATA(iSRAM_WDATA[7:0]  ), 
    .oSRAM_RDATA(oSRAM0_q          ),
    // Test Mode IO
    .iBIST_en   (iBIST_en          ),     
    .oBIST_done (BIST_done_0       ),
    .oBIST_fail (BIST_fail_0       )
  );
                                    sram_bist u_bank0_sram1 (
                                      // Function Mode IO
                                      .iSRAM_CLK  (iHCLK             ),
                                      .iSRAM_CSN  (iSRAM_CSN_BANK0[1]),
                                      .iSRAM_WEN  (iSRAM_WEN_BANK0   ),
                                      .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
                                      .iSRAM_WDATA(iSRAM_WDATA[15:8] ), 
                                      .oSRAM_RDATA(oSRAM1_q          ),
                                      // Test Mode IO
                                      .iBIST_en   (iBIST_en          ),     
                                      .oBIST_done (BIST_done_1       ),
                                      .oBIST_fail (BIST_fail_1       )
                                    );

  sram_bist u_bank0_sram2 (
    // Function Mode IO
    .iSRAM_CLK  (iHCLK             ),
    .iSRAM_CSN  (iSRAM_CSN_BANK0[2]),
    .iSRAM_WEN  (iSRAM_WEN_BANK0   ),
    .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
    .iSRAM_WDATA(iSRAM_WDATA[23:16]), 
    .oSRAM_RDATA(oSRAM2_q          ),
    // Test Mode IO
    .iBIST_en   (iBIST_en          ),     
    .oBIST_done (BIST_done_2       ),
    .oBIST_fail (BIST_fail_2       )
  );
                                    sram_bist u_bank0_sram3 (
                                      // Function Mode IO
                                      .iSRAM_CLK  (iHCLK             ),
                                      .iSRAM_CSN  (iSRAM_CSN_BANK0[3]),
                                      .iSRAM_WEN  (iSRAM_WEN_BANK0   ),
                                      .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
                                      .iSRAM_WDATA(iSRAM_WDATA[31:24]), 
                                      .oSRAM_RDATA(oSRAM3_q          ),
                                      // Test Mode IO
                                      .iBIST_en   (iBIST_en          ),     
                                      .oBIST_done (BIST_done_3       ),
                                      .oBIST_fail (BIST_fail_3       )
                                    );
  
  /*————————————————————————————————————————————————————————————————————————*\
  /                        BANK 1 Instantiation                              \
  \*————————————————————————————————————————————————————————————————————————*/
  sram_bist u_bank1_sram4 (
    // Function Mode IO
    .iSRAM_CLK  (iHCLK             ),
    .iSRAM_CSN  (iSRAM_CSN_BANK1[0]),
    .iSRAM_WEN  (iSRAM_WEN_BANK1   ),
    .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
    .iSRAM_WDATA(iSRAM_WDATA[7:0]  ), 
    .oSRAM_RDATA(oSRAM4_q          ),
    // Test Mode IO
    .iBIST_en   (iBIST_en          ),     
    .oBIST_done (BIST_done_4       ),
    .oBIST_fail (BIST_fail_4       )
  );
                                    sram_bist u_bank1_sram5 (
                                      // Function Mode IO
                                      .iSRAM_CLK  (iHCLK             ),
                                      .iSRAM_CSN  (iSRAM_CSN_BANK1[1]),
                                      .iSRAM_WEN  (iSRAM_WEN_BANK1   ),
                                      .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
                                      .iSRAM_WDATA(iSRAM_WDATA[15:8] ), 
                                      .oSRAM_RDATA(oSRAM5_q          ),
                                      // Test Mode IO
                                      .iBIST_en   (iBIST_en          ),     
                                      .oBIST_done (BIST_done_5       ),
                                      .oBIST_fail (BIST_fail_5       )
                                    );

  sram_bist u_bank1_sram6 (
    // Function Mode IO
    .iSRAM_CLK  (iHCLK             ),
    .iSRAM_CSN  (iSRAM_CSN_BANK1[2]),
    .iSRAM_WEN  (iSRAM_WEN_BANK1   ),
    .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
    .iSRAM_WDATA(iSRAM_WDATA[23:16]), 
    .oSRAM_RDATA(oSRAM6_q          ),
    // Test Mode IO
    .iBIST_en   (iBIST_en          ),     
    .oBIST_done (BIST_done_6       ),
    .oBIST_fail (BIST_fail_6       )
  );
                                    sram_bist u_bank1_sram7 (
                                      // Function Mode IO
                                      .iSRAM_CLK  (iHCLK             ),
                                      .iSRAM_CSN  (iSRAM_CSN_BANK1[3]),
                                      .iSRAM_WEN  (iSRAM_WEN_BANK1   ),
                                      .iSRAM_ADDR (iSRAM_ADDR        ), //13 bits SRAM ADDR
                                      .iSRAM_WDATA(iSRAM_WDATA[31:24]), 
                                      .oSRAM_RDATA(oSRAM7_q          ),
                                      // Test Mode IO
                                      .iBIST_en   (iBIST_en          ),     
                                      .oBIST_done (BIST_done_7       ),
                                      .oBIST_fail (BIST_fail_7       )
                                    );

endmodule

1.6 SRAM读写功能的仿真验证

在完成RTL设计后,我们编写了Testbench,
并在Vivado平台上进行了简单的读写仿真验证,波形如下:

分析一下Testbench具体对SRAM发起了什么操作:

首先,T1-T7进行了六次写操作,将6个数据依次写入SRAM的0x0000,0x0004,0x0008,0x8000,0x8004,0x8008六个地址当中

其中前三个地址对应Bank0,后三个地址对应Bank1,

因此在T2-T4期间 SRAM_CSN_BANK0 和 SRAM_WEN_BANK0 被拉高,

在T5-T7期间 SRAM_CSN_BANK1 和 SRAM_WEN_BANK1 被拉高,

从上图中可以看出,T7除了是Data 6的写数据周期,也是Data 1 读地址周期,

但是由于SRAM端口上,该周期需要执行写Data 6的操作。

于是发生了地址冲突,无法在该周期同时进行读Data 1

因此,在T8并没有返回Data 1的读数据,HREADY被拉低,

随后,在T9-T14,总线上HRDATA依次拿到了六个SRAM读数据,读出的data与T1-T7写入的data完全一致,证明了以上SRAM控制器的设计逻辑是正确的

1.7 支持半字/字节读写

细心的读者们可能发现上述设计仅考虑了读SRAM按字进行读写的情况,

也就是每次读写都是32位的。

实际上,AHB协议同样支持我们以16位/8位的方式对SRAM进行访问,由HSIZE[2:0]控制,具体对应关系如下:

HSIZE[2:0] = 3'b000:按BYTE读写
HSIZE[2:0] = 3'b001:按Half Word读写
HSIZE[2:0] = 3'b010:按Word读写

支持半字/字节读写使得CSN信号的生成变得更为复杂,

基于上述设计,读者们可以思考一下如何实现该功能,

以下给出参考设计:

// SRAM Bank CSN
  wire [3:0] SRAM_CSN_BANK0_write;
  wire [3:0] SRAM_CSN_BANK0_read ;
  wire [3:0] SRAM_CSN_BANK1_write;
  wire [3:0] SRAM_CSN_BANK1_read ;

  always @(*) begin
    case( {iHADDR[15],iHSIZE} )
      //选中BANK0,HSIZE = Word
      4'b0010:begin
        SRAM_CSN_BANK0_read <= 4'b1111;
        SRAM_CSN_BANK0_read <= 4'b0000;
      end
      //选中BANK1,HSIZE = Word
      4'b1010:begin
        SRAM_CSN_BANK0_read <= 4'b0000;
        SRAM_CSN_BANK1_read <= 4'b1111;
      end
      //选中BANK0,HSIZE = Half Word
      4'b0001:begin
        if(iHADDR[1]) begin
          SRAM_CSN_BANK0_read <= 4'b1100;
          SRAM_CSN_BANK1_read <= 4'b0000;
        end else begin
          SRAM_CSN_BANK0_read <= 4'b0011;
          SRAM_CSN_BANK1_read <= 4'b0000;
        end   
      end
      //选中BANK1,HSIZE = Half Word
      4'b1001:begin
        if(iHADDR[1]) begin
          SRAM_CSN_BANK0_read <= 4'b0000;
          SRAM_CSN_BANK1_read <= 4'b1100;
        end else begin
          SRAM_CSN_BANK0_read <= 4'b0000;
          SRAM_CSN_BANK1_read <= 4'b0011;
        end   
      end
      //选中BANK0,HSIZE = BYTE
      4'b0000:begin
        case (iHADDR[1:0])
          2'b00: begin
            SRAM_CSN_BANK0_read <= 4'b0001;
            SRAM_CSN_BANK1_read <= 4'b0000;
          end
          2'b01: begin
            SRAM_CSN_BANK0_read <= 4'b0010;
            SRAM_CSN_BANK1_read <= 4'b0000;
          end
          2'b10: begin
            SRAM_CSN_BANK0_read <= 4'b0100;
            SRAM_CSN_BANK1_read <= 4'b0000;
          end
          2'b11: begin
            SRAM_CSN_BANK0_read <= 4'b1000;
            SRAM_CSN_BANK1_read <= 4'b0000;
          end
        endcase  
      end
      //选中BANK1,HSIZE = BYTE
      4'b1000:begin
        case (iHADDR[1:0])
          2'b00: begin
            SRAM_CSN_BANK0_read <= 4'b0000;
            SRAM_CSN_BANK1_read <= 4'b0001;
          end
          2'b01: begin
            SRAM_CSN_BANK0_read <= 4'b0000;
            SRAM_CSN_BANK1_read <= 4'b0010;
          end
          2'b10: begin
            SRAM_CSN_BANK0_read <= 4'b0000;
            SRAM_CSN_BANK1_read <= 4'b0100;
          end
          2'b11: begin
            SRAM_CSN_BANK0_read <= 4'b0000;
            SRAM_CSN_BANK1_read <= 4'b1000;
          end
        endcase  
      end
    endcase
  end

  always @(*) begin
    case( {iHADDR_r[15],iHSIZE_r} )
      //选中BANK0,HSIZE = Word
      4'b0010:begin
        SRAM_CSN_BANK0_write <= 4'b1111;
        SRAM_CSN_BANK0_write <= 4'b0000;
      end
      //选中BANK1,HSIZE = Word
      4'b1010:begin
        SRAM_CSN_BANK0_write <= 4'b0000;
        SRAM_CSN_BANK1_write <= 4'b1111;
      end
      //选中BANK0,HSIZE = Half Word
      4'b0001:begin
        if(iHADDR_r[1]) begin
          SRAM_CSN_BANK0_write <= 4'b1100;
          SRAM_CSN_BANK1_write <= 4'b0000;
        end else begin
          SRAM_CSN_BANK0_write <= 4'b0011;
          SRAM_CSN_BANK1_write <= 4'b0000;
        end   
      end
      //选中BANK1,HSIZE = Half Word
      4'b1001:begin
        if(iHADDR_r[1]) begin
          SRAM_CSN_BANK0_write <= 4'b0000;
          SRAM_CSN_BANK1_write <= 4'b1100;
        end else begin
          SRAM_CSN_BANK0_write <= 4'b0000;
          SRAM_CSN_BANK1_write <= 4'b0011;
        end   
      end
      //选中BANK0,HSIZE = BYTE
      4'b0000:begin
        case (iHADDR_r[1:0])
          2'b00: begin
            SRAM_CSN_BANK0_write <= 4'b0001;
            SRAM_CSN_BANK1_write <= 4'b0000;
          end
          2'b01: begin
            SRAM_CSN_BANK0_write <= 4'b0010;
            SRAM_CSN_BANK1_write <= 4'b0000;
          end
          2'b10: begin
            SRAM_CSN_BANK0_write <= 4'b0100;
            SRAM_CSN_BANK1_write <= 4'b0000;
          end
          2'b11: begin
            SRAM_CSN_BANK0_write <= 4'b1000;
            SRAM_CSN_BANK1_write <= 4'b0000;
          end
        endcase  
      end
      //选中BANK1,HSIZE = BYTE
      4'b1000:begin
        case (iHADDR_r[1:0])
          2'b00: begin
            SRAM_CSN_BANK0_write <= 4'b0000;
            SRAM_CSN_BANK1_write <= 4'b0001;
          end
          2'b01: begin
            SRAM_CSN_BANK0_write <= 4'b0000;
            SRAM_CSN_BANK1_write <= 4'b0010;
          end
          2'b10: begin
            SRAM_CSN_BANK0_write <= 4'b0000;
            SRAM_CSN_BANK1_write <= 4'b0100;
          end
          2'b11: begin
            SRAM_CSN_BANK0_write <= 4'b0000;
            SRAM_CSN_BANK1_write <= 4'b1000;
          end
        endcase  
      end
    endcase
  end

  assign oSRAM_CSN_BANK0 = (iHWRITE_r == 1'b1) ? SRAM_CSN_BANK0_write : SRAM_CSN_BANK0_read;
  assign oSRAM_CSN_BANK1 = (iHWRITE_r == 1'b1) ? SRAM_CSN_BANK1_write : SRAM_CSN_BANK1_read;

可以看到,CSN信号的生成和ADDR类似,需要考虑因为读/写操作时序不同而带来的两种情况,

分别生成了SRAM_CSN_BANK0_write[3:0]SRAM_CSN_BANK0_read[3:0](BANK1同理),

最后通过iHWRITE_r来对两者进行选择

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
随着计算机系统的发展,内存的速度成为了系统性能的瓶颈之一。为了解决这一问题,SRAM(静态随机存储器)成为了一种常用的内存类型,而AHB(高级高速总线)是一种快速的总线协议。基于AHB的SRAM控制器设计与优化可以显著提高系统性能。 SRAM控制器的任务是管理SRAM芯片的读写操作。基于AHB的SRAM控制器可以通过总线传输协议来与其他设备进行通信,从而实现高速数据传输。其中,读取操作需要首先将地址和请求发送到SRAM中,然后等待SRAM返回数据。在这个过程中,控制器需要使用适当的寻址算法来定位存储位置,并根据SRAM的工作原理优化读取周期。 在设计和优化基于AHB的SRAM控制器时,需要考虑以下几个关键因素: 第一,SRAM内部结构的特点。SRAM的读取速度非常快,但它的存储密度相对较低,且需要专用硬件实现。因此,控制器需要在寻址和数据访问方面操作SRAM的内部结构。 第二,总线带宽。为了充分利用AHB总线的高速性能,控制器应使用适当的调度算法和数据传输协议,以充分利用总线带宽。 第三,芯片时序。SRAM芯片的时序和时延对读写操作的速度有重要影响。控制器应根据SRAM芯片的特性和内部结构来优化时序和时延参数。 综上所述,基于AHB的SRAM控制器设计和优化是一项复杂且重要的任务,它需要结合SRAM芯片的特性、总线带宽和时序参数进行考虑,以充分利用系统性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值