IP核RAM调用及验证(2)

前言

        在IP核RAM调用及验证(1)中初步使用ip核RAM进行数据写入与输出,用到单端口RAM进行实验。既然提到单端口,则有双端口与之对应,单端口RAM和双端口RAM都是用于存储数据的内存类型,但它们在设计和功能上有一些不同。下面总结二者异同之点:

        单端口RAM: 单端口RAM只有一个读写端口,通常是一个读端口和一个写端口的组合。在同一时刻,它只能进行读取或写入操作,而不能同时进行两者。也就是说,在任何给定时刻,你只能读取数据或者写入数据,但不能同时进行。设计相对简单,成本较低。适合于对存取操作要求不高的应用,如缓存、简单的数据存储等。

        双端口RAM:双端口RAM有两个独立的读写端口。这两个端口可以同时进行读操作或写操作,而互不干扰。可以同时进行读和写操作,或者在两个不同的端口上进行不同的读写操作。这样可以提高并发处理能力和数据交换效率。设计更复杂,成本较高,因为需要处理两个端口的数据冲突和同步问题。适合于对数据处理和访问速度要求较高的应用,比如高速缓存、双端口数据缓冲等。

        两者都用于存储数据,可以进行读取和写入操作。都是使用类似的存储技术,如静态随机存取内存(SRAM)或动态随机存取内存(DRAM)。

        单端口RAM适用于较为简单的数据存储需求,而双端口RAM则适用于需要高并发数据访问的场景。选择哪种RAM取决于具体应用的需求和预算。

正文

一、IP核RAM调用及验证(2)

        1.项目需求

        依照双端口RAM的特点,对数据进行跨时钟域数据信号的处理,实现简单数据加速。

        2.技术介绍

        双端口ram进行跨时钟域数据信号的处理。

        单端口地址信号包含了两种含义:一个是写地址,一个是读地址;双端口是根据单端口地址信号的两种含义,将写地址和读地址独立出来,也就是说利用两个地址信号标志写地址和读地址。双端口ram也可以设置为两个独立的单端口ram进行应用。

       控制写入ram的时钟与控制读取ram的时钟频率不同,进行数据处理时就会产生跨时钟域的问题,那么如何进行跨时钟域数据信号的处理?

        首先考虑如何控制将数据写入到ram,写地址变量和写数据变量必须在写时钟下保持同步,同时在控制写入时写使能信号必须为高电平,其对应的时序形式为:

        在T0到T1时刻之间写地址变量为有效地址,写数据变量为有效数据。在T0时刻之前或者在T1时刻之后,写使能无效,那么写数据变量和写地址变量为无效变量。同理,在读取数据时,读使能,读地址时序关系和写入ram中的时序关系一致。数据与地址的时序关系至少延迟一个读时钟周期。

       如果进行跨时钟域时,写入数据和读取数据同时进行,写使能信号和读使能信号一直有效,如果写时钟快,读时钟慢,在ram的存储深度固定的情况下,读取数据时会丢失数据;如果写时钟慢,读时钟快,在ram的存储深度固定的情况下,读取数据时会丢失数据;

        根据以上分析,如果数据量大的话,不能够通过读取同时进行对数据进行跨时钟域处理,在不考虑数据量大小时,如何进行跨时钟域处理,将写入的数据完整的读取出来?

       一般进行这种跨时钟域数据处理时,需要控制数据先完整写入,然后在控制读取数据。就需要通过控制写使能来进行数据的写入,写使能控制写地址的变化,当将写入的数据完成以后再进行数据的读取,对于读取数据来说,如何确定写入数据完成?通过判断写使能信号有没有拉低。

双端口RAM的调用:

配置为一个读端口,一个写端口。

调用双端口RAM,配置总线宽度8,数据深度256

生成inst.v文件,其他保持默认。

        3.顶层架构

        通过锁相环产生不同时钟,不同的时钟分别驱动写逻辑模块与读数据模块,两模块产生对应的数据信号传入RAM后,RAM进行对应信号的处理。

        4.端口描述

clk时钟(50Mhz)
rst_n复位按键(低电平有效
[7:0] data数据读出

二、代码验证

        rd_doubleram_ctrl模块:读逻辑模块,读取写使能信号,产生读数据地址与读使能信号。

module rd_doubleram_ctrl(

	input clk,
	input rst_n,
	input wren,//写使能信号
	
	output reg rden,//读使能信号
	output reg [7:0] rd_addr//读地址
);

reg wren_r;//当前写使能信号
reg wren_rr;//前一时钟写使能信号
wire neg_flag;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		begin
			wren_r <= 1'b1;
			wren_rr <= 1'b1;
		end 
	else
		begin
			wren_r <= wren;//暂存写使能信号
			wren_rr <= wren_r;
		end 
end 

assign neg_flag = ~wren_r & wren_rr;
//当前写使能取反后与前一时钟写使能信号,进行下降沿判断,当出现下降沿时,产生一个时钟周期高电平

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		rden <= 1'b0;
	else
		if(neg_flag == 1)//写使能拉低时立即开始读
			rden <= 1'b1;
		else
			if(rd_addr == 255)//读完,读使能信号拉低
				rden <= 1'b0;
			else
				rden <= rden;
end 

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		rd_addr <= 8'd0;
	else
		if(rden == 1)
			rd_addr <= rd_addr + 8'd1;//地址变换驱动
		else
			rd_addr <= 8'd0;
end 

endmodule 

        wr_dobuleram_ctrl模块:写逻辑控制模块:读取读使能信号,产生写地址与写数据,写使能信号。

module wr_dobuleram_ctrl(

	input clk,
	input rst_n,
	input rden,//读使能信号
	
	output reg wren,//写使能信号
	output reg [7:0] wr_addr,//写地址
	output [7:0] wr_data//写数据
);

reg rden_r;
reg rden_rr;
wire neg_flag;//读使能信号下降沿标志信号

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		begin
			rden_r <= 1'b0;
			rden_rr <= 1'b0;
		end 
	else
		begin
			rden_r <= rden;//同步
			rden_rr <= rden_r;//寄存
		end 
end 

assign neg_flag = ~rden_r & rden_rr;
//当前读使能取反后与前一时钟读使能信号,进行下降沿判断,当出现下降沿时,产生一个时钟周期高电平

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		wren <= 1'b1;
	else
		if(wr_addr == 255)//写完,写使能信号拉低
			wren <= 1'b0;
		else
			if(neg_flag == 1)//读完,检测到读使能信号下降沿,写使能信号拉高
				wren <= 1'b1;
			else
				wren <= wren;
end 

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		wr_addr <= 8'd0;
	else
		if(wren == 1)
			wr_addr <= wr_addr + 8'd1;//写地址变换驱动
		else
			wr_addr <= 8'd0;	
end 

assign wr_data = wr_addr + 8'd1;//写数据赋值

endmodule 

        double_ram_test模块:顶层连线

module double_ram_test(

	input clk,
	input rst_n,
	
	output [7:0] data
);

wire wr_clk;
wire rd_clk;
wire locked;
wire wren;
wire [7:0] wr_data;
wire [7:0] wr_addr;
wire rden;
wire [7:0] rd_addr;
reg wr_clk_r;
reg rd_clk_r;
wire wr_clk_rr;
wire rd_clk_rr;
always @(posedge wr_clk,negedge locked)
begin
	if(locked == 0)
		wr_clk_r <= 1'b0;
	else
		wr_clk_r <= wr_clk;
end 

assign wr_clk_rr = (wr_clk_r == 1)?wr_clk:1'b0;

//确保在 locked 信号不稳定时(例如失锁),输出的时钟信号 rd_clk_rr 会被安全地置为 0
//避免错误的时钟信号传递。

always @(posedge rd_clk,negedge locked)
begin
	if(locked == 0)
		rd_clk_r <= 1'b0;
	else
		rd_clk_r <= rd_clk;
end 

assign rd_clk_rr = (rd_clk_r == 1)?rd_clk:1'b0;

//确保在 locked 信号不稳定时(例如失锁),输出的时钟信号 rd_clk_rr 会被安全地置为 0
//避免错误的时钟信号传递。

my_pll	my_pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0 ( wr_clk ),
	.c1 ( rd_clk ),
	.locked ( locked )
	);

wr_dobuleram_ctrl wr_dobuleram_ctrl_inst(

			.clk(rd_clk_rr),
			.rst_n(locked),
			.rden(rden),
			
			.wren(wren),
			.wr_addr(wr_addr),
			.wr_data(wr_data)
);

rd_doubleram_ctrl rd_doubleram_ctrl_inst(

			.clk(wr_clk_rr),
			.rst_n(locked),
			.wren(wren),
			
			.rden(rden),
			.rd_addr(rd_addr)
);

double_ram	double_ram_inst (
	.data ( wr_data ),
	.rdaddress ( rd_addr ),
	.rdclock ( wr_clk_rr ),
	.rden ( rden ),
	.wraddress ( wr_addr ),
	.wrclock ( rd_clk_rr ),
	.wren ( wren ),
	.q ( data )
	);

endmodule 

        仿真代码

`timescale 1ns/1ps
module ram_test_tb;

	reg clk;
	reg rst_n;
	
	wire [7:0] data;

double_ram_test ram_test_inst(

			.clk(clk),
			.rst_n(rst_n),
			
			.data(data)
);

initial clk = 1;
always #10 clk = ~clk;

initial begin
	rst_n = 0;
	#200
	rst_n = 1;
	#10000
	$stop;
end 

endmodule 

三、仿真验证

观看仿真波形图,有数据读出。

调用中间信号分析

总体观察,使用快时钟写入数据,慢时钟读出数据,完成跨时钟域数据处理。

在写使能信号拉低后产生一个时钟周期的高电平,进行信号下降沿检测,从而拉高读使能信号

同理在读使能信号拉低后产生一个时钟周期的高电平,进行信号下降沿检测,从而拉高写使能信号

参考资料

双端口随机访问内存

随机存取存储器

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值