基于 AHB 总线的 SRAM 控制器设计

  • 一、基于 AHB 的 sram 设计
    • 1、总体设计框架
    • 2、AHB总线传输协议
      • ①没有等待状态的单个读写操作
      • ②有等待状态的单个读写操作
      • ③连续读写操作
  • 二、数据读写位宽与深度、块选与片选控制
    • 1、hsize控制读写数据位宽与数据深度(默认位宽为32bit,深度为2^14)
    • 2、当hsize为2'b00时,数据位宽为8bit,数据深度为2^16
    • 3、当hsize为2'b01时,数据位宽为16bit,数据深度为2^15
    • 4、当hsize为2'b10时,数据位宽为32bit,数据深度为2^14
  • 三、程序设计
    • RTL设计
      • 1、sramc_top.v
      • 2、ahb_slave_if.v
      • 3、sram_core.v
      • 4、sram_bist.v
      • 5、sram_bist_8kx8.v
      • 5、RA1SH.v
    • TestBench设计
      • 1、tb_sramc_top.sv
      • 2、仿真

一、基于 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 signalsinput wire           hclk,input wire         sram_clk,   // hclk 的反向,与hclk属于同一个时钟沿input wire          hresetn,    // 复位input wire         hsel,       // AHB-Slave 有多个,此处对应的是AHB-SRAM的hselinput 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 signalsoutput 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_csnwire       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 sramswire [31:0] sram_wdata;//sram data output data which selected and read by AHB buswire [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 operationinput  hclk,input  hresetn,// signals from AHB bus during normal operationinput  hsel,                   //hsel恒为1,表示选中该SRAMCinput  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_outoutput  [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 independentlyoutput  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 signalsreg         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^15reg   [31:0] sram_data_out;  //从sram发出的读数据信号,发送至AHB总线// transfer type signal encodingparameter  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 busassign  hrdata = sram_data_out;  //由sram存储单元输出数据,经hrdata导出至AHB总线,支持8/16/32bit位// Generate sram write and read enable signalsassign  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 3assign  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@(*)beginif(sram_csn_en)begincase(hsize_sel)2'b00:begin//8bitbank0_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;end2'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;               end2'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;                enddefault: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;                 endendcaseendelse beginbank0_csn = 4'b1111;bank1_csn = 4'b1111;bank_sel  = 1'b1;endend// 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@(*)beginif(!hresetn)beginsram_data_out = {32{SUB_DATA}};endelse if(sram_read)beginif(bank_sel)begincase(hsize_sel)2'b00:begin//data size 8bitcase(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};endcaseend2'b01:begin//data size 16bitcase(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                   end2'b10:sram_data_out = {sram_q3,sram_q2,sram_q1,sram_q0};//data size 32bitdefault:begin sram_data_out = {sram_q3,sram_q2,sram_q1,sram_q0};//data size 32bit                  endendcaseendelse begincase(hsize_sel)2'b00:begin//data size 8bitcase(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};endcaseend2'b01:begin//data size 16bitcase(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                   end2'b10:sram_data_out = {sram_q7,sram_q6,sram_q5,sram_q4};//data size 32bitdefault:begin sram_data_out = {sram_q7,sram_q6,sram_q5,sram_q4};//data size 32bit                   endendcase           endendelse beginsram_data_out = sram_data_out;endend// 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@(*) beginif(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 halfwordsram_csn = 4'b0011;         //访问高两片SRAM(高16bit)endelse if(hsize_sel == 2'b00)       //8bits:byte,访问4片sram中的一片begincase(haddr_sel)2'b00:sram_csn = 4'b1110;    //访问最右侧的sram2'b01:sram_csn = 4'b1101;    //访问最右侧左边第一片sram2'b10:sram_csn = 4'b1011;    //访问最左侧右边第一片sram2'b11:sram_csn = 4'b0111;    //访问最左侧的sramendcaseendelsesram_csn = 4'b0;      //默认32bit数据位宽end// Sequential portion,     时序逻辑部分(SRAM 地址和数据要对齐,所以将AHB两拍转一拍)
// tmp the ahb address and control signalsalways@(posedge hclk or negedge hresetn) beginif(!hresetn)beginhwrite_r <= 1'b0;hsize_r  <= 3'b0;hburst_r <= 3'b0;         htrans_r <= 2'b0;haddr_r  <= 32'b0;endelse if(hsel && hready)beginhwrite_r <= hwrite;hsize_r  <= hsize;       //由于sram的地址和数据在同一拍,所以需要将AH包的hburst_r <= hburst;      //地址和控制信号寄存一拍,使其与数据对齐htrans_r <= htrans;haddr_r  <= haddr;endelsebeginhwrite_r <= 1'b0;hsize_r  <= 3'b0;hburst_r <= 3'b0;         htrans_r <= 2'b0;haddr_r  <= 32'b0;endendendmodule

3、sram_core.v

    ahb sram_core模块通过ahb slave接口模块转换为sram的数据存取与块、片选指令控制各个sram片的数据进出。

module sram_core(//input signalsinput                hclk,input              sram_clk,input              hresetn,input               sram_wen,        // =1 读sram; =0,写sram.input  [12:0]      sram_addr,       //物理地址 = 系统地址 / 4input    [31:0]      sram_wdata_in,   //data write into sram when "sram_wen_in" active lowinput               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 signalsoutput [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_csnreg [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 bank0assign bank0_bistdone = (bist_done3 && bist_done2) && (bist_done1 && bist_done0);//bist results of bank0assign bank0_bistfail = {bist_fail3,bist_fail2,bist_fail1,bist_fail0};//bist finishing state of bank1assign bank1_bistdone = (bist_done7 && bist_done6) && (bist_done5 && bist_done4);//bist results of bank1assign 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@(*)beginif(!hresetn)beginsram_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 beginif(bank_sel)begin//bank_sel为1代表bank0被访问;bank_sel为0代表bank1被访问case(bank0_csn)//8bit data in 4'b1110:beginsram_cs0_data_in = sram_wdata_in[7:0];end4'b1101:beginsram_cs1_data_in = sram_wdata_in[7:0];end4'b1011:beginsram_cs2_data_in = sram_wdata_in[7:0];end4'b0111:beginsram_cs3_data_in = sram_wdata_in[7:0];end               //16bit data in 4'b1100:beginsram_cs0_data_in = sram_wdata_in[7:0];sram_cs1_data_in = sram_wdata_in[15:8];end4'b0011:beginsram_cs2_data_in = sram_wdata_in[7:0];sram_cs3_data_in = sram_wdata_in[15:8];end  //32bit data in              4'b0000:beginsram_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:;              endcaseendelse begincase(bank1_csn)//8bit data in 4'b1110:beginsram_cs4_data_in = sram_wdata_in[7:0];end4'b1101:beginsram_cs5_data_in = sram_wdata_in[7:0];end4'b1011:beginsram_cs6_data_in = sram_wdata_in[7:0];end4'b0111:beginsram_cs7_data_in = sram_wdata_in[7:0];end               //16bit data in 4'b1100:beginsram_cs4_data_in = sram_wdata_in[7:0];sram_cs5_data_in = sram_wdata_in[15:8];end4'b0011:beginsram_cs6_data_in = sram_wdata_in[7:0];sram_cs7_data_in = sram_wdata_in[15:8];end  //32bit data in              4'b0000:beginsram_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:;              endcaseendendend//-------------------------------------------------------------------------//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_cs0sram_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_cs1sram_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_cs2sram_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_cs3sram_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_cs4sram_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_cs5sram_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_cs6sram_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 signalsinput         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:readinput[12:0]   sram_addr_in,  // 物理地址 8个8K = 2^13input[7:0 ]   sram_wdata_in, // 每个8K x 8bit 的数据位宽input         bist_en,       // MBIST modeinput         dft_en,      // DFT mode//output signalsoutput[7:0 ]  sram_data_out, output        bist_done,     // 1: test overoutput        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) beginif(!sram_rst_n)dft_data_r <= 0;else if(dft_en)dft_data_r <= dft_data;end//sram data outputassign 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 widthRA1SH 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 signalsinput                      b_clk,    // bist clock   input                      b_rst_n,  // bist resetninput                      b_te,     // bist enableinput [(ADDR_WIDTH-1):0]   addr_fun, // Addressinput [(WE_WIDTH-1):0]     wen_fun,  // write enableinput                      cen_fun,  // chip enableinput                      oen_fun,  // ouput enableinput [(DATA_WIDTH-1):0]   data_fun, // data inputinput [(DATA_WIDTH-1):0]   ram_read_out, //RAM data output//output signalsoutput [(ADDR_WIDTH-1):0]  addr_test, // address of testoutput [(WE_WIDTH-1):0]    wen_test,  // writing control of bist test modeoutput                     cen_test,  // chip enable control of bist test modeoutput                     oen_test,  // output enable control of bist test modeoutput [(DATA_WIDTH-1):0]  data_test, // data input of bist test modeoutput                  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 modereg [(ADDR_WIDTH-1):0] test_addr;// bist test end signalreg 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 modereg [(WE_WIDTH-1):0] wen_test_inner;// bist start to work in IDLEreg rf_start;// compare the data read from sram with the data written into sram // enable signalreg 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 downwardreg up1_down0; // 1 -- address is stepping; 0 -- address remainsreg 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 打开的话就选择测试一侧的,否则就选择功能一侧的funassign 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) beginif (b_rst_n == 1'b0) r_end<=1'b0;else if (r_end_en == 1'b1) r_end<= 1'b1;elser_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) beginif (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}};elsetest_addr<=  {ADDR_WIDTH{1'b1}};elseif (count_en == 1'b1)if (up1_down0 == 1'b1)test_addr<=  test_addr + 1'b1; // 地址从小往大扫描elsetest_addr<=  test_addr - 1'b1; // 地址从大往小扫描endalways @(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) beginif (b_rst_n == 1'b0) cstate<=`IDEL1;elsecstate<= nstate;endalways @(cstate or b_te or r_end or test_addr) beginup1_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 :begintest_addr_rst = 1'b1;if (b_te == 1'b1 && r_end == 1'b0) beginnstate   = `P1_WRITE0;rf_start = 1'b1;endend`P1_WRITE0 :   //initial sram from addr 0~1fff(16K) 做bist的时候不会去考虑低功耗了,看到的就是0-16Kbegincount_en       = 1'b1;wen_test_inner = {WE_WIDTH{1'b0}};pattern_sel    = 1'b0;if (test_addr == {ADDR_WIDTH{1'b1}} ) beginnstate        = `IDEL2;test_addr_rst = 1'b1;up1_down0     = 1'b0;endend`IDEL2 :beginpattern_sel   = 1'b0;up1_down0     = 1'b0;test_addr_rst = 1'b1; nstate        = `P2_READ0;end`P2_READ0 :beginnstate = `P2_COMPARE0;end`P2_COMPARE0 :  //compare all "0" data after read from addr 0~1fffbeginpattern_sel = 1'b0;check_en    = 1'b1;nstate      = `P2_WRITE1;end`P2_WRITE1 :  //all "1" write test from addr 1fff~0beginup1_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}}) beginnstate        = `IDEL3;test_addr_rst = 1'b1;up1_down0     = 1'b1;endelsenstate        = `P2_READ0;end`IDEL3 :beginpattern_sel   = 1'b1;test_addr_rst = 1'b1;nstate        = `P3_READ1;end`P3_READ1 :beginnstate = `P3_COMPARE1;end`P3_COMPARE1 :  //compare all "1" data after read from addr 1fff~0beginpattern_sel = 1'b1;check_en    = 1'b1;nstate      = `P3_WRITE0;end`P3_WRITE0 :beginwen_test_inner = {WE_WIDTH{1'b0}};pattern_sel    = 1'b0;nstate         = `P3_READ0;end`P3_READ0 :beginnstate = `P3_COMPARE0;end`P3_COMPARE0 :beginpattern_sel = 1'b0;check_en    = 1'b1;nstate      = `P3_WRITE1;end`P3_WRITE1 :beginwen_test_inner = {WE_WIDTH{1'b0}};pattern_sel    = 1'b1;count_en       = 1'b1;if (test_addr == {ADDR_WIDTH{1'b1}}) beginnstate        = `IDEL4;test_addr_rst = 1'b1;endelsenstate        = `P3_READ1;end`IDEL4 :   // read all data from addr 1fff~0 and compare with write data "1" every time beginpattern_sel   = 1'b1;test_addr_rst = 1'b1;nstate        = `P4_READ1;end`P4_READ1 :beginnstate = `P4_COMPARE1;end`P4_COMPARE1 :beginpattern_sel = 1'b1;check_en    = 1'b1;nstate      = `P4_WRITE0;end`P4_WRITE0 :beginwen_test_inner = {WE_WIDTH{1'b0}};pattern_sel    = 1'b0;count_en       = 1'b1;if (test_addr == {ADDR_WIDTH{1'b1}}) beginnstate        = `IDEL5;test_addr_rst = 1'b1;endelse         nstate        = `P4_READ1;end`IDEL5 :  // read all data from addr 1fff~0 and compare with write data "0" every time beginpattern_sel   = 1'b1;test_addr_rst = 1'b1;nstate        = `P5_READ0;end`P5_READ0 :beginnstate = `P5_COMPARE0;end`P5_COMPARE0 :beginpattern_sel=1'b0;check_en=1'b1;nstate = `P5_WRITE1;end`P5_WRITE1 :beginwen_test_inner = {WE_WIDTH{1'b0}};pattern_sel   = 1'b1;nstate = `P5_READ1;end`P5_READ1 :beginnstate = `P5_COMPARE1;end`P5_COMPARE1 :beginpattern_sel=1'b1;check_en=1'b1;nstate = `P5_WRITE0;end`P5_WRITE0 :beginwen_test_inner = {WE_WIDTH{1'b0}};pattern_sel    = 1'b0;count_en       = 1'b1;if (test_addr == {ADDR_WIDTH{1'b1}}) beginnstate        = `IDEL6;test_addr_rst = 1'b1;endelsenstate        = `P5_READ0;end`IDEL6 :beginpattern_sel   = 1'b0;test_addr_rst = 1'b1;nstate        = `P6_READ0;end`P6_READ0 :beginnstate        = `P6_COMPARE0;end`P6_COMPARE0 :beginpattern_sel = 1'b0;check_en    = 1'b1;count_en    = 1'b1;if (test_addr == {ADDR_WIDTH{1'b1}}) beginnstate        = `IDEL1;test_addr_rst = 1'b1;r_end_en      = 1'b1;endelsenstate = `P6_READ0;enddefault :beginnstate        = `IDEL1;test_addr_rst = 1'b1;end endcaseend
endmodule

5、RA1SH.v

    RA1SH模块应该为封装的sram IP,主要内容为mem_cycle这个任务,是通过读写使能控制mem对输入地址进行存取,然后后面是一堆路径延迟设计、建立与保持时间检测等。

`celldefine
module RA1SH ( //8KQ,          //data_out [7:0]CLK,        //时钟 hclk 取反CEN,        //chip select 低有效WEN,        //读写使能、// 写使能,低有效A,          //读写地址 [12:0]D,          //data_in [7:0]OEN         //
);parameter        BITS = 8; // 数据位宽 8 bitparameter           word_depth = 8192; // 8K = 8 x 1024parameter          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 表示取反 为什么取反?因为代码中会用到一些BUFFERreg                       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;
beginNOT_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
endtasktask mem_cycle;
begincasez({WENi,CENi})//WENi 0:写、1:读   CENi:片选(低有效)2'b10: begin//读状态read_mem(1,0);//读数据输出end2'b00: begin//写状态write_mem(Ai,Di);//写入数据read_mem(0,0);//将写入数据显示在读数据输出end2'b?1: ;2'b1x: beginread_mem(0,1);  //读出不定态数据end2'bx0: beginwrite_mem_x(Ai);//写入不定态数据read_mem(0,1);  //读出不定态数据end2'b0x,2'bxx: beginwrite_mem_x(Ai);//写入不定态数据read_mem(0,1);  //读出不定态数据endendcase
end
endtasktask update_last_notifiers;
beginLAST_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
endtasktask latch_inputs;
beginLATCHED_A = _A ;LATCHED_D = _D ;LATCHED_WEN = _WEN ;LATCHED_CEN = _CEN ;LAST_Qi = Qi;
end
endtasktask update_logic;
beginCENi = LATCHED_CEN;WENi = LATCHED_WEN;Ai = LATCHED_A;Di = LATCHED_D;
end
endtasktask x_inputs;integer n;
beginfor (n=0; n<addr_width; n=n+1)beginLATCHED_A[n] = (NOT_A[n]!==LAST_NOT_A[n]) ? 1'bx : LATCHED_A[n] ;endfor (n=0; n<BITS; n=n+1)beginLATCHED_D[n] = (NOT_D[n]!==LAST_NOT_D[n]) ? 1'bx : LATCHED_D[n] ;endLATCHED_WEN = (NOT_WEN!==LAST_NOT_WEN) ? 1'bx : LATCHED_WEN ;LATCHED_CEN = (NOT_CEN!==LAST_NOT_CEN) ? 1'bx : LATCHED_CEN ;
end
endtasktask read_mem;input r_wb;input xflag;
beginif (r_wb)beginif (valid_address(Ai))beginQi=mem[Ai];endelse beginQi=wordx;endendelse beginif (xflag)beginQi=wordx;endelse beginQi=Di;endend
end
endtasktask write_mem;input [addr_width-1:0] a;input [BITS-1:0] d;
begincasez({valid_address(a)})1'b0:x_mem;1'b1: mem[a]=d;endcase
end
endtasktask write_mem_x;input [addr_width-1:0] a;
begincasez({valid_address(a)})//检查地址是否有效1'b0:x_mem;        1'b1: mem[a]=wordx;endcase
end
endtasktask x_mem;integer n;
beginfor (n=0; n<word_depth; n=n+1)mem[n]=wordx;
end
endtasktask process_violations;//主功能
beginif ((NOT_CLK_PER!==LAST_NOT_CLK_PER) ||(NOT_CLK_MINH!==LAST_NOT_CLK_MINH) ||(NOT_CLK_MINL!==LAST_NOT_CLK_MINL))beginif (CENi !== 1'b1)beginx_mem;read_mem(0,1);endendelse beginupdate_notifier_buses;x_inputs;update_logic;mem_cycle;endupdate_last_notifiers;
end
endtaskfunction valid_address;input [addr_width-1:0] a;
beginvalid_address = (^(a) !== 1'bx);
end
endfunctionbufif0 (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 orNOT_A2 orNOT_A3 orNOT_A4 orNOT_A5 orNOT_A6 orNOT_A7 orNOT_A8 orNOT_A9 orNOT_A10 orNOT_A11 orNOT_A12 orNOT_D0 orNOT_D1 orNOT_D2 orNOT_D3 orNOT_D4 orNOT_D5 orNOT_D6 orNOT_D7 orNOT_WEN orNOT_CEN orNOT_CLK_PER orNOT_CLK_MINH orNOT_CLK_MINL)beginprocess_violations; // 时序不满足,但是前端仿真一般不会去管时序,只管功能!
endalways@( _CLK )begin // 时钟检测 casez({LAST_CLK,_CLK})2'b01: beginlatch_inputs;update_logic;mem_cycle;end2'b10,2'bx?,2'b00,2'b11: ;2'b?x: beginx_mem;read_mem(0,1);endendcaseLAST_CLK = _CLK;
endspecify //路径延迟块$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;}
endclassmodule 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 beginhclk  =1;hresetn = 0;#200hresetn = 1;
endinitial begin:processrm_data = new();direct_write_during_read(16'd8);#200;loop_wr_rd_data(16'd18);$finish;
endtask ahb_init();hsel   = 1'b0 ;//未中该slavehwrite = 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:32bithready = 1'b1;haddr  = 32'd0;hwdata = 32'd0;rdata  = 32'd0;r_data_en = 1'b0;wait(hresetn);repeat(3)@(posedge sram_clk);
endtasktask write_data;
input [15:0] wr_nums;
beginrepeat(wr_nums)begin@(posedge hclk);rm_data.randomize();hsel   = 1'b1 ;//选中该slavehwrite = 1'b1 ;//写haddr  =  `START_ADDR + wr_iter;wr_iter = wr_iter +1;@(posedge hclk);hwdata = rm_data.data;hsel = 1;end
end
endtasktask read_data;
input [15:0] rd_nums;
beginrepeat(rd_nums)begin@(posedge sram_clk);hsel   = 1'b1 ;//选中该slavehwrite = 1'b0;//readhaddr  =  `START_ADDR + rd_iter;//bank1 cs0rd_iter = rd_iter +1;@(posedge sram_clk); hsel   = 1'b0 ;//@(posedge hclk);rdata <= hrdata[`DATA_SIZE-1:0];end
end
endtasktask direct_write_during_read;
input [15:0] wr_nums;//读写次数
beginahb_init();repeat(wr_nums)beginwrite_data(1);read_data(1);end@(posedge sram_clk);@(posedge hclk);ahb_init();#200;
end
endtasktask loop_wr_rd_data;
input [15:0] wr_nums;
beginahb_init();write_data(wr_nums);#rm_data.delay;read_data(wr_nums);@(posedge sram_clk);@(posedge hclk);ahb_init();#200;
end
endtasksramc_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、仿真

基于 AHB 总线的 SRAM 控制器设计相关推荐

  1. 一种基于PCI总线的反射内存卡设计

    一种基于PCI总线的反射内存卡设计 摘要: 对实时传输, 传统的以太网络由于传输协议开销的不确定性, 很难满足实时网络的要求, 实时网络是一种应用于高实时性要求的专用网络通信技术, 一般采用基于高速网 ...

  2. 基于FPGA的USB接口控制器设计(VHDL)(中)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第二篇,中篇,USB通信原理.USB 系统开发以及设计实例.话不多说,上货. 之前有关于 Veril ...

  3. 基于FPGA的XPT2046触摸控制器设计

    基于FPGA的XPT2046触摸控制器设计 小梅哥编写,未经许可,文章内容和所涉及代码不得用于其他商业销售的板卡 本实例所涉及代码均可通过向 xiaomeige_fpga@foxmail.com  发 ...

  4. 基于FPGA的USB接口控制器设计(VHDL)(上)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第一篇,上篇,USB 接口简介 以及 USB 体系结构.话不多说,上货. 之前有关于 Verilio ...

  5. 基于FPGA的SPI FLASH控制器设计

    1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...

  6. 【Paper】2013_基于一致性理论的无人机编队控制器设计_郭伟强

    原文地址: [1]郭伟强. 基于一致性理论的无人机编队控制器设计[D].哈尔滨工业大学,2013. 2013_基于一致性理论的无人机编队控制器设计_郭伟强 3.3 一致性理论 3.4 控制方案设计 3 ...

  7. 有温度传感器的风机控制系统C语言,毕业论文--基于单片机的工业风机控制器设计与实现.doc...

    大连东软信息学院 本科毕业设计(论文) 论文题目 论文题目:基于单片机的工业风机控制器设计与实现 系 所: 电子工程系 专 业: 电子信息工程(嵌入式系统工程方向) 学生姓名: 学生学号: 指导教师: ...

  8. Python 还原控制SCI论文算法系列1: 基于策略迭代的自适应最优控制器设计

    Python 还原控制SCI论文算法系列1: 基于策略迭代的自适应最优控制器设计 文章目录 Python 还原控制SCI论文算法系列1: 基于策略迭代的自适应最优控制器设计 0.前言 1.研究问题的描 ...

  9. 汽车主动悬挂系matlab,基于Matlab的汽车主动悬架控制器设计与仿真.doc

    基于Matlab的汽车主动悬架控制器设计与仿真 <现代控制理论及其应用>课程小论文 基于Matlab的汽车主动悬架控制器设计与仿真 学院:机 械 工 程 学 院 班级() 姓名: 2015 ...

最新文章

  1. 我要阻止做java开发的男朋友去创业型公司工作吗?
  2. 160525、高并发之mysql主从复制(linux)
  3. 某马Java架构师实战学习手册
  4. UNITY3D单词学习 speed和velocity的区别
  5. kudu导出为mysql格式_怎么实现kudu表数据的导入导出
  6. 小程序分包---组件化开发框架wepyjs的分包
  7. 右键菜单管理---右键管家
  8. 华为Mate 50系列明年初发布:麒麟990/骁龙8 Gen1加持
  9. java 枚举类型enum ppt,java中的枚举类型——Enum
  10. 【强化学习】83篇文献-万字总结
  11. 怎么关闭eureka的服务_SpringCloud之Eureka
  12. linux 查看fd命令,Linux中一种友好的find替代工具(fd命令)
  13. 这里有最新最全最专业的便携图形工作站硬件配置方案,来看看?
  14. Ubuntu VirtualBox虚拟机安装win7 win10全过程
  15. 年龄到底怎么算才对_怎么算年龄才是正确的
  16. 【Linux】CPSCP的说明及其区别
  17. 规范化理论:范式等级
  18. 关于 AlphaBlend 和 32bpp 的反锯齿图标
  19. 【第45期】《你好,安怡》热播,AI觉醒,奇点临近?
  20. 【考研精品词】历年英语真题词汇精析

热门文章

  1. cpp mysql_使用MYSQLCPPCONN连接MYSQL数据库与读写BLOB字段
  2. 树莓派(Raspberry) WIFI 配置 (无显示器)
  3. UOJ#310-[UNR #2]黎明前的巧克力【FWT】
  4. 微信小程序组件、web-view、h5之间交互
  5. 怎么把文字转换成语音,这里有简单的方法
  6. 大卫 异星觉醒 机器人_异星觉醒结局翻转恶心到观众?隐藏剧情暗含深意
  7. php spry文本域_SPRY验证文本域之用户名称
  8. 浅谈Spring定时任务
  9. RatingBar的使用
  10. 【Java并发编程的艺术】读书笔记——Java并发编程基础