基于AHB协议的SRAMC设计
前言
本系列文章将从零开始,完成一个基于AHB协议的SRAM控制器设计,并对代码进行详细分析。主要是记录一下我之前的学习过程,方便日后复习查看,如有错误,希望大家批评指正。
一、SRAMC的总体框架和功能
SRAM有着很高的读写速度,常在整个系统中作为缓存,是必不可少的一个存在。基于AHB协议的SRAMC可以实现SRAM存储器与AHB总线的数据信息交换。本设计将其分为三个模块,sram_core、ahb_slave_if以及顶层模块sram_top。
1.总体框架
2.功能
- 可以按照AHB的时序进行连续的读取和写入
- 可以传输8位、16位、32位的数据
实现功能的主要为sram_core和ahb_slave_if模块,ahb_slave_if模块负责处理来自AHB总线的信号(控制信号、地址信号、数据信号),将其转换为对sram_core的信号,控制sram_core的使能和读写。sram_core模块部分主要负责数据的写入和读取,针对ahb_slave_if模块传来的信号做出响应,使能对应的srambank,写入或读出数据。
二、设计代码实现
1.sram_core
不会使用Memory compiler,所以这一部分我就自己简单写了写。
由于最大要传输32位的数据,而sram一般是8位,因此sram_core最少需要4个srambank才能支持,所以本设计的sram_core模块一共包含了4个srambank。
我将每个srambank的大小设置为8K(数据位宽8位,数据深度1K),一共采用了4个srambank,在传输32位数据的时候他们是同时工作的,在传输8位或16位数据时只有部分srambank工作,因此需要对不同的srambank进行地址分配。我采用了以下方式分配地址。
地址0代表srambank0的0地址,地址1代表srambank1的0地址,地址2代表srambank2的0地址,地址3代表srambank3的0地址,地址4代表srambank0的1地址,地址5代表srambank1的1地址。。。以此类推。
sram代码如下:
module sram #(
parameter ADDR_WIDTH = 10,
ADDR_DEPTH = 1024,
DATA_WIDTH = 8
)(
input clk,
input en,
input write,
input read,
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] wdata,
output reg [DATA_WIDTH-1:0] rdata
);
reg [DATA_WIDTH-1:0] mem [ADDR_DEPTH-1:0];
always@(posedge clk)
if(en&&write)
mem[addr] <= wdata;
else
mem[addr] <= mem[addr];
always@(posedge clk)
if(en&&read)
rdata <= mem[addr];
else
rdata <= rdata;
endmodule
sram部分就是一个简单的mem,这里就不多说了。注意:虽然sram具有读写两个端口,但是不能同时读写,这是一个伪双口ram(可以改为一个端口控制,我只是懒得改了)
sram_core代码如下:
module sram_core (
input clk,
input [3:0]en,
input write,
input read,
input [31:0] sram_addr,
input [31:0] wdata,
output [31:0] rdata
);
genvar i;
generate for(i=0;i<4;i=i+1)begin:sram_instance
sram #(
.ADDR_WIDTH(10),
.ADDR_DEPTH(1024),
.DATA_WIDTH(8)
)u_sram(
.clk(clk),
.en(en[i]),
.write(write),
.read(read),
.addr(sram_addr[11:2]),
.wdata(wdata[8*i+7-:8]),
.rdata(rdata[8*i+7-:8])
);
end
endgenerate
endmodule
sram_core只是对sram进行了四次例化,非常简单。
注意:例化时地址连接的并不是sram_addr[9:0],而是sram_addr[11:2],这是因为将sram_addr的最后低两位用作srambank的片选。
传输8位数据时,00选择srambank0,01选择srambank1,10选择srambank2,11选择srambank3;传输16位数据时,00选择srambank0和srambank1,10选择srambank2和srambank3。
这里解释一下 为什么传输16位数据时00选择srambank0和srambank1,10选择srambank2和srambank3,为什么没有01和11呢。
这是因为AHB协议中要求了传输地址和传输位宽要对齐,每次需要读写两个srambank,对于sram_core来说就要一次读写两个地址,故AHB连续给地址的时候一定是每次+2,也就是00——10。最低位一定是0。
同理传输32位数据时,地址就要每次+4,对应最低两位一定是00。
2.ahb_slave_if
本模块才是此次设计中最重要的部分,这里先简单说一下模块中都包含哪些组成部分,后面会再详细介绍。
- 地址和控制信号寄存器,用来打排延迟,因为AHB完成一次读或写需要两个周期,地址周期和数据周期,而sram的地址和数据在同一拍。
- 地址处理,对AHB传来的地址进行处理,将其变为sram的地址。
- 数据大小处理,即对hsize的处理,跟地址处理是一起的,这俩个其实可以合并到一起。
- 使能控制,当传输16位或8位数据时只有部分srambank工作,只对工作的srambank使能,对不工作的不使能。
- 写入读出数据处理,对来自AHB总线和sram_core的数据进行处理,确保能从正确的srambank中写入和读出数据。
- 读写信号控制,顾名思义,产生sram_core的读写信号。
ahb_slave_if代码如下:
module ahb_slave_if (
input hclk,
input hrstn,
input hsel,
input hready,
input hwrite,
input [2:0]hburst,
input [31:0]haddr,
input [1:0]htrans,
input [2:0]hsize,
input [31:0]hwdata,
input [31:0]rdata,
output reg [31:0]hrdata,
output reg [3:0]en,
output write,
output read,
output [31:0]sram_addr,
output reg [31:0]wdata
);
//地址和控制信号寄存器
reg [31:0] haddr_r;
reg [2:0] hsize_r;
reg hwrite_r;
reg [2:0]hburst_r;
reg [1:0]htrans_r;
always@(posedge hclk or negedge hrstn)begi