本项目用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
注:8K * 4 = 32KB 是2^15, 所以是 0111 1111 1111 1111 , 【15:0】,16h'7FFF
WEN:写使能,当HWRITE = 1时,总线对SRAM发起write操作,WEN将被拉高;
当HWRITE = 0时,总线对SRAM发起read操作,WEN将被拉低,以保证读地址的数据不会被改写
ADDR:地址。
根据AHB SRAM读写时序中所介绍,当执行SRAM写操作时,interface模块会将存储器地址通过寄存器打一拍,然后在下一个周期和写数据一起送到相应的存储器端口上;
而执行SRAM读操作时,我们为了实现单周期读写,会直接将地址送到存储器端口,
这样,就可以在下个时钟上升沿顺利地拿到存储器返回的读数据,并送回AHB总线。
以上两种情况分别对应生成了SRAM_ADDR_write[12:0]和SRAM_ADDR_read[12:0]两个地址信号,我们通过iHWRITE_r判断当前周期的任务是read还是write,由此决定将哪个地址作真正被送往SRAM的地址端口:
wire [12:0] SRAM_ADDR_write;
wire [12:0] SRAM_ADDR_read;
assign SRAM_ADDR_write = iHADDR_r[14:2]; // WRITE:addr have to wait a T , sent together
//with data to SRAM_CORE
assign SRAM_ADDR_read = iHADDR [14:2]; // READ :addr send to MEM at once
assign oSRAM_ADDR = (iHWRITE_r == 1'b1) ? SRAM_ADDR_write : SRAM_ADDR_read;
WDATA:SRAM写数据,来自于总线上的HWDATA[31:0],sram_interface将32位的HWDATA按照下图顺序分配给8位SRAM,作为SRAM的写数据SRAM_WDATA
q:SRAM读数据,每个被选中的sram_bist将返回一个8位数据,sram_interface会将每个Bank中的4个单Byte读数据,组合为一个完整的32位读数据,返回到总线上的HRDATA
HWDATA与SRAM_WDATA的对应关系,
HRDATA与SRAM_q的对应关系,
如下图所示(以Bank0为例):
sram_interface模块的RTL代码如下:
module sram_interface (
//---------------AHB SIGNAL--------------
//in
input iHCLK ,
input iHRESETn,
input iHSEL ,
input iHWRITE ,
input [2:0] iHBURST ,
input [1:0] iHTRANS ,
input [2:0] iHSIZE ,
input [31:0] iHWDATA ,
input [15:0] iHADDR ,
//out
output [1:0] oHRESP ,
output oHREADY ,
output [31:0] oHRDATA ,
//--------------SRAM SIGNAL--------------
//in
input [7:0] iSRAM0_q,
input [7:0] iSRAM1_q,
input [7:0] iSRAM2_q,
input [7:0] iSRAM3_q,
input [7:0] iSRAM4_q,
input [7:0] iSRAM5_q,
input [7:0] iSRAM6_q,
input [7:0] iSRAM7_q,
//out
output oSRAM_CLK,
output oSRAM_WEN_BANK0,
output oSRAM_WEN_BANK1,
output [3:0] oSRAM_CSN_BANK0,
output [3:0] oSRAM_CSN_BANK1,
output [12:0] oSRAM_ADDR,
output [31:0] oSRAM_WDATA
);
/*————————————————————————————————————————————————————————————————————————*\
/ AHB Signal Register \
\*————————————————————————————————————————————————————————————————————————*/
reg iHSEL_r ;
reg iHWRITE_r ;
reg iHWRITE_2r;
reg [2:0] iHBURST_r ;
reg [1:0] iHTRANS_r ;
reg [2:0] iHSIZE_r ;
reg [31:0] iHWDATA_r ;
reg [15:0] iHADDR_r ;
reg [15:0] iHADDR_2r ;
always@( posedge iHCLK) begin
if(!iHRESETn) begin
iHSEL_r <= 1'b0;
iHWRITE_r <= 1'b0;
iHWRITE_2r <= 1'b0;
iHBURST_r <= 3'b0;
iHTRANS_r <= 2'b0;
iHSIZE_r <= 3'b0;
iHWDATA_r <= 32'b0;
iHADDR_r <= 16'b0;
iHADDR_2r <= 16'b0;
end
else begin
iHSEL_r <= iHSEL;
iHWRITE_r <= iHWRITE;
iHWRITE_2r <= iHWRITE_r;
iHBURST_r <= iHBURST;
iHTRANS_r <= iHTRANS;
iHSIZE_r <= iHSIZE;
iHWDATA_r <= iHWDATA;
iHADDR_r <= iHADDR;
iHADDR_2r <= iHADDR_r;
end
end
/*————————————————————————————————————————————————————————————————————————*\
/ AHB BUS → Interface → SRAM Core \
\*————————————————————————————————————————————————————————————————————————*/
// SRAM Write Enable
assign oSRAM_WEN_BANK0 = ( iHWRITE_r == 1'b1 && iHADDR_r[15] == 1'b0) ? 1'b1 : 1'b0;
assign oSRAM_WEN_BANK1 = ( iHWRITE_r == 1'b1 && iHADDR_r[15] == 1'b1) ? 1'b1 : 1'b0;
// SRAM Bank CSN select for read ↓ select for write ↓
assign oSRAM_CSN_BANK0 = ( iHADDR_r[15] == 1'b0 || (iHADDR[15] == 1'b0 && iHWRITE == 1'b0) ) ? 4'b1111 : 4'b0000;
assign oSRAM_CSN_BANK1 = ( iHADDR_r[15] == 1'b1 || (iHADDR[15] == 1'b1 && iHWRITE == 1'b0) ) ? 4'b1111 : 4'b0000;
// SRAM Addr
wire [12:0] SRAM_WRITE_ADDR;
wire [12:0] SRAM_READ_ADDR;
assign SRAM_WRITE_ADDR = iHADDR_r[14:2]; // WRITE:addr have to wait a T , sent together with data to SRAM_CORE
assign SRAM_READ_ADDR = iHADDR [14:2]; // READ :addr send to MEM at once
assign oSRAM_ADDR = (iHWRITE_r == 1'b1) ? SRAM_WRITE_ADDR : SRAM_READ_ADDR;
// SRAM Write Data
assign oSRAM_WDATA = iHWDATA;
/*————————————————————————————————————————————————————————————————————————*\
/ AHB BUS ← Interface ← SRAM Core \
\*————————————————————————————————————————————————————————————————————————*/
// response to AHB MASTER
assign oHREADY = (iHSEL_r == 1'b1 && (iHWRITE_r == 1'b1 || iHWRITE_2r == 1'b0)) ? 1'b1 : 1'b0 ;
assign oHRESP = (iHSEL_r == 1'b1) ? 2'b00 : 2'b00; //OKAY = 2'b00
// sram read data
assign oHRDATA = (iHSEL_r == 1'b1 && iHWRITE_r == 1'b0 && iHADDR_r[15] == 1'b0) ? {iSRAM3_q, iSRAM2_q, iSRAM1_q, iSRAM0_q}:
(iHSEL_r == 1'b1 && iHWRITE_r == 1'b0 && iHADDR_r[15] == 1'b1) ? {iSRAM7_q, iSRAM6_q, iSRAM5_q, iSRAM4_q}:
32'bz;
endmodule
1.5 sram_core
接下来是顶层模块下的sram_core,主要内容是将sram_bist模块进行了8次例化,
因此,sram_core实际上是将这8个SRAM拼成了一个16k×32的SRAM,
sram_core的地址共15位,地址范围为0x0000-0xFFFFF,
其中,Bank0对应0x0000-0x7FFFF;Bank1对应0x8000~0xFFFFF,
而每个sram_bist端口上的地址为sram_core上的地址右移两位得到,共13位,地址范围为0x0000~0x1FFF,
除此之外,sram_core将每个8k×8 SRAM的内建自测试的输出结果BIST_done_x,BIST_fail_x(x=0~7)进行了逻辑与/或以得到整块sram_core存储体的DFT测试结果,
在执行SRAM数据读写功能的时候,sram_bist可以看做8k×8的单端口SRAM。
sram_core模块的RTL代码如下:
module sram_core (
// From AHB
input iHCLK ,
input iHRESETn,
// From sram_interface
input iSRAM_WEN_BANK0,
input iSRAM_WEN_BANK1,
input [12:0] iSRAM_ADDR ,
input [3:0] iSRAM_CSN_BANK0,
input [3:0] iSRAM_CSN_BANK1,
input [31:0] iSRAM_WDATA ,
// To sram_interface
output [7:0] oSRAM0_q,
output [7:0] oSRAM1_q,
output [7:0] oSRAM2_q,
output [7:0] oSRAM3_q,
output [7:0] oSRAM4_q,
output [7:0] oSRAM5_q,
output [7:0] oSRAM6_q,
output [7:0] oSRAM7_q,
// BIST Signals
input iBIST_en,
output oBIST_done,
output oBIST_fail
);
/*————————————————————————————————————————————————————————————————————————*\
/ BIST Ouput Logic \
\*————————————————————————————————————————————————————————————————————————*/
wire BIST_done_0;
assign oBIST_done = BIST_done_0 && BIST_done_1 && BIST_done_2 && BIST_done_3
&& BIST_done_4 && BIST_done_5 && BIST_done_6 && BIST_done_7; // done if every sram_bist dones
assign oBIST_fail = BIST_done_0 || BIST_done_1 || BIST_done_2 || BIST_done_3
|| BIST_done_4 || BIST_done_5 || BIST_done_6 || BIST_done_7; // fail if any sram_bist fails
/*————————————————————————————————————————————————————————————————————————*\
/ BANK 0 Instantiation \
\*————————————————————————————————————————————————————————————————————————*/
sram_bist u_bank0_sram0 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK0[0]),
.iSRAM_WEN (iSRAM_WEN_BANK0 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[7:0] ),
.oSRAM_RDATA(oSRAM0_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_0 ),
.oBIST_fail (BIST_fail_0 )
);
sram_bist u_bank0_sram1 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK0[1]),
.iSRAM_WEN (iSRAM_WEN_BANK0 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[15:8] ),
.oSRAM_RDATA(oSRAM1_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_1 ),
.oBIST_fail (BIST_fail_1 )
);
sram_bist u_bank0_sram2 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK0[2]),
.iSRAM_WEN (iSRAM_WEN_BANK0 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[23:16]),
.oSRAM_RDATA(oSRAM2_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_2 ),
.oBIST_fail (BIST_fail_2 )
);
sram_bist u_bank0_sram3 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK0[3]),
.iSRAM_WEN (iSRAM_WEN_BANK0 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[31:24]),
.oSRAM_RDATA(oSRAM3_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_3 ),
.oBIST_fail (BIST_fail_3 )
);
/*————————————————————————————————————————————————————————————————————————*\
/ BANK 1 Instantiation \
\*————————————————————————————————————————————————————————————————————————*/
sram_bist u_bank1_sram4 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK1[0]),
.iSRAM_WEN (iSRAM_WEN_BANK1 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[7:0] ),
.oSRAM_RDATA(oSRAM4_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_4 ),
.oBIST_fail (BIST_fail_4 )
);
sram_bist u_bank1_sram5 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK1[1]),
.iSRAM_WEN (iSRAM_WEN_BANK1 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[15:8] ),
.oSRAM_RDATA(oSRAM5_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_5 ),
.oBIST_fail (BIST_fail_5 )
);
sram_bist u_bank1_sram6 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK1[2]),
.iSRAM_WEN (iSRAM_WEN_BANK1 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[23:16]),
.oSRAM_RDATA(oSRAM6_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_6 ),
.oBIST_fail (BIST_fail_6 )
);
sram_bist u_bank1_sram7 (
// Function Mode IO
.iSRAM_CLK (iHCLK ),
.iSRAM_CSN (iSRAM_CSN_BANK1[3]),
.iSRAM_WEN (iSRAM_WEN_BANK1 ),
.iSRAM_ADDR (iSRAM_ADDR ), //13 bits SRAM ADDR
.iSRAM_WDATA(iSRAM_WDATA[31:24]),
.oSRAM_RDATA(oSRAM7_q ),
// Test Mode IO
.iBIST_en (iBIST_en ),
.oBIST_done (BIST_done_7 ),
.oBIST_fail (BIST_fail_7 )
);
endmodule
1.6 SRAM读写功能的仿真验证
在完成RTL设计后,我们编写了Testbench,
并在Vivado平台上进行了简单的读写仿真验证,波形如下:
分析一下Testbench具体对SRAM发起了什么操作:
首先,T1-T7进行了六次写操作,将6个数据依次写入SRAM的0x0000,0x0004,0x0008,0x8000,0x8004,0x8008六个地址当中
其中前三个地址对应Bank0,后三个地址对应Bank1,
因此在T2-T4期间 SRAM_CSN_BANK0 和 SRAM_WEN_BANK0 被拉高,
在T5-T7期间 SRAM_CSN_BANK1 和 SRAM_WEN_BANK1 被拉高,
从上图中可以看出,T7除了是Data 6的写数据周期,也是Data 1 读地址周期,
但是由于SRAM端口上,该周期需要执行写Data 6的操作。
于是发生了地址冲突,无法在该周期同时进行读Data 1
因此,在T8并没有返回Data 1的读数据,HREADY被拉低,
随后,在T9-T14,总线上HRDATA依次拿到了六个SRAM读数据,读出的data与T1-T7写入的data完全一致,证明了以上SRAM控制器的设计逻辑是正确的
1.7 支持半字/字节读写
细心的读者们可能发现上述设计仅考虑了读SRAM按字进行读写的情况,
也就是每次读写都是32位的。
实际上,AHB协议同样支持我们以16位/8位的方式对SRAM进行访问,由HSIZE[2:0]控制,具体对应关系如下:
HSIZE[2:0] = 3'b000:按BYTE读写
HSIZE[2:0] = 3'b001:按Half Word读写
HSIZE[2:0] = 3'b010:按Word读写
支持半字/字节读写使得CSN信号的生成变得更为复杂,
基于上述设计,读者们可以思考一下如何实现该功能,
以下给出参考设计:
// SRAM Bank CSN
wire [3:0] SRAM_CSN_BANK0_write;
wire [3:0] SRAM_CSN_BANK0_read ;
wire [3:0] SRAM_CSN_BANK1_write;
wire [3:0] SRAM_CSN_BANK1_read ;
always @(*) begin
case( {iHADDR[15],iHSIZE} )
//选中BANK0,HSIZE = Word
4'b0010:begin
SRAM_CSN_BANK0_read <= 4'b1111;
SRAM_CSN_BANK0_read <= 4'b0000;
end
//选中BANK1,HSIZE = Word
4'b1010:begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b1111;
end
//选中BANK0,HSIZE = Half Word
4'b0001:begin
if(iHADDR[1]) begin
SRAM_CSN_BANK0_read <= 4'b1100;
SRAM_CSN_BANK1_read <= 4'b0000;
end else begin
SRAM_CSN_BANK0_read <= 4'b0011;
SRAM_CSN_BANK1_read <= 4'b0000;
end
end
//选中BANK1,HSIZE = Half Word
4'b1001:begin
if(iHADDR[1]) begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b1100;
end else begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b0011;
end
end
//选中BANK0,HSIZE = BYTE
4'b0000:begin
case (iHADDR[1:0])
2'b00: begin
SRAM_CSN_BANK0_read <= 4'b0001;
SRAM_CSN_BANK1_read <= 4'b0000;
end
2'b01: begin
SRAM_CSN_BANK0_read <= 4'b0010;
SRAM_CSN_BANK1_read <= 4'b0000;
end
2'b10: begin
SRAM_CSN_BANK0_read <= 4'b0100;
SRAM_CSN_BANK1_read <= 4'b0000;
end
2'b11: begin
SRAM_CSN_BANK0_read <= 4'b1000;
SRAM_CSN_BANK1_read <= 4'b0000;
end
endcase
end
//选中BANK1,HSIZE = BYTE
4'b1000:begin
case (iHADDR[1:0])
2'b00: begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b0001;
end
2'b01: begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b0010;
end
2'b10: begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b0100;
end
2'b11: begin
SRAM_CSN_BANK0_read <= 4'b0000;
SRAM_CSN_BANK1_read <= 4'b1000;
end
endcase
end
endcase
end
always @(*) begin
case( {iHADDR_r[15],iHSIZE_r} )
//选中BANK0,HSIZE = Word
4'b0010:begin
SRAM_CSN_BANK0_write <= 4'b1111;
SRAM_CSN_BANK0_write <= 4'b0000;
end
//选中BANK1,HSIZE = Word
4'b1010:begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b1111;
end
//选中BANK0,HSIZE = Half Word
4'b0001:begin
if(iHADDR_r[1]) begin
SRAM_CSN_BANK0_write <= 4'b1100;
SRAM_CSN_BANK1_write <= 4'b0000;
end else begin
SRAM_CSN_BANK0_write <= 4'b0011;
SRAM_CSN_BANK1_write <= 4'b0000;
end
end
//选中BANK1,HSIZE = Half Word
4'b1001:begin
if(iHADDR_r[1]) begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b1100;
end else begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b0011;
end
end
//选中BANK0,HSIZE = BYTE
4'b0000:begin
case (iHADDR_r[1:0])
2'b00: begin
SRAM_CSN_BANK0_write <= 4'b0001;
SRAM_CSN_BANK1_write <= 4'b0000;
end
2'b01: begin
SRAM_CSN_BANK0_write <= 4'b0010;
SRAM_CSN_BANK1_write <= 4'b0000;
end
2'b10: begin
SRAM_CSN_BANK0_write <= 4'b0100;
SRAM_CSN_BANK1_write <= 4'b0000;
end
2'b11: begin
SRAM_CSN_BANK0_write <= 4'b1000;
SRAM_CSN_BANK1_write <= 4'b0000;
end
endcase
end
//选中BANK1,HSIZE = BYTE
4'b1000:begin
case (iHADDR_r[1:0])
2'b00: begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b0001;
end
2'b01: begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b0010;
end
2'b10: begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b0100;
end
2'b11: begin
SRAM_CSN_BANK0_write <= 4'b0000;
SRAM_CSN_BANK1_write <= 4'b1000;
end
endcase
end
endcase
end
assign oSRAM_CSN_BANK0 = (iHWRITE_r == 1'b1) ? SRAM_CSN_BANK0_write : SRAM_CSN_BANK0_read;
assign oSRAM_CSN_BANK1 = (iHWRITE_r == 1'b1) ? SRAM_CSN_BANK1_write : SRAM_CSN_BANK1_read;
可以看到,CSN信号的生成和ADDR类似,需要考虑因为读/写操作时序不同而带来的两种情况,
分别生成了SRAM_CSN_BANK0_write[3:0]和SRAM_CSN_BANK0_read[3:0](BANK1同理),
最后通过iHWRITE_r来对两者进行选择