AHB 总线的 SRAM 控制器设计
一、基于 AHB 的 sram 设计
1、总体设计框架
2、AHB总线传输协议
①没有等待状态的单个读写操作
②有等待状态的单个读写操作
③连续读写操作
二、数据读写位宽与深度、块选与片选控制
1、hsize控制读写数据位宽与数据深度(默认位宽为32bit,深度为2^14)
通过haddr的高位自动控制sram内的块选与片选,根据位宽自动拼接各片存储ram,对应关系如下图所示:
2、当hsize为2’b00时,数据位宽为8bit,数据深度为2^16
当数据位宽为8位时由下图可知,控制器内的sram相当于串联了每个片选,数据会根据地址位的增加进而向高位片选进行存取操作。
3、当hsize为2’b01时,数据位宽为16bit,数据深度为2^15
当数据位宽为16位时由下图可知,控制器将两两片选绑定,低位片选存取数据低8位,高位片选存取数据高8位。
4、当hsize为2’b10时,数据位宽为32bit,数据深度为2^14
当数据位宽为32位时由下图可知,控制器将4个片选绑定,即每一块作为一个数据的32位存取。
三、程序设计
RTL设计
1、sramc_top.v
module sramc_top(
//input signals
input wire hclk,
input wire sram_clk, // hclk 的反向,与hclk属于同一个时钟沿
input wire hresetn, // 复位
input wire hsel, // AHB-Slave 有多个,此处对应的是AHB-SRAM的hsel
input wire hwrite, // 读/写指示
input wire hready, // master -> slave,一般接常高
input wire [2:0] hsize , // 访问数据有效字节数
input wire [2:0] hburst, // 此处没有用到
input wire [1:0] htrans, // SEQ/NOSEQ,传输是否有效
input wire [31:0] hwdata, // 写数据
input wire [31:0] haddr, // 本次命令访问的地址
//Signals for BIST and DFT test mode
//When signal"dft_en" or "bist_en" is high, sram controller enters into test mode.
// 内建测试,测试内部的SRAM制造是否有问题,功能验证时此处接零即可,DFT工程师会专门去做的!
input wire dft_en,
input wire bist_en,
//output signals
output wire hready_resp, // slave -> master,看 slave 是否ready,在前面介绍的规格里我们知道slave不支持反压,hready_resp 会常高
output wire [1:0] hresp, // hresp 也只会返回0,即ok状态。
output wire [31:0] hrdata, // 从sram读出的数据
//When "bist_done" is high, it shows BIST test is over.
output wire bist_done,
//"bist_fail" shows the results of each sram funtions.There are 8 srams in this controller.
output wire [7:0] bist_fail
);
//Select one of the two sram blocks according to the value of sram_csn
wire bank_sel ;
wire [3:0] bank0_csn;
wire [3:0] bank1_csn;
//Sram read or write signals: When it is high, read sram; low, writesram.
wire sram_w_en; // hwrite is 1, write; hwrite is 0, read. 但是sram是为0时写,为1时读。所以需要一个信号去翻译AHB信号(取反)
//Each of 8 srams is 8kx8, the depth is 2^13 (8K), so the sram's address width is 13 bits.
wire [12:0] sram_addr;
//AHB bus data write into srams
wire [31:0] sram_wdata;
//sram data output data which selected and read by AHB bus
wire [7:0] sram_q0;
wire [7:0] sram_q1;
wire [7:0] sram_q2;
wire [7:0] sram_q3;
wire [7:0] sram_q4;
wire [7:0] sram_q5;
wire [7:0] sram_q6;
wire [7:0] sram_q7;
// Instance the two modules:
// ahb_slave_if.v and sram_core.v
ahb_slave_if ahb_slave_if_u(
//-----------------------------------------
// AHB input signals into sram controller
//-----------------------------------------
.hclk (hclk),
.hresetn (hresetn),
.hsel (hsel),
.hwrite (hwrite),
.hready (hready),
.hsize (hsize),
.htrans (htrans),
.hburst (hburst),
.hwdata (hwdata),
.haddr (haddr),
//-----------------------------------------
//8 sram blcoks data output into ahb slave
//interface
//-----------------------------------------
.sram_q0 (sram_q0),
.sram_q1 (sram_q1),
.sram_q2 (sram_q2),
.sram_q3 (sram_q3),
.sram_q4 (sram_q4),
.sram_q5 (sram_q5),
.sram_q6 (sram_q6),
.sram_q7 (sram_q7),
//---------------------------------------------
//AHB slave(sram controller) output signals
//---------------------------------------------
.hready_resp (hready_resp),
.hresp (hresp),
.hrdata (hrdata),
//---------------------------------------------
//sram control signals and sram address
//---------------------------------------------
// 五组信号:读写指示、数据、地址、块选、片选
.sram_w_en (sram_w_en),
.sram_addr_out(sram_addr),
//data write into sram
.sram_wdata (sram_wdata),
//choose the corresponding sram in a bank, active low
.bank_sel (bank_sel ),
.bank0_csn (bank0_csn),
.bank1_csn (bank1_csn)
);
sram_core sram_core_u(
//AHB bus signals
.hclk (hclk ),
.sram_clk (sram_clk),
.hresetn (hresetn ),
//-------------------------------------------
//sram control singals from ahb_slave_if.v
//-------------------------------------------
.sram_addr (sram_addr ),
.sram_wdata_in(sram_wdata),
.sram_wen (sram_w_en ),
.bank_sel (bank_sel ),
.bank0_csn (bank0_csn ),
.bank1_csn (bank1_csn ),
//test mode enable signals
.bist_en (bist_en ),
.dft_en (dft_en ),
//-------------------------------------------
//8 srams data output into AHB bus
//-------------------------------------------
.sram_q0 (sram_q0),
.sram_q1 (sram_q1),
.sram_q2 (sram_q2),
.sram_q3 (sram_q3),
.sram_q4 (sram_q4),
.sram_q5 (sram_q5),
.sram_q6 (sram_q6),
.sram_q7 (sram_q7),
//test results output when in test mode
.bist_done (bist_done),
.bist_fail (bist_fail)
);
endmodule
2、ahb_slave_if.v
ahb slave接口模块将ahb master发送的存取指令转换为sram的数据存取与块、片选指令。
module ahb_slave_if(
// AHB信号列表
// singals used during normal operation
input hclk,
input hresetn,
// signals from AHB bus during normal operation
input hsel, //hsel恒为1,表示选中该SRAMC
input hready, //由Master总线发出,hready=1,读/写数据有效;否则,无效
input hwrite, //hwrite=1,写操作;hwrite=0,读操作
input [1:0] htrans, //当前传输类型10:NONSEQ,11:SEQ(命令是否有效)
input [2:0] hsize, //每一次传输的数据大小,支持8/16/32bit传输
input [2:0] hburst, //burst操作,该项目无用,置0即可
input [31:0] haddr, //AHB:32位系统总线地址
input [31:0] hwdata, //AHB:32位写数据操作
// signals from sram_core data output (read srams)
input [7:0] sram_q0, //表示读取sram数据信号
input [7:0] sram_q1, //8片RAM的返回数据
input [7:0] sram_q2, //可以根据hsize和haddr判断哪一片RAM有效
input [7:0] sram_q3,
input [7:0] sram_q4,
input [7:0] sram_q5,
input [7:0] sram_q6,
input [7:0] sram_q7,
// signals to AHB bus used during normal operation
//(读数据输出->进入AHB总线),即返回给Master的相关信号
output [1:0] hresp, //状态信号,判断hrdata读数据是否出错;00:OKAY,01:ERROR传输错误
output hready_resp, //判断hrdata是否有效,hready_out
output [31:0] hrdata, //读数据信号
// sram read or write enable signals,
// 以下5个信号为返回给RAM的信号
// when "sram_w_en" is low, it means write sram, when "sram_w_en" is high, it means read sram,
output sram_w_en, //写使能信号,0:写;1:读
// choose the write srams when bank is confirmed
// bank_csn allows the four bytes in the 32-bit width to be written independently
output reg bank_sel,//bank_sel为1代表bank0被访问;bank_sel为0代表bank1被访问
output reg [3:0] bank0_csn,//bank0内片区选择(0为选中该片区,如4’b1100则选中bank0低两位片区)
output reg [3:0] bank1_csn,//bank1内片区选择
// signals to sram_core in normal operation, it contains sram address and data writing into sram
//(写数据输出->进入sram存储单元)
output [12:0] sram_addr_out, // 地址输出进入sram,13位地址(8k=2^3*2^10=2^13)
output [31:0] sram_wdata //写数据进入sram
);
// internal registers used for temp the input ahb signals (临时信号)
// temperate all the AHB input signals
reg hwrite_r; //_r:表示这些信号会经过寄存器寄存一拍;
reg [2:0] hsize_r; //因为AHB协议传输分为地址阶段和数据阶段两个部分,而SRAM的地址和数据是在同一拍进行传输,
reg [2:0] hburst_r; //AHB的地址和控制信号会在数据信号的前一拍生效,所以,为了将AHB与SRAM之间的协议进行转换,
reg [1:0] htrans_r; //使数据对齐,需将AHB的地址与控制信号打一拍再传输,这样传入SRAM的地址和数据便处于同一拍,
reg [31:0] haddr_r; //以满足SRAM的时序要求。
reg [3:0] sram_csn; //内部信号,由于sram分为bank0和bank1两部分,在进行读写数据时,首先会根据地址范围判断选中bank0
//或者bank1,再根据hsize_r和haddr_r来确定具体访问到bank0/bank1中的具体哪一片sram。
// Internal signals 中间信号
// "haddr'_sel" and "hsize_sel" used to generate banks of sram: "bank0_sel" and "bank1_sel"
wire [1:0] haddr_sel;
wire [1:0] hsize_sel;
//reg bank_sel;
wire sram_csn_en; //sram片选使能信号
wire sram_write; //来自AHB总线的sram写使能信号
wire sram_read; //来自AHB总线的sram读使能信号
wire [15:0] sram_addr; //来自AHB总线的sram地址信号,64K=2^5*2^10=2^15
reg [31:0] sram_data_out; //从sram发出的读数据信号,发送至AHB总线
// transfer type signal encoding
parameter IDLE = 2'b00; //定义htrans的状态
parameter BUSY = 2'b01;
parameter NONSEQ = 2'b10; //数据传输有效(非连续传输)
parameter SEQ = 2'b11; //数据传输有效(连续传输)
parameter SUB_DATA = 1'b0;//输出数据补状态(例输出16位数据,则高16位补SUB_DATA)
//--------------------------------------------------------------------------------------------------------
//----------------------------------------------Main code-------------------------------------------------
//--------------------------------------------------------------------------------------------------------
// Combitional portion , 组合逻辑部分
// assign the response and read data of the AHB slave
// To implement sram function-writing or reading in one cycle, value of hready_resp is always "1"
assign hready_resp = 1'b1; //hready_resp恒为1,不支持反压,由Slave返回给Master,读/写数据可在一个循环内完成
assign hresp = 2'b00; //00表示hrdata读数据OKAY,不支持ERROR、RETRY、SPLIT,只返回OKAY状态
// sram data output to AHB bus
assign hrdata = sram_data_out; //由sram存储单元输出数据,经hrdata导出至AHB总线,支持8/16/32bit位
// Generate sram write and read enable signals
assign sram_write = ((htrans_r == NONSEQ) || (htrans_r == SEQ)) && hwrite_r;
assign sram_read = ((htrans_r == NONSEQ) || (htrans_r == SEQ)) && (! hwrite_r);
assign sram_w_en = !sram_write; //SRAM写使能为0,代表写;为1,代表读;其含义与由总线产生的sram_write中间信号相反
// Generate sram address
// 系统逻辑地址(eg:CPU)看到的空间是 0 1 2 3 4 5 6 7 8 ...,但是访问的时候总线位宽是32bit,所以访问地址依次是0 4 8 C。
// 但是对于SRAM这个存储介质来讲,看到的空间是实际的物理地址,每个地址是由32bit组成的,所以访问地址依次是:0 1 2 3
assign sram_addr = haddr_r[15:0]; //系统内存空间:64K=2^6*2^10=2^16,即系统地址由16根地址线组成--系统地址
//assign sram_addr_out = sram_addr[14:2];//物理地址=系统地址/4,即右移两位,64KB=8*8K*8bit,每一片SRAM地址深度为8K=2^13,有13根地址线(详细原因参考后文)
assign sram_addr_out = sram_addr[12:0];//物理地址=系统地址/4,即右移两位,64KB=8*8K*8bit,每一片SRAM地址深度为8K=2^13,有13根地址线(详细原因参考后文)
// Generate bank select signals by the value of sram_addr[15].
// Each bank(32K*32)comprises of four sram block(8K*8), and the width of the address of the bank is
// 15 bits(14-0),so the sram_addr[15] is the minimum of the next bank. if it is value is '1', it means
// the next bank is selected.
assign sram_csn_en = (sram_write || sram_read);
// signals used to generating sram chip select signal in one bank.
//assign haddr_sel = sram_addr[1:0]; /*修改前*/
assign haddr_sel = sram_addr[14:13]; /*修改后*/ //通过sram的地址低两位和hsize_r信号判断选择4片sram中的具体哪一片
assign hsize_sel = hsize_r[1:0];
// data from AHB writing into sram.
assign sram_wdata =hwdata; //将通过AHB的数据写进sram存储单元中
/*修改前*/
//片选使能为1且sram_addr[15]为0,表示选中bank0,接着再根据sram_csn选中bank0中4个RAM的某几个RAM(详细原因参考后文)
//assign bank0_csn = (sram_csn_en && (sram_addr[15] == 1'b0))?sram_csn:4'b1111; //系统地址的最高位为sram_addr[15],用来判断访问sram的bank0还是bank1
//assign bank1_csn = (sram_csn_en && (sram_addr[15] == 1'b1))?sram_csn:4'b1111; //sram_addr[15]=0 访问bank0;sram_addr[15]=1 访问bank1 因为是均分的BANK
//assign bank_sel = (sram_csn_en && (sram_addr[15] == 1'b0))?1'b1:1'b0; //bank_sel为1代表bank0被访问;bank_sel为0代表bank1被访问
/*修改后*///bank与cs控制选择
always@(*)begin
if(sram_csn_en)begin
case(hsize_sel)
2'b00:begin//8bit
bank0_csn = (sram_addr[15] == 1'b0)?sram_csn:4'b1111;
bank1_csn = (sram_addr[15] == 1'b1)?sram_csn:4'b1111;
bank_sel = (sram_addr[15] == 1'b0)?1'b1:1'b0;
end
2'b01:begin
bank0_csn = (sram_addr[14] == 1'b0)?sram_csn:4'b1111;
bank1_csn = (sram_addr[14] == 1'b1)?sram_csn:4'b1111;
bank_sel = (sram_addr[14] == 1'b0)?1'b1:1'b0;
end
2'b10:begin
bank0_csn = (sram_addr[13] == 1'b0)?sram_csn:4'b1111;
bank1_csn = (sram_addr[13] == 1'b1)?sram_csn:4'b1111;
bank_sel = (sram_addr[13] == 1'b0)?1'b1:1'b0;
end
default:begin//默认32位
bank0_csn = (sram_addr[13] == 1'b0)?sram_csn:4'b1111;
bank1_csn = (sram_addr[13] == 1'b1)?sram_csn:4'b1111;
bank_sel = (sram_addr[13] == 1'b0)?1'b1:1'b0;
end
endcase
end
else begin
bank0_csn = 4'b1111;
bank1_csn = 4'b1111;
bank_sel = 1'b1;
end
end
// Choose the right data output of two banks(bank0,bank1) according to the value of bank_sel.
/*修改前*///If bank_sel = 1'b1, bank1 selected;or, bank0 selected.
// assign sram_data_out = (bank_sel) ? {sram_q3,sram_q2,sram_q1,sram_q0}: //对sram的数据输出进行选择
// {sram_q7,sram_q6,sram_q5,sram_q4};
/*修改一版*///改为可变数据位宽输出与默认输出为0
// /assign sram_data_out = sram_read?//如果sram读,则对sram的数据输出进行选择、如果sram非读,sram输出为0
// ((bank_sel) ? ((hsize_sel==2'b10)?{sram_q3,sram_q2,sram_q1,sram_q0}:((hsize_sel==2'b01)?{16'd0,sram_q1,sram_q0}:{24'd0,sram_q0})):
// ((hsize_sel==2'b10)?{sram_q7,sram_q6,sram_q5,sram_q4}:((hsize_sel==2'b01)?{16'd0,sram_q5,sram_q4}:{24'd0,sram_q4})))
// :32'd0;
/*修改最终版*///片区输出控制sram_data_out
always@(*)begin
if(!hresetn)begin
sram_data_out = {32{SUB_DATA}};
end
else if(sram_read)begin
if(bank_sel)begin
case(hsize_sel)
2'b00:begin//data size 8bit
case(haddr_sel)
2'b00:sram_data_out = {{24{SUB_DATA}},sram_q0};
2'b01:sram_data_out = {{24{SUB_DATA}},sram_q1};
2'b10:sram_data_out = {{24{SUB_DATA}},sram_q2};
2'b11:sram_data_out = {{24{SUB_DATA}},sram_q3};
endcase
end
2'b01:begin//data size 16bit
case(haddr_sel[0])
1'b0:sram_data_out = {{16{SUB_DATA}},sram_q1,sram_q0};
1'b1:sram_data_out = {{16{SUB_DATA}},sram_q3,sram_q2};
endcase
end
2'b10:sram_data_out = {sram_q3,sram_q2,sram_q1,sram_q0};//data size 32bit
default:begin
sram_data_out = {sram_q3,sram_q2,sram_q1,sram_q0};//data size 32bit
end
endcase
end
else begin
case(hsize_sel)
2'b00:begin//data size 8bit
case(haddr_sel)
2'b00:sram_data_out = {{24{SUB_DATA}},sram_q4};
2'b01:sram_data_out = {{24{SUB_DATA}},sram_q5};
2'b10:sram_data_out = {{24{SUB_DATA}},sram_q6};
2'b11:sram_data_out = {{24{SUB_DATA}},sram_q7};
endcase
end
2'b01:begin//data size 16bit
case(haddr_sel[0])
1'b0:sram_data_out = {{16{SUB_DATA}},sram_q5,sram_q4};
1'b1:sram_data_out = {{16{SUB_DATA}},sram_q7,sram_q6};
endcase
end
2'b10:sram_data_out = {sram_q7,sram_q6,sram_q5,sram_q4};//data size 32bit
default:begin
sram_data_out = {sram_q7,sram_q6,sram_q5,sram_q4};//data size 32bit
end
endcase
end
end
else begin
sram_data_out = sram_data_out;
end
end
// Generate the sram chip selecting signals in one bank.
// results show the AHB bus write or read how many data once a time:byte(8),halfword(16) or word(32).
always@(*) begin
//always@(*) begin
if(hsize_sel == 2'b10) //32bits:word operation,4片sram都会进行访问
sram_csn = 4'b0; //active low,sram_csn信号低有效,4'b0000代表4片SRAM都被选中
else if(hsize_sel == 2'b01) //16bits:halfword,选中4片中的其中两片(前两片或者后两片)
begin
//if(haddr_sel[1] == 1'b0) /*修改前*/ //low halfword,若地址的低两位为00,则访问低16位;如为10,则访问高16位(详细原因参考后文)
if(haddr_sel[0] == 1'b0) /*修改后*/
sram_csn = 4'b1100; //访问低两片SRAM(低16bit)
else //high halfword
sram_csn = 4'b0011; //访问高两片SRAM(高16bit)
end
else if(hsize_sel == 2'b00) //8bits:byte,访问4片sram中的一片
begin
case(haddr_sel)
2'b00:sram_csn = 4'b1110; //访问最右侧的sram
2'b01:sram_csn = 4'b1101; //访问最右侧左边第一片sram
2'b10:sram_csn = 4'b1011; //访问最左侧右边第一片sram
2'b11:sram_csn = 4'b0111; //访问最左侧的sram
endcase
end
else
sram_csn = 4'b0; //默认32bit数据位宽
end
// Sequential portion, 时序逻辑部分(SRAM 地址和数据要对齐,所以将AHB两拍转一拍)
// tmp the ahb address and control signals
always@(posedge hclk or negedge hresetn) begin
if(!hresetn)
begin
hwrite_r <= 1'b0;
hsize_r <= 3'b0;
hburst_r <= 3'b0;
htrans_r <= 2'b0;
haddr_r <= 32'b0;
end
else if(hsel && hready)
begin
hwrite_r <= hwrite;
hsize_r <= hsize; //由于sram的地址和数据在同一拍,所以需要将AH包的
hburst_r <= hburst; //地址和控制信号寄存一拍,使其与数据对齐
htrans_r <= htrans;
haddr_r <= haddr;
end
else
begin
hwrite_r <= 1'b0;
hsize_r <= 3'b0;
hburst_r <= 3'b0;
htrans_r <= 2'b0;
haddr_r <= 32'b0;
end
end
endmodule
3、sram_core.v
ahb sram_core模块通过ahb slave接口模块转换为sram的数据存取与块、片选指令控制各个sram片的数据进出。
module sram_core(
//input signals
input hclk,
input sram_clk,
input hresetn,
input sram_wen, // =1 读sram; =0,写sram.
input [12:0] sram_addr, //物理地址 = 系统地址 / 4
input [31:0] sram_wdata_in, //data write into sram when "sram_wen_in" active low
input bank_sel, //bank_sel为1代表bank0被访问;bank_sel为0代表bank1被访问
input [3:0] bank0_csn, //两个bank,每个bank有四个片选
input [3:0] bank1_csn,
input bist_en, //BIST test mode//用不上
input dft_en, //DFT test mode//用不上
//output signals
output [7:0] sram_q0,
output [7:0] sram_q1,
output [7:0] sram_q2,
output [7:0] sram_q3,
output [7:0] sram_q4,
output [7:0] sram_q5,
output [7:0] sram_q6,
output [7:0] sram_q7,
output bist_done, //When "bist_done" is high, it shows BIST test is over.
output [7:0] bist_fail //"bist_fail" shows the results of each sram funtions.
);
//data_in sram_csn
reg [7:0] sram_cs0_data_in;
reg [7:0] sram_cs1_data_in;
reg [7:0] sram_cs2_data_in;
reg [7:0] sram_cs3_data_in;
reg [7:0] sram_cs4_data_in;
reg [7:0] sram_cs5_data_in;
reg [7:0] sram_cs6_data_in;
reg [7:0] sram_cs7_data_in;
//Every sram bist's work state and results output.
wire bist_done0;
wire bist_fail0;
wire bist_done1;
wire bist_fail1;
wire bist_done2;
wire bist_fail2;
wire bist_done3;
wire bist_fail3;
wire bist_done4;
wire bist_fail4;
wire bist_done5;
wire bist_fail5;
wire bist_done6;
wire bist_fail6;
wire bist_done7;
wire bist_fail7;
wire bank0_bistdone;
wire bank1_bistdone;
wire [3:0] bank0_bistfail;
wire [3:0] bank1_bistfail;
//bist finishing state of bank0
assign bank0_bistdone = (bist_done3 && bist_done2) && (bist_done1 && bist_done0);
//bist results of bank0
assign bank0_bistfail = {bist_fail3,bist_fail2,bist_fail1,bist_fail0};
//bist finishing state of bank1
assign bank1_bistdone = (bist_done7 && bist_done6) && (bist_done5 && bist_done4);
//bist results of bank1
assign bank1_bistfail = {bist_fail7,bist_fail6,bist_fail5,bist_fail4};
//--------------------------------------------------------------------------
//the 8 srams results of BIST test and the finishing state
//--------------------------------------------------------------------------
assign bist_done = bank0_bistdone && bank1_bistdone;
assign bist_fail = {bank1_bistfail,bank0_bistfail} ;
//write data in bank cs
// assign sram_cs0_data_in = (~sram_wen&~bank0_csn[0]&bank_sel) ?sram_wdata_in[7:0] :{8{1'bz}};
// assign sram_cs1_data_in = (~sram_wen&~bank0_csn[1]&bank_sel) ?sram_wdata_in[15:8] :{8{1'bz}};
// assign sram_cs2_data_in = (~sram_wen&~bank0_csn[2]&bank_sel) ?sram_wdata_in[23:16] :{8{1'bz}};
// assign sram_cs3_data_in = (~sram_wen&~bank0_csn[3]&bank_sel) ?sram_wdata_in[31:24] :{8{1'bz}};
// assign sram_cs4_data_in = (~sram_wen&~bank1_csn[0]&~bank_sel)?sram_wdata_in[7:0] :{8{1'bz}};
// assign sram_cs5_data_in = (~sram_wen&~bank1_csn[1]&~bank_sel)?sram_wdata_in[15:8] :{8{1'bz}};
// assign sram_cs6_data_in = (~sram_wen&~bank1_csn[2]&~bank_sel)?sram_wdata_in[23:16] :{8{1'bz}};
// assign sram_cs7_data_in = (~sram_wen&~bank1_csn[3]&~bank_sel)?sram_wdata_in[31:24] :{8{1'bz}};
//write data in bank cs
always@(*)begin
if(!hresetn)begin
sram_cs0_data_in = 8'd0;
sram_cs1_data_in = 8'd0;
sram_cs2_data_in = 8'd0;
sram_cs3_data_in = 8'd0;
sram_cs4_data_in = 8'd0;
sram_cs5_data_in = 8'd0;
sram_cs6_data_in = 8'd0;
sram_cs7_data_in = 8'd0;
end
else begin
if(bank_sel)begin//bank_sel为1代表bank0被访问;bank_sel为0代表bank1被访问
case(bank0_csn)
//8bit data in
4'b1110:begin
sram_cs0_data_in = sram_wdata_in[7:0];
end
4'b1101:begin
sram_cs1_data_in = sram_wdata_in[7:0];
end
4'b1011:begin
sram_cs2_data_in = sram_wdata_in[7:0];
end
4'b0111:begin
sram_cs3_data_in = sram_wdata_in[7:0];
end
//16bit data in
4'b1100:begin
sram_cs0_data_in = sram_wdata_in[7:0];
sram_cs1_data_in = sram_wdata_in[15:8];
end
4'b0011:begin
sram_cs2_data_in = sram_wdata_in[7:0];
sram_cs3_data_in = sram_wdata_in[15:8];
end
//32bit data in
4'b0000:begin
sram_cs0_data_in = sram_wdata_in[7:0];
sram_cs1_data_in = sram_wdata_in[15:8];
sram_cs2_data_in = sram_wdata_in[23:16];
sram_cs3_data_in = sram_wdata_in[31:24];
end
default:;
endcase
end
else begin
case(bank1_csn)
//8bit data in
4'b1110:begin
sram_cs4_data_in = sram_wdata_in[7:0];
end
4'b1101:begin
sram_cs5_data_in = sram_wdata_in[7:0];
end
4'b1011:begin
sram_cs6_data_in = sram_wdata_in[7:0];
end
4'b0111:begin
sram_cs7_data_in = sram_wdata_in[7:0];
end
//16bit data in
4'b1100:begin
sram_cs4_data_in = sram_wdata_in[7:0];
sram_cs5_data_in = sram_wdata_in[15:8];
end
4'b0011:begin
sram_cs6_data_in = sram_wdata_in[7:0];
sram_cs7_data_in = sram_wdata_in[15:8];
end
//32bit data in
4'b0000:begin
sram_cs4_data_in = sram_wdata_in[7:0];
sram_cs5_data_in = sram_wdata_in[15:8];
sram_cs6_data_in = sram_wdata_in[23:16];
sram_cs7_data_in = sram_wdata_in[31:24];
end
default:;
endcase
end
end
end
//-------------------------------------------------------------------------
//Instance 8 srams and each provides with BIST and DFT functions.
//Bank0 comprises of sram0~sram3, and bank1 comprises of sram4~sram7.
//In each bank, the sram control signals broadcast to each sram, and data
//written per byte into each sram in little-endian style.
//-------------------------------------------------------------------------
//bank0_cs0
sram_bist u_sram_bist0(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank0_csn[0] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs0_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q0 ),
.bist_done (bist_done0 ),
.bist_fail (bist_fail0 )
);
//bank0_cs1
sram_bist u_sram_bist1(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank0_csn[1] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs1_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q1 ),
.bist_done (bist_done1 ),
.bist_fail (bist_fail1 )
);
//bank0_cs2
sram_bist u_sram_bist2(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank0_csn[2] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs2_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q2),
.bist_done (bist_done2),
.bist_fail (bist_fail2)
);
//bank0_cs3
sram_bist u_sram_bist3(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank0_csn[3] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs3_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q3 ),
.bist_done (bist_done3 ),
.bist_fail (bist_fail3 )
);
//bank1_cs4
sram_bist u_sram_bist4(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank1_csn[0] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs4_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q4 ),
.bist_done (bist_done4 ),
.bist_fail (bist_fail4 )
);
//bank1_cs5
sram_bist u_sram_bist5(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank1_csn[1] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs5_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q5 ),
.bist_done (bist_done5 ),
.bist_fail (bist_fail5 )
);
//bank1_cs6
sram_bist u_sram_bist6(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank1_csn[2] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs6_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q6 ),
.bist_done (bist_done6 ),
.bist_fail (bist_fail6 )
);
//bank1_cs7
sram_bist u_sram_bist7(
.hclk (hclk ),
.sram_clk (sram_clk ),
.sram_rst_n (hresetn ),
.sram_csn_in (bank1_csn[3] ),
.sram_wen_in (sram_wen ),
.sram_addr_in (sram_addr ),
.sram_wdata_in (sram_cs7_data_in),
.bist_en (bist_en ),
.dft_en (dft_en ),
.sram_data_out (sram_q7 ),
.bist_done (bist_done7 ),
.bist_fail (bist_fail7 )
);
endmodule
4、sram_bist.v
sram_bist模块如果进行bist与dft测试就转入sram_bist_8kx8内的测试设计,否的话只对数据通过RA1SH模块经行存取。
module sram_bist(
//input signals
input hclk,
input sram_clk,
input sram_rst_n,
input sram_csn_in, //chip select(negative) enable (0 有效)
input sram_wen_in, //sram write or read enable; 0:write; 1:read
input[12:0] sram_addr_in, // 物理地址 8个8K = 2^13
input[7:0 ] sram_wdata_in, // 每个8K x 8bit 的数据位宽
input bist_en, // MBIST mode
input dft_en, // DFT mode
//output signals
output[7:0 ] sram_data_out,
output bist_done, // 1: test over
output bist_fail // high: MBIST Fail
);
//----------------------------------------------------
//Internal signals connected the sram with bist module
//when "bist_en" active high.
//----------------------------------------------------
wire sram_csn;
wire sram_wen;
wire sram_oen;
wire [12:0] sram_a;
wire [7:0] sram_d;
wire [7:0] data_out;
//Sram output data when "dft_en" active high.
wire [7:0] dft_data;
reg [7:0] dft_data_r;
wire [12:0] sram_addr;
wire [7:0] sram_wdata;
//clock for bist logic, when bist is not work, clock should be 0.
wire bist_clk;
genvar K;
//block sram input when cs is diable for low power design
assign sram_addr = sram_csn_in ? 0 : sram_addr_in;
assign sram_wdata = sram_csn_in ? 0 : sram_wdata_in;
//dft test result 具体为什么这么异或,不需要太关注
assign dft_data = (sram_d ^ sram_a[7:0]) ^ {sram_csn, sram_wen, sram_oen, sram_a[12:8]};
always @(posedge hclk or negedge sram_rst_n) begin
if(!sram_rst_n)
dft_data_r <= 0;
else if(dft_en)
dft_data_r <= dft_data;
end
//sram data output
assign sram_data_out = dft_en ? dft_data_r : data_out;
// Note: Need to take place the mux using the special library cell
/*
generate for(K = 0; K < 8; K = K 1 )
begin :hold
//BHDBWP7T holdQ (.Z(data_out[K])); // 作用:把data_out做一个保持 在做DFT的时候是例化标准单元(源语)实现的,没有用RTL方式
end
endgenerate
*/
//clock for bist logic, when bist is not work, clock should be 0.
// Note: Need to take place the mux using the special library cell
// CKMUX2D2BWP7T U_bist_clk_mux (.I0(1'b0), .I1(hclk), .S(bist_en), .Z(bist_clk));
assign bist_clk = bist_en ? hclk : 1'b0;
// One sram with BIST and DFT function
// 在整个SRAM_BIST 中实际上包含了两部分代码,一部分是存储单元,一部分是Memory Bist
// sram_sp_hse_8kx8 : sram singleport high density 8k depth x 8bit width
RA1SH u_RA1SH(
.Q (data_out), // 输出数据端口
.CLK (sram_clk), // hclk 取反
.CEN (sram_csn), // chip select 低有效
.WEN (sram_wen), // 写使能,低有效
.A (sram_a), // Address 地址(要么是读,要么是写) 选择功能地址还是DFT测试地址
.D (sram_d), // Data 数据 从下面的Bist过来的
.OEN (sram_oen) // 没怎么用,只在bist的时候用了一下
);
//测试控制逻辑
sram_bist_8kx8 u_sram_bist_8kx8(
.b_clk (bist_clk), // 同hclk
.b_rst_n (sram_rst_n),
.b_te (bist_en), // 外面给过来的启动使能
//--------------------------------------------------------
//All the input signals will be derectly connected to
//the sram input when in normal operation; and when in
//BIST TEST mode, there are some mux in BIST module
//selcting all sram input signals which generated by itself:
//sram controll signals, sram write data, etc.
//--------------------------------------------------------
// xx_fun 表示从ahb过来的, 需要验证的功能
.addr_fun (sram_addr), // 物理地址 = 系统地址 / 4
.wen_fun (sram_wen_in), // ahb_wen 基础上取反 1读 0写
.cen_fun (sram_csn_in), // ahb的address 和 size 低两比特得到的 csn
// .oen_fun (~sram_wen_in), // 低电平有效,一直打开
.oen_fun (1'b0), // 低电平有效,一直打开
.data_fun (sram_wdata), // 写数据
// 输出不用选,测试电路和功能电路都会送过去
.ram_read_out (sram_data_out), //
.data_test (sram_d),
.addr_test (sram_a), // sram_addr 和 内部产生的addr进行 bist_en 选择之后输出的一个值
.wen_test (sram_wen), // wen 也是通过bist_en选择之后输出的
.cen_test (sram_csn),
.oen_test (sram_oen),
.b_done (bist_done),
.b_fail (bist_fail)
);
endmodule
5、sram_bist_8kx8.v
sram_bist_8kx8模块的大部分内容专门为bist测试与dft测试所设计,如果没有这方面需求可忽略不看。
module sram_bist_8kx8
#(parameter WE_WIDTH = 1,
parameter ADDR_WIDTH = 13,
parameter DATA_WIDTH = 8
)
(
//input signals
input b_clk, // bist clock
input b_rst_n, // bist resetn
input b_te, // bist enable
input [(ADDR_WIDTH-1):0] addr_fun, // Address
input [(WE_WIDTH-1):0] wen_fun, // write enable
input cen_fun, // chip enable
input oen_fun, // ouput enable
input [(DATA_WIDTH-1):0] data_fun, // data input
input [(DATA_WIDTH-1):0] ram_read_out, //RAM data output
//output signals
output [(ADDR_WIDTH-1):0] addr_test, // address of test
output [(WE_WIDTH-1):0] wen_test, // writing control of bist test mode
output cen_test, // chip enable control of bist test mode
output oen_test, // output enable control of bist test mode
output [(DATA_WIDTH-1):0] data_test, // data input of bist test mode
output b_done, // output state of bist test mode
// When "bist_done" is high, it shows BIST test is over.
output reg b_fail // output result of sram function
// When "bist_fail" is high, the sram function is wrong;
// else, the sram function is right.
);
//----------------------------------------------------
//Define 27 work states of BIST block for bist test
//----------------------------------------------------
`define IDEL1 5'b00000
`define P1_WRITE0 5'b00001
`define IDEL2 5'b00010
`define P2_READ0 5'b00011
`define P2_COMPARE0 5'b00100
`define P2_WRITE1 5'b00101
`define IDEL3 5'b00110
`define P3_READ1 5'b00111
`define P3_COMPARE1 5'b01000
`define P3_WRITE0 5'b01001
`define P3_READ0 5'b01010
`define P3_COMPARE0 5'b01011
`define P3_WRITE1 5'b01100
`define IDEL4 5'b01101
`define P4_READ1 5'b01110
`define P4_COMPARE1 5'b01111
`define P4_WRITE0 5'b10000
`define IDEL5 5'b10001
`define P5_READ0 5'b10010
`define P5_COMPARE0 5'b10011
`define P5_WRITE1 5'b10100
`define P5_READ1 5'b10101
`define P5_COMPARE1 5'b10110
`define P5_WRITE0 5'b10111
`define IDEL6 5'b11000
`define P6_READ0 5'b11001
`define P6_COMPARE0 5'b11010
// sram address when in bist test mode
reg [(ADDR_WIDTH-1):0] test_addr;
// bist test end signal
reg r_end;
reg r_end_en;
// sram address reset when in bist test mode.
reg test_addr_rst;
// sram read or write enable signal when in bist test mode
reg [(WE_WIDTH-1):0] wen_test_inner;
// bist start to work in IDLE
reg rf_start;
// compare the data read from sram with the data written into sram
// enable signal
reg check_en;
// bist test data source select signal
// "pattern_sel == 1'b0"-----> test_pattern = 32'b0;
// "pattern_sel == 1'b1"-----> test_pattern = 32'b1;
reg pattern_sel;
wire [(DATA_WIDTH-1):0] test_pattern;
reg [4:0] cstate, nstate;
// 1 -- address is goign upward; 0 -- address is going downward
reg up1_down0;
// 1 -- address is stepping; 0 -- address remains
reg count_en;
//-----------------------------------------------------------------
// Main Code
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// Combinatorial portion
//-----------------------------------------------------------------
assign b_done = r_end;
assign test_pattern = (pattern_sel == 1'b0) ? {DATA_WIDTH{1'b0}} : {DATA_WIDTH{1'b1}};
//--------------------------------------------------------------------
// The output values of all the mux below will be changed based on the
// sram whether in normal operation or in bist test mode.
//---------------------------------------------------------------------
// b_te 打开的话就选择测试一侧的,否则就选择功能一侧的fun
assign data_test = (b_te == 1'b1) ? test_pattern : data_fun;
assign addr_test = (b_te == 1'b1) ? test_addr : addr_fun;
assign wen_test = (b_te == 1'b1) ? wen_test_inner : wen_fun;
assign cen_test = (b_te == 1'b1) ? 1'b0 : cen_fun;
assign oen_test = (b_te == 1'b1) ? 1'b0 : oen_fun;
//--------------------------------------------------------------------
// Sequential portion
//--------------------------------------------------------------------
//--------------------------------
// Generate bist work end signal.
//--------------------------------
always @(posedge b_clk , negedge b_rst_n) begin
if (b_rst_n == 1'b0)
r_end<=1'b0;
else if (r_end_en == 1'b1)
r_end<= 1'b1;
else
r_end <= 1'b0;
end
//----------------------------------------------------
// Generate the sram test address.
// "test_addr_rst " and "up1_down0" decide the mode of
// variable the address(increment/decrement).
//-----------------------------------------------------
always @(posedge b_clk , negedge b_rst_n) begin
if (b_rst_n == 1'b0)
test_addr <= {ADDR_WIDTH{1'b0}};
else if (b_te == 1'b1)
if (test_addr_rst == 1'b1)
if (up1_down0 == 1'b1)
test_addr<= {ADDR_WIDTH{1'b0}};
else
test_addr<= {ADDR_WIDTH{1'b1}};
else
if (count_en == 1'b1)
if (up1_down0 == 1'b1)
test_addr<= test_addr 1'b1; // 地址从小往大扫描
else
test_addr<= test_addr - 1'b1; // 地址从大往小扫描
end
always @(posedge b_clk , negedge b_rst_n)
if (b_rst_n == 1'b0)
b_fail<=1'b1;
else begin
//---------------------------------------------------------
// When in bist idle1 state, "b_fail" defualt value is "0".
// --------------------------------------------------------
if ((b_te == 1'b1) && (rf_start == 1'b1)) // 重新启动清零
b_fail<= 1'b0;
//------------------------------------------------------------
// "b_fail" value is "1", when data read from sram is different
// from the expected data wirtten into sram.
//--------------------------------------------------------------
if ((b_te == 1'b1) && (check_en == 1'b1) && !(test_pattern == ram_read_out)) // 写的跟读出的是否一致
b_fail<= 1'b1;
end
//------------------------------------------------------------------------------
// Bist test state machine(知道了解即可)
// write "0"(initial sram) test_address 0-->1fff
// read "0"------> compare -------->write "1" test_address 1fff-->0
// read "1"------> compare -------->write "0" test_address 0-->1fff
// write "1"------> read "1"-------->compare test_address 1fff-->0
// write "0"------> read "0"-------->compare test_address 0-->1fff
// write "1"------> read "1"-------->compare test_address 1fff-->0
// write "0"------> read "0"-------->compare test_address 0-->1fff
//------------------------------------------------------------------------------
always @(posedge b_clk , negedge b_rst_n) begin
if (b_rst_n == 1'b0)
cstate<=`IDEL1;
else
cstate<= nstate;
end
always @(cstate or b_te or r_end or test_addr) begin
up1_down0 = 1'b1;
count_en = 1'b0;
r_end_en = 1'b0;
pattern_sel = 1'b0;
test_addr_rst = 1'b0;
rf_start = 1'b0;
check_en = 1'b0;
wen_test_inner = {WE_WIDTH{1'b1}};
nstate = cstate;
case (cstate)
`IDEL1 :
begin
test_addr_rst = 1'b1;
if (b_te == 1'b1 && r_end == 1'b0) begin
nstate = `P1_WRITE0;
rf_start = 1'b1;
end
end
`P1_WRITE0 : //initial sram from addr 0~1fff(16K) 做bist的时候不会去考虑低功耗了,看到的就是0-16K
begin
count_en = 1'b1;
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b0;
if (test_addr == {ADDR_WIDTH{1'b1}} ) begin
nstate = `IDEL2;
test_addr_rst = 1'b1;
up1_down0 = 1'b0;
end
end
`IDEL2 :
begin
pattern_sel = 1'b0;
up1_down0 = 1'b0;
test_addr_rst = 1'b1;
nstate = `P2_READ0;
end
`P2_READ0 :
begin
nstate = `P2_COMPARE0;
end
`P2_COMPARE0 : //compare all "0" data after read from addr 0~1fff
begin
pattern_sel = 1'b0;
check_en = 1'b1;
nstate = `P2_WRITE1;
end
`P2_WRITE1 : //all "1" write test from addr 1fff~0
begin
up1_down0 = 1'b0;
count_en = 1'b1;
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b1;
if (test_addr == {ADDR_WIDTH{1'b0}}) begin
nstate = `IDEL3;
test_addr_rst = 1'b1;
up1_down0 = 1'b1;
end
else
nstate = `P2_READ0;
end
`IDEL3 :
begin
pattern_sel = 1'b1;
test_addr_rst = 1'b1;
nstate = `P3_READ1;
end
`P3_READ1 :
begin
nstate = `P3_COMPARE1;
end
`P3_COMPARE1 : //compare all "1" data after read from addr 1fff~0
begin
pattern_sel = 1'b1;
check_en = 1'b1;
nstate = `P3_WRITE0;
end
`P3_WRITE0 :
begin
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b0;
nstate = `P3_READ0;
end
`P3_READ0 :
begin
nstate = `P3_COMPARE0;
end
`P3_COMPARE0 :
begin
pattern_sel = 1'b0;
check_en = 1'b1;
nstate = `P3_WRITE1;
end
`P3_WRITE1 :
begin
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b1;
count_en = 1'b1;
if (test_addr == {ADDR_WIDTH{1'b1}}) begin
nstate = `IDEL4;
test_addr_rst = 1'b1;
end
else
nstate = `P3_READ1;
end
`IDEL4 : // read all data from addr 1fff~0 and compare with write data "1" every time
begin
pattern_sel = 1'b1;
test_addr_rst = 1'b1;
nstate = `P4_READ1;
end
`P4_READ1 :
begin
nstate = `P4_COMPARE1;
end
`P4_COMPARE1 :
begin
pattern_sel = 1'b1;
check_en = 1'b1;
nstate = `P4_WRITE0;
end
`P4_WRITE0 :
begin
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b0;
count_en = 1'b1;
if (test_addr == {ADDR_WIDTH{1'b1}}) begin
nstate = `IDEL5;
test_addr_rst = 1'b1;
end
else
nstate = `P4_READ1;
end
`IDEL5 : // read all data from addr 1fff~0 and compare with write data "0" every time
begin
pattern_sel = 1'b1;
test_addr_rst = 1'b1;
nstate = `P5_READ0;
end
`P5_READ0 :
begin
nstate = `P5_COMPARE0;
end
`P5_COMPARE0 :
begin
pattern_sel=1'b0;
check_en=1'b1;
nstate = `P5_WRITE1;
end
`P5_WRITE1 :
begin
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b1;
nstate = `P5_READ1;
end
`P5_READ1 :
begin
nstate = `P5_COMPARE1;
end
`P5_COMPARE1 :
begin
pattern_sel=1'b1;
check_en=1'b1;
nstate = `P5_WRITE0;
end
`P5_WRITE0 :
begin
wen_test_inner = {WE_WIDTH{1'b0}};
pattern_sel = 1'b0;
count_en = 1'b1;
if (test_addr == {ADDR_WIDTH{1'b1}}) begin
nstate = `IDEL6;
test_addr_rst = 1'b1;
end
else
nstate = `P5_READ0;
end
`IDEL6 :
begin
pattern_sel = 1'b0;
test_addr_rst = 1'b1;
nstate = `P6_READ0;
end
`P6_READ0 :
begin
nstate = `P6_COMPARE0;
end
`P6_COMPARE0 :
begin
pattern_sel = 1'b0;
check_en = 1'b1;
count_en = 1'b1;
if (test_addr == {ADDR_WIDTH{1'b1}}) begin
nstate = `IDEL1;
test_addr_rst = 1'b1;
r_end_en = 1'b1;
end
else
nstate = `P6_READ0;
end
default :
begin
nstate = `IDEL1;
test_addr_rst = 1'b1;
end
endcase
end
endmodule
5、RA1SH.v
RA1SH模块应该为封装的sram IP,主要内容为mem_cycle这个任务,是通过读写使能控制mem对输入地址进行存取,然后后面是一堆路径延迟设计、建立与保持时间检测等。
`celldefine
module RA1SH ( //8K
Q, //data_out [7:0]
CLK, //时钟 hclk 取反
CEN, //chip select 低有效
WEN, //读写使能、// 写使能,低有效
A, //读写地址 [12:0]
D, //data_in [7:0]
OEN //
);
parameter BITS = 8; // 数据位宽 8 bit
parameter word_depth = 8192; // 8K = 8 x 1024
parameter addr_width = 13;
parameter wordx = {BITS{1'bx}}; // x 态
parameter addrx = {addr_width{1'bx}};
output [BITS-1:0] Q;
input CLK;
input CEN;
input WEN;
input [addr_width-1:0] A;
input [BITS-1:0] D;
input OEN;
reg [BITS-1:0] mem [word_depth-1:0]; // SRAM 的行为本质上就是一个数组的行为 [word_depth-1:0]为深度 [BITS-1:0]为宽度
reg NOT_CEN; // NOT 表示取反 为什么取反?因为代码中会用到一些BUFFER
reg NOT_WEN;
reg NOT_A0;
reg NOT_A1;
reg NOT_A2;
reg NOT_A3;
reg NOT_A4;
reg NOT_A5;
reg NOT_A6;
reg NOT_A7;
reg NOT_A8;
reg NOT_A9;
reg NOT_A10;
reg NOT_A11;
reg NOT_A12;
reg [addr_width-1:0] NOT_A;
reg NOT_D0;
reg NOT_D1;
reg NOT_D2;
reg NOT_D3;
reg NOT_D4;
reg NOT_D5;
reg NOT_D6;
reg NOT_D7;
reg [BITS-1:0] NOT_D ;
reg NOT_CLK_PER;
reg NOT_CLK_MINH;
reg NOT_CLK_MINL;
reg LAST_NOT_CEN;
reg LAST_NOT_WEN;
reg [addr_width-1:0] LAST_NOT_A;
reg [BITS-1:0] LAST_NOT_D;
reg LAST_NOT_CLK_PER;
reg LAST_NOT_CLK_MINH;
reg LAST_NOT_CLK_MINL;
wire [BITS-1:0] _Q;
wire _OENi;
wire [addr_width-1:0] _A;
wire _CLK;
wire _CEN;
wire _OEN;
wire _WEN;
wire [BITS-1:0] _D;
wire re_flag;
wire re_data_flag;
reg LATCHED_CEN;
reg LATCHED_WEN;
reg [addr_width-1:0] LATCHED_A;
reg [BITS-1:0] LATCHED_D;
reg CENi;
reg WENi;
reg [addr_width-1:0] Ai;
reg [BITS-1:0] Di;
reg [BITS-1:0] Qi;
reg [BITS-1:0] LAST_Qi;
reg LAST_CLK;
task update_notifier_buses;
begin
NOT_A = {
NOT_A12,
NOT_A11,
NOT_A10,
NOT_A9,
NOT_A8,
NOT_A7,
NOT_A6,
NOT_A5,
NOT_A4,
NOT_A3,
NOT_A2,
NOT_A1,
NOT_A0};
NOT_D = {
NOT_D7,
NOT_D6,
NOT_D5,
NOT_D4,
NOT_D3,
NOT_D2,
NOT_D1,
NOT_D0};
end
endtask
task mem_cycle;
begin
casez({WENi,CENi})//WENi 0:写、1:读 CENi:片选(低有效)
2'b10: begin//读状态
read_mem(1,0);//读数据输出
end
2'b00: begin//写状态
write_mem(Ai,Di);//写入数据
read_mem(0,0);//将写入数据显示在读数据输出
end
2'b?1: ;
2'b1x: begin
read_mem(0,1); //读出不定态数据
end
2'bx0: begin
write_mem_x(Ai);//写入不定态数据
read_mem(0,1); //读出不定态数据
end
2'b0x,
2'bxx: begin
write_mem_x(Ai);//写入不定态数据
read_mem(0,1); //读出不定态数据
end
endcase
end
endtask
task update_last_notifiers;
begin
LAST_NOT_A = NOT_A;
LAST_NOT_D = NOT_D;
LAST_NOT_WEN = NOT_WEN;
LAST_NOT_CEN = NOT_CEN;
LAST_NOT_CLK_PER = NOT_CLK_PER;
LAST_NOT_CLK_MINH = NOT_CLK_MINH;
LAST_NOT_CLK_MINL = NOT_CLK_MINL;
end
endtask
task latch_inputs;
begin
LATCHED_A = _A ;
LATCHED_D = _D ;
LATCHED_WEN = _WEN ;
LATCHED_CEN = _CEN ;
LAST_Qi = Qi;
end
endtask
task update_logic;
begin
CENi = LATCHED_CEN;
WENi = LATCHED_WEN;
Ai = LATCHED_A;
Di = LATCHED_D;
end
endtask
task x_inputs;
integer n;
begin
for (n=0; n<addr_width; n=n 1)begin
LATCHED_A[n] = (NOT_A[n]!==LAST_NOT_A[n]) ? 1'bx : LATCHED_A[n] ;
end
for (n=0; n<BITS; n=n 1)begin
LATCHED_D[n] = (NOT_D[n]!==LAST_NOT_D[n]) ? 1'bx : LATCHED_D[n] ;
end
LATCHED_WEN = (NOT_WEN!==LAST_NOT_WEN) ? 1'bx : LATCHED_WEN ;
LATCHED_CEN = (NOT_CEN!==LAST_NOT_CEN) ? 1'bx : LATCHED_CEN ;
end
endtask
task read_mem;
input r_wb;
input xflag;
begin
if (r_wb)begin
if (valid_address(Ai))begin
Qi=mem[Ai];
end
else begin
Qi=wordx;
end
end
else begin
if (xflag)begin
Qi=wordx;
end
else begin
Qi=Di;
end
end
end
endtask
task write_mem;
input [addr_width-1:0] a;
input [BITS-1:0] d;
begin
casez({valid_address(a)})
1'b0:x_mem;
1'b1: mem[a]=d;
endcase
end
endtask
task write_mem_x;
input [addr_width-1:0] a;
begin
casez({valid_address(a)})//检查地址是否有效
1'b0:x_mem;
1'b1: mem[a]=wordx;
endcase
end
endtask
task x_mem;
integer n;
begin
for (n=0; n<word_depth; n=n 1)
mem[n]=wordx;
end
endtask
task process_violations;//主功能
begin
if ((NOT_CLK_PER!==LAST_NOT_CLK_PER) ||
(NOT_CLK_MINH!==LAST_NOT_CLK_MINH) ||
(NOT_CLK_MINL!==LAST_NOT_CLK_MINL))begin
if (CENi !== 1'b1)begin
x_mem;
read_mem(0,1);
end
end
else begin
update_notifier_buses;
x_inputs;
update_logic;
mem_cycle;
end
update_last_notifiers;
end
endtask
function valid_address;
input [addr_width-1:0] a;
begin
valid_address = (^(a) !== 1'bx);
end
endfunction
bufif0 (Q[0], _Q[0], _OENi);//三态门bufif0(out, in, ctrl)enable-->ctrl=0
bufif0 (Q[1], _Q[1], _OENi);//三态门bufif1(out, in, ctrl)enable-->ctrl=1
bufif0 (Q[2], _Q[2], _OENi);
bufif0 (Q[3], _Q[3], _OENi);
bufif0 (Q[4], _Q[4], _OENi);
bufif0 (Q[5], _Q[5], _OENi);
bufif0 (Q[6], _Q[6], _OENi);
bufif0 (Q[7], _Q[7], _OENi);
buf (_D[0], D[0]);//多输出门buf(out1, out2,..., in);允许有多个输出,但只有一个输入
buf (_D[1], D[1]);
buf (_D[2], D[2]);
buf (_D[3], D[3]);
buf (_D[4], D[4]);
buf (_D[5], D[5]);
buf (_D[6], D[6]);
buf (_D[7], D[7]);
buf (_A[0], A[0]);
buf (_A[1], A[1]);
buf (_A[2], A[2]);
buf (_A[3], A[3]);
buf (_A[4], A[4]);
buf (_A[5], A[5]);
buf (_A[6], A[6]);
buf (_A[7], A[7]);
buf (_A[8], A[8]);
buf (_A[9], A[9]);
buf (_A[10], A[10]);
buf (_A[11], A[11]);
buf (_A[12], A[12]);
buf (_CLK, CLK);
buf (_WEN, WEN);
buf (_OEN, OEN);
buf (_CEN, CEN);
assign _OENi = _OEN;
assign _Q = Qi;
assign re_flag = !(_CEN);
assign re_data_flag = !(_CEN || _WEN);
always @( // Verilog 95 语法
NOT_A0 or // 13位地址、8位数据分为单bit的信号 (写仿真模型的一般操作)
NOT_A1 or
NOT_A2 or
NOT_A3 or
NOT_A4 or
NOT_A5 or
NOT_A6 or
NOT_A7 or
NOT_A8 or
NOT_A9 or
NOT_A10 or
NOT_A11 or
NOT_A12 or
NOT_D0 or
NOT_D1 or
NOT_D2 or
NOT_D3 or
NOT_D4 or
NOT_D5 or
NOT_D6 or
NOT_D7 or
NOT_WEN or
NOT_CEN or
NOT_CLK_PER or
NOT_CLK_MINH or
NOT_CLK_MINL
)begin
process_violations; // 时序不满足,但是前端仿真一般不会去管时序,只管功能!
end
always@( _CLK )begin // 时钟检测
casez({LAST_CLK,_CLK})
2'b01: begin
latch_inputs;
update_logic;
mem_cycle;
end
2'b10,
2'bx?,
2'b00,
2'b11: ;
2'b?x: begin
x_mem;
read_mem(0,1);
end
endcase
LAST_CLK = _CLK;
end
specify //路径延迟块
$setuphold(posedge CLK, CEN, 1.000, 0.500, NOT_CEN); // $setuphold 检查 setup 和 hpld 的系统函数,但是我们一般会将其关闭
$setuphold(posedge CLK &&& re_flag, WEN, 1.000, 0.500, NOT_WEN);
$setuphold(posedge CLK &&& re_flag, A[0], 1.000, 0.500, NOT_A0);
$setuphold(posedge CLK &&& re_flag, A[1], 1.000, 0.500, NOT_A1);
$setuphold(posedge CLK &&& re_flag, A[2], 1.000, 0.500, NOT_A2);
$setuphold(posedge CLK &&& re_flag, A[3], 1.000, 0.500, NOT_A3);
$setuphold(posedge CLK &&& re_flag, A[4], 1.000, 0.500, NOT_A4);
$setuphold(posedge CLK &&& re_flag, A[5], 1.000, 0.500, NOT_A5);
$setuphold(posedge CLK &&& re_flag, A[6], 1.000, 0.500, NOT_A6);
$setuphold(posedge CLK &&& re_flag, A[7], 1.000, 0.500, NOT_A7);
$setuphold(posedge CLK &&& re_flag, A[8], 1.000, 0.500, NOT_A8);
$setuphold(posedge CLK &&& re_flag, A[9], 1.000, 0.500, NOT_A9);
$setuphold(posedge CLK &&& re_flag, A[10], 1.000, 0.500, NOT_A10);
$setuphold(posedge CLK &&& re_flag, A[11], 1.000, 0.500, NOT_A11);
$setuphold(posedge CLK &&& re_data_flag, D[0], 1.000, 0.500, NOT_D0);
$setuphold(posedge CLK &&& re_data_flag, D[1], 1.000, 0.500, NOT_D1);
$setuphold(posedge CLK &&& re_data_flag, D[2], 1.000, 0.500, NOT_D2);
$setuphold(posedge CLK &&& re_data_flag, D[3], 1.000, 0.500, NOT_D3);
$setuphold(posedge CLK &&& re_data_flag, D[4], 1.000, 0.500, NOT_D4);
$setuphold(posedge CLK &&& re_data_flag, D[5], 1.000, 0.500, NOT_D5);
$setuphold(posedge CLK &&& re_data_flag, D[6], 1.000, 0.500, NOT_D6);
$setuphold(posedge CLK &&& re_data_flag, D[7], 1.000, 0.500, NOT_D7);
$period(posedge CLK, 3.000, NOT_CLK_PER);
$width(posedge CLK, 1.000, 0, NOT_CLK_MINH);
$width(negedge CLK, 1.000, 0, NOT_CLK_MINL);
/
//在路径延时中可以说明6个延时值(0->1, 1->0, 0->Z, Z->1, 1->Z, Z->0)
//在路径延时中说明所有12个延时值(0->1, 1->0, 0->Z, Z->1, 1->Z, Z->0, 0->X, X->1, 1->X, X->0, X->Z, Z->X)
//
//上升延时是输出转换为1时的延时(0->1,Z->1,X->1)
//下降延时是输出转换为0时的延时(1->0,Z->0,X->0)
//关断延时输出转换为三态Z时的延时(0->Z,1->Z,X->Z,)
//到X的转换延时是最小延时,而从X到其它值的转换使用最坏(长)延时
//
//如果只说明了一个延时,则所有转换使用这个延时。
//如果只说明了上升和下降延时,则 1->X 和 X->0 使用下降延时, X->Z 使用上升和下降延时的最小延时
//如果说明了六个延时,则 1->X 使用 1->X 和 1->Z 中最小延时; X->0 使用 1->0 和 X->0 的最大延时; X->Z 使用 1->Z 和 0->Z 中的最大延时。
(CLK => Q[0])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[1])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[2])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[3])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[4])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[5])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[6])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(CLK => Q[7])=(1.000, 1.000, 0.500, 1.000, 0.500, 1.000);
(OEN => Q[0])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[1])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[2])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[3])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[4])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[5])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[6])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
(OEN => Q[7])=(1.000, 1.000, 1.000, 1.000, 1.000, 1.000);
endspecify
endmodule
`endcelldefine
TestBench设计
1、tb_sramc_top.sv
TB模块可以定义数据位宽(DATA_SIZE)与开始存取地址(START_ADDR),设计了边读边写(direct_write_during_read)任务与写定量读空(loop_wr_rd_data)任务,数据则由random_data类随机产生。如果对数字ic验证感兴趣,还可以了解基于UVM的AHB总线SRAM控制器设计验证平台设计这个资源。
`define START_ADDR 8192//32bit:0~2*8192-1、16bit:0~4*8192-1、8bit:0~8*8192-1
`define DATA_SIZE 16
`define IS_SEQ 1 //1:SEQ write、read 0:NOSEQ write、read(STL中两种都一样)
class random_data;
rand bit [`DATA_SIZE-1:0] data;
rand bit [`DATA_SIZE-1:0] delay;
constraint c_delay{
delay <=50;
}
endclass
module tb_sramc_top();
//interface define
reg hclk ;//产生时钟信号
wire sram_clk ;//hclk 的反向,与hclk属于同一个时钟沿
reg hresetn ;//复位
reg hsel ;//选中该slave
reg hwrite ;//读写模式0:读、1:写
reg [1:0] htrans ;//传输是否有效00:空闲、01:忙、10:非连续、11:连续
reg [2:0] hsize ;//有效传输位00:8bit、01:16bit、10:32bit
reg hready ;// master -> slave,一般接常高
reg [31:0] haddr ;//本次命令访问的地址
reg [31:0] hwdata ;// 写数据
wire [31:0] hrdata ;// 从sram读出的数据
wire hready_resp;// slave -> master,看 slave 是否ready
wire [1:0] hresp ;// hresp 也只会返回0,即ok状态。
reg [`DATA_SIZE-1:0] rdata ;//读出数据
reg r_data_en;
static int wr_iter = 0 ;
static int rd_iter = 0 ;
always #10 hclk = ~hclk;
assign sram_clk = ~hclk;
random_data rm_data;
initial begin
hclk =1;
hresetn = 0;
#200
hresetn = 1;
end
initial begin:process
rm_data = new();
direct_write_during_read(16'd8);
#200;
loop_wr_rd_data(16'd18);
$finish;
end
task ahb_init();
hsel = 1'b0 ;//未中该slave
hwrite = 1'b1 ;//写
htrans = `IS_SEQ?2'b11:2'b10;
hsize = (`DATA_SIZE==32)?2'b10:((`DATA_SIZE==16)?2'b01:((`DATA_SIZE==8)?2'b00:2'b10));//00:8bit、01:16bit、10:32bit、11:32bit
hready = 1'b1;
haddr = 32'd0;
hwdata = 32'd0;
rdata = 32'd0;
r_data_en = 1'b0;
wait(hresetn);
repeat(3)@(posedge sram_clk);
endtask
task write_data;
input [15:0] wr_nums;
begin
repeat(wr_nums)begin
@(posedge hclk);
rm_data.randomize();
hsel = 1'b1 ;//选中该slave
hwrite = 1'b1 ;//写
haddr = `START_ADDR wr_iter;
wr_iter = wr_iter 1;
@(posedge hclk);
hwdata = rm_data.data;
hsel = 1;
end
end
endtask
task read_data;
input [15:0] rd_nums;
begin
repeat(rd_nums)begin
@(posedge sram_clk);
hsel = 1'b1 ;//选中该slave
hwrite = 1'b0;//read
haddr = `START_ADDR rd_iter;//bank1 cs0
rd_iter = rd_iter 1;
@(posedge sram_clk);
hsel = 1'b0 ;
//@(posedge hclk);
rdata <= hrdata[`DATA_SIZE-1:0];
end
end
endtask
task direct_write_during_read;
input [15:0] wr_nums;//读写次数
begin
ahb_init();
repeat(wr_nums)begin
write_data(1);
read_data(1);
end
@(posedge sram_clk);
@(posedge hclk);
ahb_init();
#200;
end
endtask
task loop_wr_rd_data;
input [15:0] wr_nums;
begin
ahb_init();
write_data(wr_nums);
#rm_data.delay;
read_data(wr_nums);
@(posedge sram_clk);
@(posedge hclk);
ahb_init();
#200;
end
endtask
sramc_top u_sramc_top(
.hclk (hclk ), //input
.sram_clk (sram_clk ), //input
.hresetn (hresetn ), //input
.hsel (hsel ), //input
.hwrite (hwrite ), //input
.htrans (htrans ), //input [1:0]
.hsize (hsize ), //input [2:0]
.hready (hready ), //input
.haddr (haddr ), //input [31:0]
.hwdata (hwdata ), //input [31:0]
.hrdata (hrdata ), //output [31:0]
.hready_resp (hready_resp), //output
.hresp (hresp ), //output [1:0]
.hburst (3'b0), //burst没用的话就接0,在tr里面激励产生什么都关系不大了
.dft_en (1'b0), //不测 dft不测,写成0
.bist_en (1'b0), //不测
.bist_done ( ), //不测
.bist_fail ( ) //不测
);
endmodule
2、仿真
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgkiijh
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13