赞
踩
学习FPGA的笔记,根据正点原子视频进行学习
开发板AX7020中本身并没有FIFO模块,其FIFO模块其实是通过block memory generator产生的
FIFO与RAM模块不同的是FIFO好像是一个管道,并没有对应的地址,其遵循先进先出的原则。其可作用在同步或者异步之间传输数据。
创建FIFO模块并写入0-255再读出来
首先我们需要调用IP_FIFO模块,再通过一个写入驱动模块以及一个读出驱动模块对FIFO进行驱动,由于本次实验进行的是异步FIFO实验(读和写用的不是一个时钟),因此我们需要一个锁相环(PLL)模块对时钟进行分频。最后我们需要一个顶层模块对以上四个模块进行调用。
直接在IP Catalog模块中搜索FIFO,其中有一个FIFO Generator,进入即为FIFO配置模块。Basic模块中可以选择不同的FIFO类型,前面几个带有Common的是同步FIFO,后面几个是异步的FIFO,具体不同点可以查看数据手册。
我们这里选用第四个Block产生的异步FIFO。
Native Ports中可以对FIFO的容量进行配置,其中包括宽度以及深度
Status Flags中可以设置Almost Flag,这两个选项勾选上,它表明是加入FIFO快满信号(实际上是,FIFO还有一位就满了或者还有一位就空了的时候这个引脚会拉高,在后面是很有用的)。
其余配置可以根据数据手册按照自己的需求进行配置。
在IP Catalog中搜索clock,选择clock wzard选项进入配置
clocking options中可以设置MMCM或者PLL类型,MMCM比PLL功能稍微强大一点,能用 PLL解决的MMCM也行,下面一些配置我们默认。
output clock页面是配置输出时钟,可以设置输出时钟的条数,名称,要求的频率,相位,占空比,下面可以设置是否要reset,locked。
注意:FIFO、PLL的reset都是高电平有效即输入1复位在该页面可以修改,但AX7020的按键是输入0有效。
后面的页面保持默认。
3.分析FIFO时序图
以上是三个模块(FIFO)的输入输出,以及整个项目中四个模块之间的关系以及输入输出。
下面对写、读模块进行分析:
256位FIFO实际容量是255,因此写入数据位0-254
写模块:
clk_50m:时钟 50m
rst_n:复位
wr_rst_busy: 复位在忙信号,复位结束一段时间后拉低
empty:FIFO是否为空,通常写入几个数据之后,空信号才会由高变低
empty_d0 d1:打拍,(避免亚稳态的产生,常常采用打两拍的方法,类似于消抖)
fifo_wr_en: 写使能,在复位繁忙结束后即可进行写使能的拉高
fifo_wr_data:FIFO写入数据,注意定义256位的FIFO实际只有255位可以存储数据
almost_full:当还差一位满的时候进行拉高,通常用于停止写入
读模块:
clk_100m:时钟
rst_n:复位
full:满
fifo_rd_en:读使能信号
fifo_rd_data:读取数据、
almost_empty:几乎空
这里解释一下我们为什么需要“几乎满”、“几乎空了这两个信号”
我们使用满信号/几乎满信号来判断是否停止写操作(如果满了之后仍然进行写操作是无效的,而且会触发溢出标志)。如果我们使用满信号,则在写完254的周期的下一个时钟上升沿拉上满信号,因此在254下一个周期判断full是否是1,由于非阻塞赋值的原因,254周期full信号仍然是0,因此在下一个周期内检测到的full仍然是0。这样会导致有一位溢出。
如果使用快满信号,则在写入254位的周期时候快满信号拉高,虽然在本周期内无法检测到快满拉高,但下一周期就不会继续写入。
-
-
- module fifo_wr(
- (*mark_debug = "true"*) input clk_50M ,
- (*mark_debug = "true"*) input rst ,
- (*mark_debug = "true"*) input locked ,
- (*mark_debug = "true"*) input empty ,
- (*mark_debug = "true"*) input almost_full ,
- (*mark_debug = "true"*) input wr_rst_busy ,
- (*mark_debug = "true"*) output reg fifo_wr_en ,
- (*mark_debug = "true"*) output reg [7:0] fifo_wr_data
- );
- (*mark_debug = "true"*) reg empty_1;
- (*mark_debug = "true"*) reg empty_2; //实际上使用的是empty_2,相当于消抖
-
- //要对异步信号进行打两拍的操作
- always @(posedge clk_50M or negedge rst) begin
- if(rst == 1'b0) begin
- empty_1 <= 1'b0;
- empty_2 <= 1'b0;
- end
-
- else begin
- empty_1 <= empty ;
- empty_2 <= empty_1;
- end
- end
-
-
- //fifo_wr_en
- always @(posedge clk_50M or negedge rst) begin
- if(rst == 1'b0)
- fifo_wr_en <= 1'b0;
- else if (rst == 1'b1 && locked == 1'b1 && wr_rst_busy == 1'b0 )
- if( empty_2 == 1'b1 )
- fifo_wr_en <= 1'b1;
- else begin
- fifo_wr_en <= fifo_wr_en;
- if(almost_full == 1'b1)
- fifo_wr_en <= 1'b0;
- end
- // if( almost_full == 1'b1)
- // fifo_wr_en <= 1'b0;
- else
- fifo_wr_en <= 1'b0;
-
- end
-
-
- //fifo_wr_data
- always @(posedge clk_50M or negedge rst) begin
- if (rst == 1'b0)
- fifo_wr_data <= 8'b0;
- else if( fifo_wr_en == 1'b1)
- fifo_wr_data <= fifo_wr_data + 8'b1;
-
- end
-
-
-
-
-
-
-
-
- endmodule
(*mark_debug=="true"*)是后面调试的代码,可以忽略
需要注意的就是打两拍的这段代码是第一层接触,其实其意思也就是使用两个周期后的异步信号的值,防止不同时钟频率之间不稳定的情况。
-
- module fifo_rd(
- (*mark_debug = "true"*) input clk_100M ,
- (*mark_debug = "true"*) input rst ,
- (*mark_debug = "true"*) output reg fifo_rd_en ,
- (*mark_debug = "true"*) input almost_empty,
- (*mark_debug = "true"*) input full ,
- (*mark_debug = "true"*) input rd_rst_busy ,
- (*mark_debug = "true"*) input [7:0] fifo_rd_data,
- (*mark_debug = "true"*) input locked
- );
-
- (*mark_debug = "true"*) reg full_1;
- (*mark_debug = "true"*) reg full_2;
-
-
- always @(posedge clk_100M or negedge rst) begin
- if(rst == 1'b0) begin
- full_1 <= 1'b0;
- full_2 <= 1'b0;
- end
-
- else begin
- full_1 <= full ;
- full_2 <= full_1;
- end
- end
-
-
-
-
- //fifo_rd_en
- always @(posedge clk_100M or negedge rst) begin
- if (rst == 1'b0)
- fifo_rd_en <= 1'b0;
- else if (rst == 1'b1 && locked == 1'b1 && rd_rst_busy == 1'b0)
- if (full_2 == 1'b1)
- fifo_rd_en <= 1'b1;
- else begin
- fifo_rd_en <= fifo_rd_en;
- if(almost_empty == 1'b1)
- fifo_rd_en <= 1'b0;
- end
-
- else
- fifo_rd_en <= 1'b0 ;
- end
-
-
-
- endmodule
同样需要打两排的模块
-
-
- module ip_fifo(
- input sys_clk,
- input sys_rst
- );
-
- wire clk_50M ;
- wire clk_100M ;
- wire locked ;
- wire [7:0] fifo_wr_data;
- wire fifo_wr_en ;
- wire fifo_rd_en ;
- wire [7:0] fifo_rd_data ;
- wire full ;
- wire almost_full ;
- wire empty ;
- wire almost_empty ;
- wire [7:0] rd_data_count;
- wire [7:0] wr_data_count;
- wire wr_rst_busy ;
- wire rd_rst_busy ;
-
-
- //pll
- clk_wiz_0 u_clk_wiz_0
- (
- // Clock out ports
- .clk_50M (clk_50M) , // output clk_50M
- .clk_100M (clk_100M) , // output clk_100M
- // Status and control signals
- .reset (~sys_rst) , // input reset
- .locked (locked) , // output locked
- // Clock in ports
- .clk_in1 (sys_clk)
- );
- //fifo
- fifo_generator_0 u_fifo_generator_0 (
- .rst (~sys_rst ), // input wire rst fifo 高电平有效
- .wr_clk (clk_50M ), // input wire wr_clk
- .rd_clk (clk_100M ), // input wire rd_clk
- .din (fifo_wr_data ), // input wire [7 : 0] din
- .wr_en (fifo_wr_en ), // input wire wr_en
- .rd_en (fifo_rd_en ), // input wire rd_en
- .dout (fifo_rd_data ), // output wire [7 : 0] dout
- .full (full ), // output wire full
- .almost_full (almost_full ), // output wire almost_full
- .empty (empty ), // output wire empty
- .almost_empty (almost_empty ), // output wire almost_empty
- .rd_data_count (rd_data_count ), // output wire [7 : 0] rd_data_count
- .wr_data_count (wr_data_count ), // output wire [7 : 0] wr_data_count
- .wr_rst_busy (wr_rst_busy ), // output wire wr_rst_busy
- .rd_rst_busy (rd_rst_busy ) // output wire rd_rst_busy
- );
-
-
- fifo_wr u_fifo_wr (
- .clk_50M ( clk_50M ) ,
- .rst ( sys_rst ) ,
- .locked ( locked ) ,
- .empty ( empty ) ,
- .almost_full ( almost_full ) ,
- .wr_rst_busy ( wr_rst_busy ) ,
- .fifo_wr_en ( fifo_wr_en ) ,
- .fifo_wr_data ( fifo_wr_data )
-
- );
- fifo_rd u_fifo_rd(
- .clk_100M ( clk_100M ) ,
- .rst ( sys_rst ) ,
- .fifo_rd_en ( fifo_rd_en ) ,
- .almost_empty ( almost_empty ) ,
- .full ( full ) ,
- .rd_rst_busy ( rd_rst_busy ) ,
- .fifo_rd_data ( fifo_rd_data ) ,
- .locked ( locked )
- );
-
-
-
-
- endmodule
对四个子模块调用,注意引脚别错了就行。
需要单独写仿真模块代码,本项目中对于整体(顶层模块)来说只有系统时钟、系统复位两个输入,因此仿真只需要对这两个信号进行模拟输入即可。
- `timescale 1ns/1ns
- module tb_ip_2port_ram();
-
- parameter clk_period = 20;
-
- reg sys_clk;
- reg sys_rst;
- //初始化复位
- initial begin
- sys_clk <= 1'b0;
- sys_rst <= 1'b0;
- #200
- sys_rst <= 1'b1;
- end
-
- //初始化时钟
- always #(clk_period/2) sys_clk =~sys_clk;
-
- ip_fifo u_ip_fifo (
- .sys_clk (sys_clk),
- .sys_rst (sys_rst)
- );
-
-
- endmodule
(50M的时钟周期20ns)
直接在vivado中进行仿真即可
可以看到正常写入数据
254写入后停止写入数据
正常读出是数据
1.FIFO的使用
2.PLL使用
3.异步模块之间需要打两排
4.PLL FIFO默认情况下1为复位信号(可修改)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。