前言
在数字系统设计中,提升数据处理速度和效率是关键目标。本实验着眼于利用双端口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)