绪论
本项目用Verilog HDL语言设计了AHB总线上的SRAM控制器接口IP,SRAM存储器在AHB总线上作为AHB slave存在,该SRAM控制器具有以下特性:
-
支持单周期的SRAM读写操作
-
支持低功耗工作
SRAM存储体由两个Bank组成,系统根据地址选中一块/多块Bank,未被选中的Bank将处于low-power standby模式以降低功耗 -
支持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存储体的作用,具体来说:
- 将HCLK,HRESETn,HTRANS,HBURST,HWRITE,HWDATA这些来自于AHB总线的信号转化为存储器接口信号
- 接收存储器8位读数据,并根据总线给出的地址,转化为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验证,本项目中Pattern基于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的时钟为AHB总线时钟信号HCLK,因此为了实现总线上的单周期读出,会直接将地址送到SRAM端口。这样,在数据周期刚开始时,读数据就可以返回HRDATA,
注意,这样的设计具有一些局限性:由于SRAM端口上的读地址相比于写地址要滞后一个周期,因此当写操作的下一个周期切换为读操作时,会产生地址冲突,
于是,SRAM控制器会将HREADY拉低一个周期,进行缓冲,下一个周期才会重新拉高HREADY并且返回相应的读数据,
在多个读操作连续执行/多个写操作连续执行时,则不会有这样的问题,可以AHB总线最高的速度进行SRAM读写访问,
由于在实际应用中,存储器访问一般不会频繁地在读与写之间切换,因此这样设计对于访问速度带来的代价是可以忽略掉的,
而这样设计的好处则,在于可以避免为SRAM引入额外的时钟源
1.3 RTL代码设计
在明确了AHB SRAM读写的设计需求和读写时序后,我们来看看具体的硬件电路如何设计:
sram_top
首先是顶层模块,主要内容是对两个子模块进行了例化,其中涉及到的信号均已经在架构图中标明,这里不再赘述
`timescale 1ns / 1ps
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 ),
.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_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
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
WEN:写使能,当HWRITE = 1时,总线对SRAM发起write操作,WEN将被拉高;
当HWRITE = 0时,总线对SRAM发起read操作,WEN将被拉低,以保证读地址的数据不会被改写
ADDR:地址。根据AHB SRAM读写时序中所介绍,
当执行SRAM写操作时,interface模块会将存储器地址通过寄存器打一拍,然后在下一个周期和写数据一起送到相应的存储器端口上
而执行SRAM读操作时,我们为了实现单周期读写,会直接将地址送到存储器端口。这样,就可以在下个时钟上升沿顺利地拿到存储器返回的读数据,并送回AHB总线
WDATA:SRAM写数据,来自于总线上的HWDATA[31:0],sram_interface将HWDATA按照下图顺序作为每个8位SRAM的写数据
q:SRAM读数据,每个被选中的sram_bist将返回一个8位数据,sram_interface会将每个Bank中的4个单Byte读数据,组合为一个完整的32位读数据,返回到总线上的HRDATA
HWDATA与SRAM_WDATA的对应关系,
HRDATA与SRAM_q的对应关系,如下图所示:
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 [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_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
);
/*————————————————————————————————————————————————————————————————————————*\
/ Register for AHB Signal \
\*————————————————————————————————————————————————————————————————————————*/
reg iHSEL_r ;
reg iHWRITE_r ;
reg iHWRITE_2r;
reg [2:0] iHBURST_r ;
reg [1:0] iHTRANS_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;
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;
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
sram_core
接下来是顶层模块下的sram_core,主要内容是将sram_bist模块进行了8次例化,
因此,sram_core实际上是将这8个SRAM拼成了一个16k×32的SRAM,
sram_core的地址共15位,为0000~0xFFFFF,
其中,Bank0对应0x00000x7FFFF;Bank1对应0x80000xFFFFF,
而每个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, BIST_done_1, BIST_done_2, BIST_done_3;
wire BIST_done_4, BIST_done_5, BIST_done_6, BIST_done_7;
wire BIST_fail_0, BIST_fail_1, BIST_fail_2, BIST_fail_3;
wire BIST_fail_4, BIST_fail_5, BIST_fail_6, BIST_fail_7;
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.4 SRAM读写仿真验证
在完成RTL设计后,我们编写了Testbench,
并在Vivado平台上进行了简单的读写仿真验证,波形如下:
分析一下Testbench具体对SRAM发起了什么操作:
首先,T1-T7进行了六次写操作,将6个数据依次写入SRAM的0x0000,0x0001,0x0002,0x8000,0x8001,0x8002六个地址当中
其中前三个地址对应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控制器的设计逻辑是正确的
2. 基于March C-算法的DFT功能
2.1 BIST架构
在设计中,SRAM读写模式和DFT模式的选择通过2选1选择器实现,当DFT模式的使能信号BIST_en = 1时,来自于sram_interface的所有信号逻辑将被忽略,
SRAM端口上的输入信号将全部来自于sram_bist内部的BIST控制电路生成的Pattern,
对于March C-算法,BIST控制电路会对SRAM每个地址进行“写 → 读 → 比较”的操作,
若所有的读数据结果与写入的数据是一致的,BIST_done最终将被拉高,说明该电路生产过程中没有出现生产故障,
反之,如果比较发现有错误,BIST_fail最终将被拉高,该块芯片将被报废,
在模块内加入DFT测试电路,虽然会增加系统的数字面积,但同时也极大地降低了产品在测试环节所花费的开销成本
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算法的具体检测流程如下:
-
从最低地址开始,在整个存储器中依次写入0(升序)
-
读出最低地址,结果应为0,然后把1写入该存储单元。完成后地址+1,再次执行该操作,直至对整个存储器执行该操作(升序)
-
读出最高地址,结果应为1,然后把0写入该存储单元,再次读该单元,结果应为0。完成后地址-1,再次执行该操作,直至对整个存储器执行该操作(降序)
-
读出最高地址,结果应为0,然后把1写入该存储单元。完成后地址-1,再次执行该操作,直至对整个存储器执行该操作(降序)
-
读出最低地址,结果应为1,然后把0写入该存储单元,再次读该单元,结果应为0。完成后地址+1,再次执行该操作,直至对整个存储器执行该操作(升序)
由于步骤4,步骤5中,加粗字体所描述的操作实际上是重复的,
因此后来有了改进的March C-算法, 将步骤3中的加粗部分删除,如下:
图中,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_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被拉高,这是必然的 ,因为逻辑仿真中不涉及实际芯片制造中的各种故障,
我们先从宏观上看仿真波形:
从波形中可以看到,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状态之间的转换波形:
其他几段状态转化处的波形也是同理,在此不在一一标注解释了,具体如下:
MARCH_1状态和MARCH_2状态之间的波形:
MARCH_2状态和MARCH_3状态之间的波形:
MARCH_3状态和MARCH_4状态之间的波形:
MARCH_4状态最后一段波形:
完成MARCH_4后,BIST_done被拉高,代表BIST结束。
以上,就是BIST的逻辑仿真解读。
至此,本项目的所有内容已介绍完毕。