verilog 实现 单口 ram ,我确定你看完后会彻底理解 ram

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 仿真

在这里插入图片描述

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用单口RAM实现乒乓FIFO的Verilog代码示例: ```verilog module PingPongFIFO ( input wire clk, input wire reset, input wire write_en, input wire read_en, input wire [7:0] write_data, output wire [7:0] read_data, output wire empty, output wire full ); parameter DATA_WIDTH = 8; parameter DEPTH = 16; reg [DATA_WIDTH-1:0] mem [DEPTH-1:0]; reg [4:0] write_ptr; reg [4:0] read_ptr; reg [4:0] count; wire wr_ptr_inc = (write_en && !full) ? write_ptr + 1 : write_ptr; wire rd_ptr_inc = (read_en && !empty) ? read_ptr + 1 : read_ptr; assign read_data = mem[read_ptr]; assign empty = (count == 0); assign full = (count == DEPTH); always @(posedge clk or posedge reset) begin if (reset) begin write_ptr <= 0; read_ptr <= 0; count <= 0; end else begin if (write_en && !full) write_ptr <= wr_ptr_inc; if (read_en && !empty) read_ptr <= rd_ptr_inc; if (write_en && read_en && !full && !empty) count <= count; else if (write_en && !read_en && !full) count <= count + 1; else if (!write_en && read_en && !empty) count <= count - 1; end end always @(posedge clk) begin if (write_en && !full) mem[write_ptr] <= write_data; end endmodule ``` 这段代码与之前的示例代码相同,使用了单口RAM作为存储器,并实现了乒乓FIFO的读写功能。单口RAM在这里被用来存储数据,通过读写指针进行数据的读写操作。其他部分的逻辑与之前的示例相同。 请注意,这只是一个简单的乒乓FIFO的实现示例,您可以根据需要进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值