调用IP实现数据加速

前言

在数字系统设计中,提升数据处理速度和效率是关键目标。本实验着眼于利用双端口RAM和异步FIFO对IP核ROM中的数据进行加速处理,通过这两种硬件组件的有效结合来优化数据访问和传输。双端口RAM允许同时进行读写操作,提高数据处理的并行性和吞吐量,而异步FIFO则解决了不同时钟域之间的数据传输问题,实现了高效的数据缓冲和同步。设计一个集成了双端口RAM和异步FIFO的FPGA系统,验证其在加速数据处理方面的性能。

正文

一、调用IP实现数据加速

1.项目需求

通过ROM,RAM,FIFO与锁相环四个ip核结合起来进行一个综合设计,本设计主要是利用双端口ram和异步fifo对Ip核rom中的数据进行加速处理。

2.技术介绍

双端口RAM:拥有两个独立的读写端口,可同时进行读写操作。提供了更高的数据吞吐量和更低的访问延迟,适用于需要并行处理的应用场景。

异步FIFO:通过内部的读写指针和状态标志位,实现了数据的无缝缓冲和处理,有效解决了数据传输中的速率不匹配问题。是一种先进先出(FIFO)的数据缓存器,能够跨时钟域进行数据传输。

RAM(Random Access Memory,随机访问存储器):RAM是一种易失性存储器,即断电后存储的数据会丢失。它支持随机读写,这意味着可以跳过前面的内存地址,直接对任何地址上的数据进行访问。RAM通常用于快速存储正在被CPU使用的程序和数据,以提高响应速度和效率。它通常用于操作系统和应用程序的内存分配,因为它能够以非常快的速度进行读写操作。

ROM(Read-Only Memory,只读存储器):ROM是一种非易失性存储器,即使电源关闭,其中存储的数据也可以保持不变。它只能进行读操作,不能进行写操作,或者是写操作受到限制,需要特殊的硬件或软件条件才能进行。ROM通常用于存储固件或固化的代码,这些信息不应当被修改,或者是在设备启动的时候需要使用这些数据。例如,BIOS(基本输入输出系统)就是一种ROM,它储存了计算机启动和检测硬件所需的基本指令。mif文件保存正弦波数据。

FIFO(First-In First-Out,先进先出队列):FIFO是一种数据结构,遵循先进先出的原则,即最早进入队列的数据或元素是第一个被处理的。FIFO在软件中是一种算法,而在硬件中则可能是一个专门的组件。在FPGA中,FIFO通常由专门的硬件逻辑实现,它可以跨越不同的时钟域工作,支持不同速度的数据源和使用者之间的高效数据传输。FIFO用于在数据处理的不同阶段之间缓冲数据,保证数据流不受速度差异的影响,避免数据丢失或乱序。在多任务系统和实时系统中,FIFO是一种常见的数据交互机制。

在实际的数字系统设计中,RAM、ROM和FIFO可以通过不同的搭配方式来提高数据处理的效率和性能。进行数据预取与缓存流程为

1.ROM读取:从ROM中读取数据或指令。

2.数据存储:将数据存储到RAM中,以便进行快速访问和处理。

3.FIFO缓冲:将数据从RAM通过FIFO传输到处理单元,FIFO在数据传输过程中进行缓冲,避免由于处理速度不匹配而导致的数据丢失。

基于IP核,本章将讲述如何实现利用双端口ram和异步fifo对Ip核rom中的数据进行加速处理。

3.顶层架构

数据保存在rom中通过ram写控制模块读出数据并写入到ram中,ram的数据通过ram读控制模块读出并通过fifo写控制模块写入到fifo中,数据最终通过fifo读控制模块读出。

4.端口描述

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

二、代码验证

rd_fifo_ctrl模块:

复制代码
module rd_fifo_ctrl(
 
	input clk,
	input rst_n,
	input empty,//标志信号,指示FIFO是否为空。为空时,无法进行读操作。
	input full,//标志信号,指示FIFO是否已满。FIFO满时,无法进行写操作。
	
	output reg rdreq//读请求信号,表示请求从FIFO中读取数据。
);
 
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		rdreq <= 1'b0;
	else
		if(full == 1)//FIFO已满
			rdreq <= 1'b1;//开始读
		else
			if(empty == 1)//FIFO为空
				rdreq <= 1'b0;//停止读
			else
				rdreq <= rdreq;
end 
endmodule 

rd_ram_ctrl模块:

复制代码
module rd_ram_ctrl(

	input clk,
	input rst_n,
	input wren,//ram写使能
	
	output reg rden,//ram读使能信号
	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'b0;
			wren_rr <= 1'b0;
		end 
	else
		begin
			wren_r <= wren;//暂存写使能信号
			wren_rr <= wren_r;
		end 
end 

assign neg_flag = ~wren_r & wren_rr;//ram写使能信号下降沿检测

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_fifo_ctrl模块:

复制代码
module wr_fifo_ctrl(

	input clk,
	input rst_n,
	input rden,//ram的读使能信号
	input [7:0] data,//写入FIFO的数据
	
	output reg wrreq,//fifo写使能
	output [7:0] fifo_data//fifo写数据
);

assign fifo_data = data;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		wrreq <= 1'b0;
	else
		wrreq <= rden;//ram读使能时fifo写使能
end 

endmodule 

wr_ram_ctrl模块:

复制代码
module wr_ram_ctrl(

	input clk,
	input rst_n,
	input empty,//标志信号,指示FIFO是否为空。为空时,无法进行读操作。
	input [7:0] data,//rom读出的数据
	
	output reg [7:0] rom_addr,//rom的地址
	output reg wren,//ram写使能
	output reg [7:0] wr_addr,//ram写地址
	output [7:0] wr_data//ram写数据
);

reg rd_rom_en;//读rom使能信号
reg wren_r;

reg [1:0] state;
parameter s0 = 2'd0;//fifo数据是否读空状态
parameter s1 = 2'd1;//控制从rom中读取数据
parameter s2 = 2'd2;//判断fifo是否写入数据

//状态机
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		begin
			state <= s0;
			rd_rom_en <= 1'b0;
		end 
	else
		case(state)
			s0	:	begin
						rd_rom_en <= 1'b0;//读rom使能信号停止
						if(empty == 1)//fifo为空,状态跳转
							state <= s1;
						else
							state <= s0;
					end 
			s1	:	begin
						if(rom_addr == 255)//rom的地址读完
							begin
								rd_rom_en <= 1'b0;
								state <= s2;
							end 
						else
							begin
								rd_rom_en <= 1'b1;
								state <= s1;
							end 
					end 
			s2	:	begin
						rd_rom_en <= 1'b0;
						if(empty == 0)//fifo不为空
							state <= s0;
						else
							state <= s2;
					end 
			default	:	begin rd_rom_en <= 1'b0;state <= s0; end 
		endcase
end 

always @(posedge clk,negedge rst_n)//地址递增
begin
	if(rst_n == 0)
		rom_addr <= 8'd0;
	else
		if(rd_rom_en == 1)
			rom_addr <= rom_addr + 8'd1;
		else
			rom_addr <= 8'd0;
end 

always @(posedge clk,negedge rst_n)//
begin
	if(rst_n == 0)
		wren <= 1'b0;
	else
		wren <= rd_rom_en;	//ram写使能=rom读使能
end 

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		wr_addr <= 8'd0;
	else
		wr_addr <= rom_addr;
end 

assign wr_data = data;//rom读出数据等于ram写入数据

endmodule 

data_speed_up模块:顶层连线

复制代码
module data_speed_up(

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

	wire wr_ram_clk;//20
	wire rd_ram_clk;//50
	wire rd_fifo_clk;//120
	wire locked;
	wire [7:0] rom_addr;
	wire [7:0] rom_data;
	wire empty;
	wire wren;
	wire [7:0] wr_addr;
	wire [7:0] wr_data;
	wire rden;
	wire [7:0] rd_addr;
	wire [7:0] rd_data;
	wire wrreq;
	wire [7:0] fifo_data;
	wire rdfull;
	wire rdreq;
	wire wrfull;
	wire wrempty;
	wire [7:0] wrusedw;
	wire [7:0] rdusedw;

my_pll	my_pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0 ( wr_ram_clk ),
	.c1 ( rd_ram_clk ),
	.c2 ( rd_fifo_clk ),
	.locked ( locked )
);

my_rom	my_rom_inst (
	.address ( rom_addr ),
	.clock ( wr_ram_clk ),
	.q ( rom_data )
	);

wr_ram_ctrl wr_ram_ctrl_inst(

			.clk(wr_ram_clk),
			.rst_n(locked),
			.empty(empty),
			.data(rom_data),
			
			.rom_addr(rom_addr),
			.wren(wren),
			.wr_addr(wr_addr),
			.wr_data(wr_data)
);

double_ram	double_ram_inst (
	.data ( wr_data ),
	.rdaddress ( rd_addr ),
	.rdclock ( rd_ram_clk ),
	.rden ( rden ),
	.wraddress ( wr_addr ),
	.wrclock ( wr_ram_clk ),
	.wren ( wren ),
	.q ( rd_data )
	);
	
rd_ram_ctrl rd_ram_ctrl_inst(

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

wr_fifo_ctrl wr_fifo_ctrl_inst(

			.clk(rd_ram_clk),
			.rst_n(locked),
			.rden(rden),
			.data(rd_data),
			
			.wrreq(wrreq),
			.fifo_data(fifo_data)
);

rd_fifo_ctrl rd_fifo_ctrl_inst(

			.clk(rd_fifo_clk),
			.rst_n(locked),
			.full(rdfull),
			.empty(empty),
			
			.rdreq(rdreq)
);

asyn_fifo	asyn_fifo_inst (

			.data ( wr_data ),//输入信号,将要写入FIFO的数据。
			.rdclk ( rd_clk ),//读操作的时钟信号,控制FIFO读操作的时序。
			.rdreq ( rdreq ),//读请求信号,表示请求从FIFO中读取数据。
			.wrclk ( wr_clk ),//写操作的时钟信号,控制FIFO写操作的时序。
			.wrreq ( wrreq ),//写请求信号,表示请求将数据写入FIFO。
			.q ( data ),//输出信号,从FIFO中读取的数据。
			.rdempty ( rdempty ),//标志信号,指示FIFO是否为空。为空时,无法进行读操作。
			.rdfull ( rdfull ),//标志信号,指示FIFO是否已满。FIFO满时,无法进行写操作。
			.rdusedw ( rdusedw ),//信号,指示当前FIFO中已被使用的单元数量。
			.wrempty ( wrempty ),//标志信号,指示FIFO在写操作时是否为空。
			.wrfull ( wrfull ),//标志信号,指示FIFO在写操作时是否已满。
			.wrusedw ( wrusedw )//信号,指示当前FIFO中已被写入的单元数量。
);

endmodule 

仿真文件

复制代码
`timescale 1ns/1ps
module data_speed_up_tb;

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

data_speed_up data_speed_up_inst(

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

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

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

endmodule 

三、仿真验证

观看仿真波形图,数据可以正常读出:

调出中间数据进行分析,可以看到读rom/写ram,读ram/写fifo,读fifo三个阶段分别使用依次变慢的时钟,对数据进行降速处理。

三阶段数据均位有丢失。

既然依次变慢的时钟会对数据读取有减速效果,同理,将锁相环输出时钟依次变快

由此,数据加速完成,利用双端口ram和异步fifo对Ip核rom中的数据进行加速处理成功。

参考资料

IP核FIFO调用及验证(2)

IP核RAM调用及验证(2)

相关推荐
内有小猪卖33 分钟前
时序约束 记录
fpga开发
Cao1234567893214 小时前
FPGA时钟设计
fpga开发
JNTeresa7 小时前
锁存器知识点详解
fpga开发
Cao12345678932110 小时前
FPGA基础之基础语法
fpga开发
一大Cpp10 小时前
通过Quartus II实现Nios II编程
fpga开发
7yewh11 小时前
Verilog 语法 (二)
fpga开发
边缘计算社区1 天前
FPGA与边缘AI:计算革命的前沿力量
人工智能·fpga开发
S&Z34631 天前
[官方IP] Shift RAM
网络协议·tcp/ip·fpga开发
S&Z34631 天前
[FPGA Video IP] Video Processing Subsystem
网络协议·tcp/ip·fpga开发·video
FPGA_Linuxer1 天前
FPGA 100G UDP纯逻辑协议栈
网络协议·fpga开发·udp