数字IC设计——SRAM的Verilog语言实现(一)(单端口SRAM)
一、 随机访问内存(RAM)的介绍
随机访问内存(Random Access Memory) 用来存储和保存数据的。在任何时候都可以读写,RAM通常用作操作系统或其他正在运行的程序的临时存储介质(可称作系统内存)。但是,当电源关闭时时RAM不能保留数据,如果需要保存数据,就必须把它们写入到一个长期的存储器中(例如硬盘)。正因为如此,有时也将RAM称作"可变存储器"。
RAM内存可以进一步分为静态RAM(SRAM)和动态内存(DRAM)两大类。
首先介绍一下SRAM和DRAM的区别:
-
静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种。所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。然而,当电力供应停止时,SRAM储存的数据还是会消失(被称为volatile memory)。SRAM 不需要刷新电路即能保存它内部存储的数据。SRAM具有较高的性能,SRAM 速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲
但是SRAM也有它的缺点,即它的集成度较低,功耗较DRAM大 ,相同容量的DRAM内存可以设计为较小的体积,但是SRAM却需要很大的体积 -
DRAM(Dynamic Random Access Memory) 每隔一段时间,要刷新充电一次,否则内部的数据即会消失。DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM
-
总结如下表:
- | SRAM | DRAM |
---|---|---|
存储信息 | 触发器 | 电容 |
破坏性读出 | 非 | 是 |
需要刷新 | 不要 | 需要 |
送行列地址 | 同时送 | 分两次送 |
运行速度 | 快 | 慢 |
集成度 | 低 | 高 |
发热量 | 大 | 小 |
存储成本 | 高 | 低 |
二、用verilog实现一个深度为16,位宽8bit的单端口SRAM。搭建一个仿真环境,完成初始化,读取,写入的操作
要求:
可仿真,可综合,可用于fpga
搭建一个仿真环境,完成初始化,读取,写入的操作。
- Verilog 代码模块
module mini_sp_ram #(
parameter ADDR_BITS=16
)(
input clk,
input [ 3:0] addr,
input [ 7:0] din,
input ce,
input we,
output reg [ 7:0] dout
);
localparam MEM_DEPTH = 1 << ADDR_BITS; //16
reg [7:0] mem[MEM_DEPTH-1:0]; //mem是8*16的寄存器数组
// synopsys_translate_off
integer i;
initial begin
for(i=0; i<MEM_DEPTH;i=i+1) begin
mem[i] = 8'h00;
end
end
// synopsys_translate_on
always @(posedge clk) begin
if(ce & we) begin
mem[addr] = din;
end
end
always @(posedge clk) begin
if(ce && (!we)) begin
dout <= mem[addr];
end
end
endmodule
- 代码分析及注意事项
1)代码中的
上面的一段是可综合的片段。
引导语句“// synopsys translate_off”
以前一直没弄懂,以为就是个简单的注释完事,原来还可以用来引导综合过程:
设计者在写设计代码时,有时可能针对仿真写一些语句,这些语句可能是不为DC所接受,也不希望DC接受;设计者如果不对这些语句进行特殊说明,DC读入设计代码时就会产生语法错误。
另一种情况是,设计者在写设计代码,有些设计代码是为专有的对象写的(如公司内部),这些专有的设计代码可能不希望被综合。
Synopsys提供了引导语句,设计者可以使用这些引导语句控制DC综合的对象。
在设计代码中,引导语句“// synopsys translate_off”后直到“// synopsys translate_on”之间的语句被DC忽略。
原文链接
2)当读信号we 为高电平时,数据写入 SRAM mem[i]的某个地址中,当 信号we为低电平时,数据SRAM mem[i]被从地址中读出到d_out,
后面的两段always语句块可以改成如下:
always @(posedge clk) begin
casex ({ce,we})
2'b1x: dout <= 'hx;
2'b01: mem[addr] <= din;
2'b00: dout <= mem[addr];
default: ;
endcase
end
//casex综合出的电路和case一样,x会被综合工具忽略,一般多用于测试testbench中,等同于:
case ({ce,we})
2'b01: mem[addr] <= din;
2'b00: dout <= mem[addr];
default: dout <= 'hx;
endcase
当然需要注意casex的用法,具体和case、casez的用法可以参考博客:
Verilog语言中case、casex、casez的用法和区别
- 测试代码
module mem_tb(
);
reg clk,we,ce;
reg [3:0] addr;
reg [7:0] data_in;
wire [7:0] data_o;
integer i;
mini_sp_ram #(.ADDR_BITS(4)) u_ram( //参数例化
.clk(clk),
.we(we),
.ce(ce),
.din(data_in),
.addr(addr),
.dout(data_o)
);
//clk generate
always #5 clk = ~clk;
initial begin
clk = 1'b1;
ce = 1'b0;
we = 1'b0;
addr = 4'd0;
data_in = 8'h00;
#20
@(negedge clk) //read
ce = 1'b1;
for (i = 0; i<16; i=i+1) begin
@(negedge clk)
addr = i;
end
@(negedge clk) //write
we = 1'b1;
for (i= 0; i<16; i=i+1) begin
@(negedge clk) begin
addr = i;
data_in = data_in +'h01;
end
end
@(negedge clk) //read
we = 1'b0;
for (i= 0; i<16; i=i+1) begin
@(posedge clk)
addr = i;
end
@(negedge clk)
ce = 1'b0;
//200s stop
#200 $stop;
end
endmodule
- 仿真测试结果:
we = 0, ce = 1 时,初始化
we = 1, ce = 1 时,data_in数据写入addr中(数据为1-16)
we = 0, ce = 1 时,data_o读出mem中的数据(数据为1-16)
可以看到此Memory的设计可以实现单端口的读写,即不能同时对memory进行读写。
但是可以通过此单端口的SRAM设计为一个双端口的FIFO,可以实现同时读写。
具体可以参考这边博客使用单端口SRAM制作双端口FIFO