概述
link
`timescale 1ns / 1ps
// Engineer: Reborn Lee
// Module Name: single_port_syn_ram
module single_port_syn_ram#(
parameter ADDR_WIDTH = 4, //地址位宽
parameter DATA_WIDTH = 16, // 数据位宽
//内存 ram 的本质是一个一维的列表
//对于这个 一维度的列表式的ram ,他的地址就是 一个序号而已
//故而 ,列表的长度最大可以使 2的 ADDR_WIDTH(4) 次方,也就是16
//其实这里你可以设置成为 任何一个比16 小的数字
parameter DEPTH = 2**ADDR_WIDTH //ram 列表长度
)(
input i_clk, //时钟信号
input [ADDR_WIDTH - 1 : 0] addr, // ram 地址参数
inout [DATA_WIDTH - 1 : 0] data, //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
input cs, // 这是ram 使能 信号,表示是否操作这个ram
//其实这个cs本质是一个选择符号
input wr, // 写使能
input oe // output enable,输出使能时RAM读取的结果才能输出
);
reg [DATA_WIDTH - 1 : 0] mem[0 : DEPTH - 1]; //ram 的位宽 是 16 DATA_WIDTH ,列表的长度是 DEPTH 16
reg [DATA_WIDTH - 1 : 0] mid_data; //中间缓存的寄存器
// write part //写数据部分
always@(posedge i_clk) begin //在时钟上升沿
if(cs&wr) begin //如果 cs=1 并且 写 wr=1 使能 ,则操作数据的写
mem[addr] <= data; //数据放在缓存寄存器
end
end
// read part
always@(posedge i_clk) begin //在时钟上升沿
if(cs & !wr) begin //如果 cs=1 并且 写 wr=0 使能 ,则操作数据的读数据,这里一共只有两个状态,
//读和写,这里并没有 读使能 read_enable
mid_data <= mem[addr]; //数据放在缓存寄存器
end
end
//在读数据的时候,我们需要设计一个三态缓冲器,如下:
/***
读使能有效时,我们将从缓冲区读出的数据放到mid_data中,之后通过一个三态门来将数据mid_data输出到三态总线上,此三态门的使能条件为读使能!
这条语句在综合工具中就会被推断为一个三态缓冲器!
在读使能有效时,将读取数据放在总线上,否则呈现为高阻态,避免占用此数据总线。
***/
//读使能有效时,我们将从缓冲区读出的数据放到mid_data中
//这个例子中并没有读使能,当使能cs=1且& 输出使能 oe=1 且& 写使能 wr=0 的时候 ,将读取数据放在总线上,否则呈现为高阻态,避免占用此数据总线。
assign data = (cs & oe & !wr)? mid_data: 'hz; //读数据
endmodule
//下面是 testbench
`timescale 1ns / 1ps
// Engineer: Reborn Lee
// Module Name: ram_tb
module ram_tb(
);
parameter ADDR_WIDTH = 4; //地址位宽
parameter DATA_WIDTH = 16; // 数据位宽
parameter DEPTH = 2**ADDR_WIDTH; //ram 列表长度
reg i_clk; //时钟
reg [ADDR_WIDTH - 1 : 0] addr; // ram 地址参数
wire [DATA_WIDTH - 1 : 0] data; //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
reg cs; // 这是ram 使能 信号,表示是否操作这个ram
reg wr; // 写使能
reg oe; // output enable,输出使能时RAM读取的结果才能输出
reg [DATA_WIDTH-1:0] tb_data; //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
//generate system clock
initial begin
i_clk = 0;
forever begin //这里的forever 不是绝对意义上的永远 而是,在数据读写的时候,永远每5ns 翻转一次
//就想说我永远爱你,不是从现在开始,到时间无涯的尽头一直爱你,而是在我的整个生命力,永远爱你
# 5 i_clk = ~i_clk; // 每5 ns 时钟翻转一次,
end
end
assign data = !oe ? tb_data : 'hz; // 写数据,
// assign data = tb_data ;
initial begin
{cs, wr, addr, tb_data, oe} = 0; //一开始都置零
repeat (2) @ (posedge i_clk); //先来两个时钟周期
//注意 verilog 是硬件描述语言,只要不涉及到时钟或者#的时延,都是同时 并行
//不可以 有C 语言 和python 的方法学习 verilog
//write test
//每个时钟周期,向 ram 中写一个随机数据,数据写的时候不能读 ,oe=0 输出使能0
for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
repeat (1) @(negedge i_clk) addr = i; wr = 1; cs =1; oe = 0; tb_data = $random;
end
//read test
//两次读写之间 有两个时钟周期的延时
repeat (2) @ (posedge i_clk);
//每个时钟周期,向 ram 中读一个随机数据,数据读的时候不能写 ,wr=0 写使能 0
for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
repeat (1) @(posedge i_clk) addr = i; wr = 0; cs = 1; oe = 1;
end
#20 $finish;
end
//下面是函数调用的接口
single_port_syn_ram #(
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH),
.DEPTH(DEPTH)
) inst_single_port_syn_ram (
.i_clk (i_clk),
.addr (addr),
.data (data),
.cs (cs),
.wr (wr),
.oe (oe)
);
endmodule
我用的是 vivado 21 秒学会 vivado 仿真
最后
以上就是冷酷荔枝为你收集整理的verilog 实现 单口 ram ,我确定你看完后会彻底理解 ram的全部内容,希望文章能够帮你解决verilog 实现 单口 ram ,我确定你看完后会彻底理解 ram所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复