绪论
本项目用Verilog HDL语言设计了AHB总线上的SRAM控制器,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位读数据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
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来对两者进行选择
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测试电路,虽然会增加系统的数字面积,但同时也极大地降低了产品在测试环节所花费的开销成本
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读写操作,是由有限状态机控制的,该状态机示意图如下:
下面让我们来看看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结束。
以上,就是关于AHB-SRAN的BIST模式下的逻辑仿真解读。
最后附上两种工作模式下的Testbench:文章来源:https://www.toymoban.com/news/detail-429979.html
点击查看代码
`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-429979.html
到了这里,关于「数字IC设计项目」 —— AHB SRAM控制器设计 & March C-算法内建自测试的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!