SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

这篇具有很好参考价值的文章主要介绍了SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

绪论

本项目用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两个子模块
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

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总线时序

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

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

SRAM接口时序
写时序
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

读时序
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

读时序与写时序的区别主要在于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的接口信号是如何生成的呢?

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

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

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

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为例):

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

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平台上进行了简单的读写仿真验证,波形如下:
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

分析一下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 被拉高,
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

从上图中可以看出,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来对两者进行选择

2. 基于March C-算法的DFT功能

2.1 BIST架构

在设计中,SRAM读写模式和DFT模式的选择通过2选1选择器实现,

当DFT模式的使能信号BIST_en = 1时,来自于AHB sram_interface的所有信号(图中红色数据)将被忽略,

SRAM端口上的输入信号将全部来自于sram_bist内部的BIST控制电路生成的Pattern(图中蓝色数据),

对于March C-算法,BIST控制电路会对SRAM每个地址进行“写 → 读 → 比较”的操作,

若所有的读数据结果与写入的数据是一致的,BIST_done最终将被拉高,说明该电路生产过程中没有出现生产故障,

反之,如果比较发现有错误,BIST_fail最终将被拉高,该块芯片将被报废,

在模块内加入DFT测试电路,虽然会增加系统的数字面积,但同时也极大地降低了产品在测试环节所花费的开销成本

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

2.2 SRAM常见故障

首先,介绍一些常见的存储器(比如SRAM)故障模型:

固定型故障(Stuck-At Faults,SAF):
存储单元中的值固定为0/1(简记为SA0/SA1),无法发生改变。固定型故障可以通过对待测单元写入0再读出0,然后写入1再读出1来进行检测。

跳变故障(Transition Faults,TF):
存储单元中的值无法从0跳变到1(简记为TF(0→1)),或者从1跳变到0(简记为TF(1→0)),需要通过写入1到0的跳变再读出0,然后写入0到1的跳变再读出1来进行检测

耦合故障(Coupling Faults,CF):
一个存储单元的值发生改变,导致另一个存储单元的值发生改变,可以通过先升序对所有存储单元进行写读操作,然后再降序对所有存储单元进行写读操作的方法进行故障检测

2.3 March C-算法

March C算法是目前应用最为广泛的MBIST(Memory Built-In-Self-Test,存储器内建自测试)算法,

March C算法对上文提到的SAF故障,TF故障,CF故障的故障覆盖率均达到100%,

March C算法的具体检测流程如下:

  1. 从最低地址开始,在整个存储器中依次写入0(升序)

  2. 读出最低地址,结果应为0,然后把1写入该存储单元。完成后地址+1,再次执行该操作,直至对整个存储器执行该操作(升序)

  3. 读出最高地址,结果应为1,然后把0写入该存储单元,再次读该单元,结果应为0。完成后地址-1,再次执行该操作,直至对整个存储器执行该操作(降序)

  4. 读出最高地址,结果应为0,然后把1写入该存储单元。完成后地址-1,再次执行该操作,直至对整个存储器执行该操作(降序)

  5. 读出最低地址,结果应为1,然后把0写入该存储单元,再次读该单元,结果应为0。完成后地址+1,再次执行该操作,直至对整个存储器执行该操作(升序)

由于步骤4,步骤5中,加粗字体所描述的操作实际上是重复的,

因此后来有了改进的March C-算法, 将步骤3中的加粗部分删除,如下:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

图中,March C-所执行的全套操作被分成了5个部分,也就是MARCH_0~MARCH_ 4

这也是本项目的BIST功能在RTL设计中,March C-状态机所用到的5个状态的名称

2.4 Verilog实现March C-算法的BIST

本项目中,内建自测试逻辑电路位于每个sram_bist模块中,

BIST_en作为顶层模块的BIST使能信号,被直接接到每块bist的BIST_en输入使能端口,

依据March C-算法发起的SRAM读写操作,是由有限状态机控制的,该状态机示意图如下:
SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

下面让我们来看看sram_bist模块的RTL代码:

module sram_bist #(

    //---------------    MARCH C-  --------------------//
    //   STATE            ACTION           DIRECTION
    // MARCH 0            write 0              ↑  
    // MARCH 1         read 0, write 1         ↑
    // MARCH 2         read 1, write 0         ↓
    // MARCH 3         read 0, write 1         ↓
    // MARCH 4         read 1, write 0         ↑        
    //-------------------------------------------------//
    
            // TEST_state parameters //            
        parameter MARCH_0 = 3'b000,        // 0
        parameter MARCH_1 = 3'b001,        // 1
        parameter MARCH_2 = 3'b010,        // 2
        parameter MARCH_3 = 3'b011,        // 3
        parameter MARCH_4 = 3'b100,        // 4
        parameter MARCH_finished = 3'b101, // 5

            // TEST_action parameters //
              
        parameter WRITE_0 = 2'b00,
        parameter READ_0  = 2'b01,
        parameter WRITE_1 = 2'b10,
        parameter READ_1  = 2'b11,

            // TEST_compare_result parameters //
                      
        parameter COMPARE_RIGHT  = 1'b1,
        parameter COMPARE_ERROR  = 1'b0

)(

    // Function Mode IO
    input         iSRAM_CLK  ,
    input         iSRAM_CSN  ,
    input         iSRAM_WEN  ,
    input  [12:0] iSRAM_ADDR ,
    input  [7:0]  iSRAM_WDATA,    
    output [7:0]  oSRAM_RDATA,

    // Test Mode IO
    input  iBIST_en   ,     
    output oBIST_done ,
    output oBIST_fail
);

    /*————————————————————————————————————————————————————————————————————————*\
    /                                                                          \
    /                     SRAM Normal Function Mode                            \
    /                                                                          \
    \*————————————————————————————————————————————————————————————————————————*/

    // wire connected to sram's port
    wire SRAM_CLK  ;  
    wire SRAM_CSN  ;  
    wire SRAM_WEN  ;  
    wire [12:0] SRAM_ADDR ;  
    wire [7:0]  SRAM_WDATA;  
    wire [7:0]  SRAM_RDATA;  
    
    // TEST-FUN MUX
    assign SRAM_ADDR  = (iBIST_en == 1'b1) ? TEST_ADDR   :
                        (iBIST_en == 1'b0) ? iSRAM_ADDR  : 13'bz;
    
    assign SRAM_CSN   = (iBIST_en == 1'b1) ? 1'b1        :
                        (iBIST_en == 1'b0) ? iSRAM_CSN   : 1'bz;  
    
    
    assign SRAM_WDATA = (iBIST_en == 1'b1) ? TEST_WDATA     :
                        (iBIST_en == 1'b0) ? iSRAM_WDATA    : 8'bz;  

    assign SRAM_WEN   = (iBIST_en == 1'b1) ? TEST_SRAM_WEN  :
                        (iBIST_en == 1'b0) ? iSRAM_WEN      : 1'bz;                    
    
    assign oSRAM_RDATA = SRAM_RDATA;
       
    // IP instantiation
    RAM_8K_8 u_bt_sram (
        .clka  (iSRAM_CLK   ),// HCLK → SRAM_CLK
        .ena   (SRAM_CSN    ),// csn → ena
        .wea   (SRAM_WEN    ),// 
        .addra (SRAM_ADDR   ),// unite addr
        .dina  (SRAM_WDATA  ),// input data
        .douta (SRAM_RDATA  ) // output data
    );
    
    /*————————————————————————————————————————————————————————————————————————*\
    /                                                                          \
    /                   BIST (Build-in Self Test)                            \
    /                                                                          \
    \*————————————————————————————————————————————————————————————————————————*/

    // BIST CLOCK Generation   
    wire   BIST_CLK;
    assign BIST_CLK = ( iBIST_en == 1'b1) ? iSRAM_CLK : 1'b0;

    // BIST RESET Generation
    reg  iBIST_en_r;
    reg  iBIST_en_2r;
    wire TEST_RESET;    
    always @( posedge BIST_CLK) begin
        if(iBIST_en && iBIST_en_r) begin
            iBIST_en_r  <= 1'b1;
            iBIST_en_2r <= 1'b1;
        end
        else if ( iBIST_en ) begin
            iBIST_en_r  <= 1'b1;
            iBIST_en_2r <= 1'b0;
        end
        else begin
            iBIST_en_r  <= 1'b0;
            iBIST_en_2r <= 1'b0;
        end
    end
    assign TEST_RESET = iBIST_en_2r ^ iBIST_en;


    // BIST Controller (March C)
    reg        TEST_flag_finish;
    reg [2:0]  TEST_state;
    reg [1:0]  TEST_action;
    reg        TEST_SRAM_WEN;
    reg [31:0] TEST_ADDR;
    reg [7:0]  TEST_WDATA;    
    
    always@( posedge BIST_CLK ) begin 
    if ( TEST_RESET ) begin                                    //Synchronous Reset
        TEST_flag_finish <= 1'b0;
        TEST_state       <= MARCH_0;
        TEST_action      <= WRITE_0;
        TEST_SRAM_WEN    <= 1'b1;
        TEST_ADDR        <= 13'h0000;
        TEST_WDATA       <= 8'b0000_0000;
    end
    else begin       
        case ( TEST_state )
            //---------------    MARCH 0  ↑   -----------------//
            MARCH_0 : begin                  
                if ( TEST_ADDR == 13'h1FFF ) begin
                    TEST_state       <= MARCH_1;               // 
                    TEST_action      <= READ_0;                // 
                    TEST_SRAM_WEN    <= 1'b0;                  // jump to MARCH_1 to read 0
                    TEST_ADDR        <= 13'h0000;              // 
                    TEST_WDATA       <= 8'bz;                  // 
                end 
                else if ( TEST_action == WRITE_0 ) begin
                    TEST_state       <= TEST_state;
                    TEST_action      <= TEST_action;
                    TEST_SRAM_WEN    <= 1'b1;
                    TEST_ADDR        <= TEST_ADDR + 1'b1;     // addr ++   
                    TEST_WDATA       <= 8'b0000_0000;         // write 0
                end          
            end 

            //---------------    MARCH 1  ↑   ----------------//
            MARCH_1 : begin
                if ( TEST_action == WRITE_1 && TEST_ADDR == 13'h1FFF ) begin
                    TEST_state       <= MARCH_2;              // 
                    TEST_action      <= READ_1;               //
                    TEST_SRAM_WEN    <= 1'b0;                 // jump to MARCH_2 to read 1
                    TEST_ADDR        <= 13'h1FFF;             // 
                    TEST_WDATA       <= 8'bz;                 //
                end 
                else if ( TEST_action == READ_0 ) begin
                    TEST_state       <= TEST_state;
                    TEST_action      <= WRITE_1;              // write 1 in next clk
                    TEST_SRAM_WEN    <= 1'b1;                 // write 1 in next clk
                    TEST_ADDR        <= TEST_ADDR;            // addr kept for write 1  
                    TEST_WDATA       <= 8'b1111_1111;         // write 1 in next clk
                end 
                else if ( TEST_action == WRITE_1 )begin
                    TEST_state       <= TEST_state;
                    TEST_action      <= READ_0;               // read 0 in next clk 
                    TEST_SRAM_WEN    <= 1'b0;                 // read 0 in next clk 
                    TEST_ADDR        <= TEST_ADDR + 1'b1;     // addr++ 
                    TEST_WDATA       <= 8'bz;                 // read 0 in next clk 
                end
            end

            //---------------    MARCH 2  ↓   ----------------//
            MARCH_2 : begin
                if ( TEST_action == WRITE_0 && TEST_ADDR == 13'h0000 ) begin
                    TEST_state       <= MARCH_3;              //                     
                    TEST_action      <= READ_0;               //                             
                    TEST_SRAM_WEN    <= 1'b0;                 // jump to MARCH_3 to read 0  
                    TEST_ADDR        <= 13'h1FFF;             //      
                    TEST_WDATA       <= 8'bz;                 //                              
                end                                            
                else if ( TEST_action == READ_1 ) begin             
                    TEST_state       <= TEST_state;               
                    TEST_action      <= WRITE_0;              // write 0 in next clk               
                    TEST_SRAM_WEN    <= 1'b1;                 // write 0 in next clk        
                    TEST_ADDR        <= TEST_ADDR;            // addr kept for write 0         
                    TEST_WDATA       <= 8'b0000_0000;         // write 0 in next clk                                  
                end                                             
                else if ( TEST_action == WRITE_0 )begin       //      
                    TEST_state       <= TEST_state;           //    
                    TEST_action      <= READ_1;               // read 1 in next clk             
                    TEST_SRAM_WEN    <= 1'b0;                 // read 1 in next clk   
                    TEST_ADDR        <= TEST_ADDR - 1'b1;     // addr-- 
                    TEST_WDATA       <= 8'bz;                 // read 1 in next clk
                end    
            end

            //---------------    MARCH 3  ↓   ----------------//
            MARCH_3 : begin
                if ( TEST_action == WRITE_1 && TEST_ADDR == 13'h0000 ) begin
                    TEST_state       <= MARCH_4;
                    TEST_action      <= READ_1;               // jump to MARCH_4 to read 1
                    TEST_SRAM_WEN    <= 1'b0;
                    TEST_ADDR        <= 13'h0000;
                    TEST_WDATA       <= 8'bz;
                end 
                else if ( TEST_action == READ_0 ) begin
                    TEST_state       <= TEST_state;           // write 1 in next clk
                    TEST_action      <= WRITE_1;              // write 1 in next clk
                    TEST_SRAM_WEN    <= 1'b1;                 // write 1 in next clk
                    TEST_ADDR        <= TEST_ADDR;            // addr kept for write 1   
                    TEST_WDATA       <= 8'b1111_1111;         // write 1 in next clk
                end 
                else if ( TEST_action == WRITE_1 )begin
                    TEST_state       <= TEST_state;           // read 0 in next clk
                    TEST_action      <= READ_0;               // read 0 in next clk
                    TEST_SRAM_WEN    <= 1'b0;                 // read 0 in next clk
                    TEST_ADDR        <= TEST_ADDR - 1'b1;     // addr-- 
                    TEST_WDATA       <= 8'bz;                 // read 0 in next clk
                end
            end

            //---------------    MARCH 4  ↑   ----------------//
            MARCH_4 : begin
                if ( TEST_action == READ_0 && TEST_ADDR == 13'h1FFF ) begin
                    TEST_flag_finish <= 1'b1;
                    TEST_state       <= MARCH_finished;
                    TEST_action      <= 2'bz;          
                    TEST_SRAM_WEN    <= 1'bz;
                    TEST_ADDR        <= 13'hz;
                    TEST_WDATA       <= 8'bz;
                end 
                else if ( TEST_action == READ_1 ) begin
                    TEST_state       <= TEST_state;     
                    TEST_action      <= WRITE_0;              // write 0 in next clk
                    TEST_SRAM_WEN    <= 1'b1;                 // write 0 in next clk
                    TEST_ADDR        <= TEST_ADDR;            // addr kept for write 0   
                    TEST_WDATA       <= 8'b0000_0000;         // write 0 in next clk
                end 
                else if ( TEST_action == WRITE_0 )begin
                    TEST_state       <= TEST_state; 
                    TEST_action      <= READ_0;               // read 0 in next clk
                    TEST_SRAM_WEN    <= 1'b0;                 // read 0 in next clk
                    TEST_ADDR        <= TEST_ADDR;            // addr kept for read 0 
                    TEST_WDATA       <= 8'bz;                 // read 0 in next clk
                end
                else if ( TEST_action == READ_0 )begin
                    TEST_state       <= TEST_state;
                    TEST_action      <= READ_1;               // read 1 in next clk
                    TEST_SRAM_WEN    <= 1'b0;                 // read 1 in next clk
                    TEST_ADDR        <= TEST_ADDR + 1'b1;     // addr++ 
                    TEST_WDATA       <= 8'bz;                 // read 1 in next clk
                end
            end
            MARCH_finished : begin
                TEST_flag_finish <= 1'b1;
                TEST_state       <= TEST_state;
            end
            default: begin
                TEST_flag_finish <= 1'b0;
                TEST_state       <= MARCH_0;
                TEST_action      <= WRITE_0;
                TEST_SRAM_WEN    <= 1'b1;
                TEST_ADDR        <= 13'h0000;
                TEST_WDATA       <= 8'b0000_0000;
            end 
        endcase        
    end
    end

    // Compare SRAM_RDATA with Ideal Result 
    reg TEST_compare_result;

    always@( posedge BIST_CLK ) begin     

        // Reset the Comparsion Result
        if ( TEST_RESET ) begin
            TEST_compare_result <= COMPARE_RIGHT; // COMPARE_RIGHT = 1'b1
        end

        // Read 0 in March_1 
        else if ( TEST_state == MARCH_1 && TEST_action == WRITE_1 ) begin
            if ( SRAM_RDATA == 8'b0000_0000)   TEST_compare_result <= TEST_compare_result && 1'b1; 
            else                               TEST_compare_result <= TEST_compare_result && 1'b0; 
        end

        // Read 1 in March_2 
        else if ( TEST_state == MARCH_2 && TEST_action == WRITE_0 ) begin
            if ( SRAM_RDATA == 8'b1111_1111)   TEST_compare_result <= TEST_compare_result && 1'b1;
            else                               TEST_compare_result <= TEST_compare_result && 1'b0; 
        end

        // Read 0 in March_3 
        else if ( TEST_state == MARCH_3 && TEST_action == WRITE_1 ) begin
            if ( SRAM_RDATA == 8'b0000_0000)   TEST_compare_result <= TEST_compare_result && 1'b1; 
            else                               TEST_compare_result <= TEST_compare_result && 1'b0; 
        end

        // Read 1 in March_4 
        else if ( TEST_state == MARCH_4 && TEST_action == WRITE_0 ) begin
            if ( SRAM_RDATA == 8'b1111_1111)   TEST_compare_result <= TEST_compare_result && 1'b1; 
            else                               TEST_compare_result <= TEST_compare_result && 1'b0; 
        end

        // Read 0 in March_4    
        else if ( TEST_state == MARCH_4 && TEST_action == READ_1 && TEST_ADDR != 13'h0000) begin
            if ( SRAM_RDATA == 8'b0000_0000)   TEST_compare_result <= TEST_compare_result && 1'b1; 
            else                               TEST_compare_result <= TEST_compare_result && 1'b0; 
        end

        else begin
            TEST_compare_result <= TEST_compare_result ; 
        end

    end

    assign oBIST_done = ( TEST_flag_finish && TEST_compare_result  ) ? 1'b1 : 1'b0; 
    assign oBIST_fail = ( TEST_flag_finish && !TEST_compare_result ) ? 1'b1 : 1'b0; 

endmodule

2.4 BIST的仿真验证

最后,BIST功能同样在Vivado平台上进行了逻辑仿真,

整个BIST过程共BIST_en = 1开始,一共花费了约1600μs完成,

最后BIST_done被拉高,这是必然的 ,因为逻辑仿真中不涉及实际芯片制造中的各种故障,

我们先从宏观上看仿真波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

从波形中可以看到,MARCH_0状态持续时间最短,这是因为MARCH_0对于每个地址操作仅为WRITE_0,

而MARCH_1,MARCH_2,MARCH_3状态分别要进行读和写两个操作,因此每个状态下的总周期数均为MARCH_0状态的2倍,

MARCH_4则更长,共READ_1,WRITE_0,READ_0三个操作,总周期数为MARCH_0状态的3倍,

接下来看看MARCH_0和MARCH_1状态之间的转换波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

其他几段状态转化处的波形也是同理,在此不在一一标注解释了,具体如下:

MARCH_1状态和MARCH_2状态之间的波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

MARCH_2状态和MARCH_3状态之间的波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

MARCH_3状态和MARCH_4状态之间的波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

MARCH_4状态最后一段波形:

SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现

完成MARCH_4后,BIST_done被拉高,代表BIST结束。

以上,就是关于AHB-SRAN的BIST模式下的逻辑仿真解读。

最后附上两种工作模式下的Testbench:

点击查看代码
`timescale 1ns / 1ps

module sram_tb #(
    //HRESP
    parameter    OKAY  = 2'b00   ,
    parameter    ERROR = 2'b01   ,
    parameter    SPLIT = 2'b10   ,
    parameter    RETRY = 2'b11   ,
    //HTRANS
    parameter    IDLE   = 2'b00  ,
    parameter    BUSY   = 2'b01  ,
    parameter    SEQ    = 2'b10  ,
    parameter    NONSEQ = 2'b11  ,
    //HSIZE
    parameter    BYTE  = 3'b000  ,
    parameter    WORD  = 3'b001  ,
    parameter    DWORD = 3'b010  ,
    //HBURST
    parameter    SINGLE = 3'b000 ,
    parameter    INCR   = 3'b001 ,
    parameter    WRAP4  = 3'b010 ,
    parameter    INCR4  = 3'b011 ,
    parameter    WARP8  = 3'b100 ,
    parameter    INCR8  = 3'b101 ,
    parameter    WARP16 = 3'b110 ,
    parameter    INCR16 = 3'b111 

)();

    // input output declaration   
    reg HCLK;
    reg HRESETn;
    reg           HSEL  ;
    reg  [1:0]    HTRANS;    
    reg  [2:0]    HBURST;
    reg  [2:0]    HSIZE ;
    reg           HWRITE;
    reg  [15:0]   HADDR ;
    reg  [31:0]   HWDATA;
    wire          HREADY;
    wire [1:0]    HRESP ;
    wire [31:0]   HRDATA;
    // BIST IO
    reg           BIST_en;
    wire          BIST_done;
    wire          BIST_fail;

    // top module instantion
    sram_top u_test(
        .HCLK   (HCLK   ), 
        .HRESETn(HRESETn), 
        .HSEL   (HSEL   ),
        .HTRANS (HTRANS ),    
        .HBURST (HBURST ),
        .HSIZE  (HSIZE  ),
        .HWRITE (HWRITE ),
        .HADDR  (HADDR  ),
        .HWDATA (HWDATA ),
        .HREADY (HREADY ),
        .HRESP  (HRESP  ),
        .HRDATA (HRDATA ),
        .BIST_en  (BIST_en  ),     
        .BIST_done(BIST_done),
        .BIST_fail(BIST_fail)
    );
 
    // Excitation Vector Generation
    initial begin
    	forever #10 HCLK = ~HCLK;	//50Mhz
    end
    
    initial begin
        HCLK  = 0;
        HRESETn = 0;


    // Choose BIST Mode/ FUNC Mode to run simulation 
    
    /*————————————————————————————————————————————————————————————————————————*\
    /                        Testbench for BIST Mode                           \  
    \*————————————————————————————————————————————————————————————————————————*/        
        
/*
        BIST_en = 1;
    end
*/  

    /*————————————————————————————————————————————————————————————————————————*\
    /                        Testbench for FUNC Mode                           \
    \*————————————————————————————————————————————————————————————————————————*/      
        
        HRESETn = 0;
        BIST_en = 0;

        HSEL    = 0;
        HTRANS  = 3'b111;    
        HBURST  = 2'b11;
        HSIZE   = 3'b111;
        HWRITE  = 1'b0;
        HADDR   = 16'h0000; //HADDR[1:0] have to be 2'b00 ( 4n )
        HWDATA  = 32'h0;

        #65  HRESETn = 1;
        //-------------------------WRITE MEM---------------
        #20  
        // write control0 
        HSEL    = 1;
        HTRANS  = NONSEQ;    
        HBURST  = SINGLE;
        HSIZE   = WORD;
        HWRITE  = 1'b1;
        HADDR   = 16'h0000;

        #20  
        // data0 → mem(0x0000)     
        HWDATA  = 32'h11223344;
        // write control 1        
        HWRITE  = 1'b1;
        HADDR   = 16'h0004;


        #20  
        // data1 → mem(0x0004)
        HWDATA  = 32'h55667788;
        // write control 2     
        HWRITE  = 1'b1;
        HADDR   = 16'h0008;


        #20  
        // data2 → mem(0x0008)
        HWDATA  = 32'h99AABBCC;
        //// write control 3
        //HWRITE  = 1'b1;
        //HADDR   = 16'h000C;

        #20  
        // data3 → mem(0x000C)
        HWDATA  = 32'hAAAAAAAA;
        // write control 4
        HWRITE  = 1'b1;
        HADDR   = 16'h0010;

        #20  
        // data4 → mem(0x0010)
        HWDATA  = 32'hBBBBBBBB;
        // write control 5
        HWRITE  = 1'b1;
        HADDR   = 16'h0014;

        
        #20  
        // data5 → mem(0x0014)
        HWDATA  = 32'hCCCCCCCC;
        // write control 6
        HWRITE  = 1'b1;
        HADDR   = 16'h8000;

        #20  
        // data6 → mem(0x8000)
        HWDATA  = 32'hDDDDDDDD;
        // write control 7
        HWRITE  = 1'b1;
        HADDR   = 16'h8004;

        #20  
        // data7 → mem(0x8004)
        HWDATA  = 32'hEEEEEEEE;
        // write control 8
        HWRITE  = 1'b1;
        HADDR   = 16'h8008;

        #20  
        // data8 → mem(0x8008)
        HWDATA  = 32'hFFFFFFFF;
        // read control 0
        HWRITE  = 1'b0;
        HADDR   = 16'h0000;
        
        //--------------READ MEM----------------
        #20
        // MASTER FINDS HREADY=0, SO MASTER WAITS
        
        #20  
        // read control 1
        HWRITE  = 1'b0;
        HADDR   = 16'h0004;
        HWDATA  = 32'h00000000; // to test if hwdata will work when HWRTIE = 0

        #20  
        // read control 2
        HWRITE  = 1'b0;
        HADDR   = 16'h0008;

        #20  
        // read control 3
        HWRITE  = 1'b0;
        HADDR   = 16'h000C;

        #20  
        // read control 4
        HWRITE  = 1'b0;
        HADDR   = 16'h0010;

        #20  
        // read control 5
        HWRITE  = 1'b0;
        HADDR   = 16'h0014;

        #20  
        // read control 6
        HWRITE  = 1'b0;
        HADDR   = 16'h8000;

        #20  
        // read control 7
        HWRITE  = 1'b0;
        HADDR   = 16'h8004;

        #20  
        // read control 8
        HWRITE  = 1'b0;
        HADDR   = 16'h8008;

        #100 
        $finish();
    end


endmodule

至此,本项目的所有内容已介绍完毕。文章来源地址https://www.toymoban.com/news/detail-417717.html

到了这里,关于SoC设计项目 —— AHB SRAM控制器的设计 & March C-算法内建自测试的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数字IC实践项目(2)——高速SDRAM控制器的设计与综合(入门级工程项目)

    这个实践项目来源于研究生电子设计竞赛,在涉及到视频图像处理时需要用到DRAM存储数据 ;整个项目过程中先后学习了 小梅哥(AC620开发板资料) 、 开源骚客SDRAM控制器 、 正点原子FPGA教程 、 野火FPGA开发教程 等网络资料。 在此对上述提供学习资料的前辈表示真诚的感谢。

    2024年02月13日
    浏览(79)
  • 自抗扰(ADRC)控制原理及控制器设计

    自抗扰控制是在PID控制算法基础上进行改进的新型控制方法,它具有不依赖于控制对象模型、不区分系统内外扰的结构特点。常用的自抗扰控制器主要由 跟踪微分器 (Tracking Differentiator,TD)、 扩张状态观测器 (Extended State Observer,ESO)和 非线性状态误差反馈控制率 (Non

    2024年01月18日
    浏览(55)
  • 【设计模式】前端控制器模式

    前端控制器模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。 前端控制器(Front Controller)  

    2024年02月13日
    浏览(49)
  • 数电实验4:彩灯控制器设计

    西南交大 数电实验 ————《数字电路与计算机组成原理》 巩固组合逻辑电路设计、仿真方法. 学习简单时序电路的设计与实验方法。 巩固 Verilog HDL 层次化文件设计。 用 Verilog HDL 以层次化的设计方法(电路结构参照图 1 所示的电路框图), 设计一个 6 进制计数器及合适的

    2024年02月08日
    浏览(50)
  • 数字电路课程设计汽车尾灯控制器

    1、汽车尾灯控制器内容 基本设计要求: 设计系统模拟汽车尾灯两侧信号,左右各有3个 指示灯(用发光二极管模拟),具有如下模式: (1)汽车正向行使时,指示灯全部处于熄灭状态。 (2)汽车右转弯行驶时,右侧的3个指示灯按右循环顺序点亮, (3)汽车左转弯行驶时

    2024年02月06日
    浏览(52)
  • 一级倒立摆控制 —— LQR 控制器设计及 MATLAB 实现

    最优控制介绍 一级倒立摆控制 —— 系统建模(传递函数模型与状态空间方程表示) 一级倒立摆控制 —— PID 控制器设计及 MATLAB 实现 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现 1.1 系统变量表: 参数 符号 数值 小车质量 M M M 0.5 kg 摆杆质量 m m m 0.2 kg 小车摩擦系数 b

    2024年02月04日
    浏览(78)
  • 一级倒立摆控制 —— PID 控制器设计及 MATLAB 实现

    最优控制介绍 一级倒立摆控制 —— 系统建模(传递函数模型与状态空间方程表示) 一级倒立摆控制 —— 最优控制 线性二次型控制(LQR)及 MATLAB 实现 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现 一级倒立摆控制 —— ROS2 仿真 一级倒立摆控制 —— LQR 控制器 GAZEBO 仿

    2024年02月03日
    浏览(56)
  • 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现

    最优控制介绍 一级倒立摆控制 —— 系统建模(传递函数模型与状态空间方程表示) 一级倒立摆控制 —— PID 控制器设计及 MATLAB 实现 一级倒立摆控制 —— 最优控制 线性二次型控制(LQR)及 MATLAB 实现 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现 一级倒立摆控制 ——

    2024年02月03日
    浏览(46)
  • MATLAB 模型预测控制(MPC)控制入门 —— 设计并仿真 MPC 控制器

    MATLAB 模型预测控制(MPC) 模型预测控制工具箱™ 提供了用于开发模型预测控制 (MPC) 的函数、应用程序、Simulink® 模块和参考示例。对于线性问题,该工具箱支持设计隐式、显式、自适应和增益调度 MPC。对于非线性问题,您可以实现单级和多级非线性 MPC。该工具箱提供可部

    2024年02月02日
    浏览(53)
  • 基于FPGA的PID控制器设计

    PID控制应该算是应用非常广泛的控制算法了。常见的比如控制环境温度,控制无人机飞行高度速度等。PID我们将其分成三个参数,如下: P-比例控制,基本作用就是控制对象以线性的方式增加,在一个常量比例下,动态输出,缺点是会产生一个稳态误差。 I-积分控制,基本作

    2024年02月03日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包