FIFO在数据处理过程中是十分重要的。
同步FIFO比较简单,面试过程中手撕代码可能会用到。

module sFIFO #(parameter     DATA_WIDTH = 8,ADDR_WIDTH = 4
)
(
input clk,
input reset_p,
input wr_en,
input [DATA_WIDTH-1:0]wr_data,
input rd_en,
output reg[DATA_WIDTH-1:0]rd_data,
output full,
output empty
);
reg [ADDR_WIDTH-1:0] wr_addr;
reg [ADDR_WIDTH-1:0] rd_addr;
reg [DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0];//定义16个8位宽的寄存器
reg [ADDR_WIDTH:0] cnt;//默认先写后读
/读写地址
always@(posedge clk or negedge reset_p)beginif(!reset_p)wr_addr <= 'd0;else if(wr_en &&(!full))wr_addr <= wr_addr + 'd1;else wr_addr <= wr_addr;
endalways@(posedge clk or negedge reset_p)beginif(!reset_p)rd_addr <= 'd0;else if(rd_en &&(!empty))rd_addr <= rd_addr + 'd1;else rd_addr <= rd_addr;
end//读写数据
integer i;
always@(posedge clk or negedge reset_p)beginif(!reset_p)begin          //FIFO内所有数都为0for(i=0; i<16; i=i+1)beginram[i] <= 0;endendelse if(wr_en)beginram[wr_addr] <= wr_data;end
endalways@(posedge clk or negedge reset_p)beginif(!reset_p)begin//FIFO内所有数都为0rd_data <= 'd0;endelse if(wr_en)beginrd_data <= ram[rd_addr];end
end空满
always@(posedge clk or negedge reset_p)beginif(!reset_p) cnt <= 'd0;else if(wr_en && (!rd_en) && (cnt!=16))cnt <= cnt + 'd1;else if(rd_en && (!wr_en) && (cnt!=0))cnt <= cnt - 'd1;else;
endassign full = (cnt == 16);
assign empty = (cnt == 0);endmodule

对于异步FIFO来说,需要关注的点为跨时钟域以及空满判断的问题;
解决思路:
跨时钟域:1、对地址打两拍,避免亚稳态的产生;2、需要对读写地址进行格雷码转换,避免地址同步过程中产生意外。
空满判断:由于是异步电路,两时钟的频率可能不相同,若默认先写后读,若写时钟频率大于读时钟频率,则写满判断和读空判断条件一致(读写地址相同)。为解决此问题,提前给出读写指针,位宽比地址位宽大1,由此,可以得到写满标志为最高位不同,其他位相同。转换为格雷码的写满标志为最高2位不同,其他位相同。

module asFIFO#(
parameter   DATA_WIDTH = 8,ADDR_WIDTH = 4
)
(
input reset_p,
input wr_clk,
input wr_en,
input [DATA_WIDTH-1:0]wr_data,
input rd_clk,
input rd_en,
output reg[DATA_WIDTH-1:0]rd_data,
output wr_full,
output rd_empty
);reg [ADDR_WIDTH:0]wr_addr_ptr;
reg [ADDR_WIDTH:0]rd_addr_ptr;
wire [ADDR_WIDTH-1:0]wr_addr;
wire [ADDR_WIDTH-1:0]rd_addr;
//跨时钟域,打两拍
wire [ADDR_WIDTH:0]wr_addr_gray;
reg [ADDR_WIDTH:0]wr_addr_gray_r;
reg [ADDR_WIDTH:0]wr_addr_gray_r1;
wire [ADDR_WIDTH:0]rd_addr_gray;
reg [ADDR_WIDTH:0]rd_addr_gray_r;
reg [ADDR_WIDTH:0]rd_addr_gray_r1;
reg [DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0];//地址指针
always@(posedge wr_clk or negedge reset_p)if(!reset_p)wr_addr_ptr <= 'd0;else if(wr_en && (!wr_full))wr_addr_ptr <= wr_addr_ptr +1;else;always@(posedge rd_clk or negedge reset_p)if(!reset_p)rd_addr_ptr <= 'd0;else if(rd_en && (!rd_empty))rd_addr_ptr <= rd_addr_ptr +1;else;
//格雷码转换
assign wr_addr_gray = (wr_addr_ptr>>1 )^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr>>1 )^ rd_addr_ptr;//打拍always@(posedge wr_clk )beginwr_addr_gray_r <= wr_addr_gray;wr_addr_gray_r1 <= wr_addr_gray_r;
end
always@(posedge rd_clk )beginrd_addr_gray_r <= rd_addr_gray;rd_addr_gray_r1 <= rd_addr_gray_r;
end
//空满标志,重要
assign wr_full =(wr_addr_gray == {~rd_addr_gray_r1[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_gray_r1[ADDR_WIDTH-2:0]}) ;
assign rd_empty = (rd_addr_gray == wr_addr_gray_r1) ;assign wr_addr = wr_addr_ptr[ADDR_WIDTH-1:0];
assign rd_addr = rd_addr_ptr[ADDR_WIDTH-1:0];///读写数据
integer i;
always@(posedge wr_clk or negedge reset_p)if(!reset_p) for(i=0;i<2**ADDR_WIDTH;i=i+1) beginram[i] <= 'd0;endelse if(wr_en &&(!wr_full))ram[wr_addr] <= wr_data;else ;always@(posedge rd_clk or negedge reset_p)if(!reset_p)   rd_data <= 'd0;else if(rd_en && (!rd_empty))rd_data <= ram[rd_addr];else ;
endmodule

以上为面试过程中可能问到的问题,在FPGA的实际开发过程中,只需要我们调用IP核即可进行开发。

本项目使用的是ACX720开发板,配有DDR3 SDRAM。

为了实现FIFO模块与DDR控制器接口的对接,需要设计fifo2axi模块,将普通的FIFO接口转换为AXI接口,实现将FIFO里的数据读出然后储存在DDR储存器中以及将DDR储存器里的数据读出到FIFO中。

AXI4 总线的一大特征是它有 5 个独立的传输通道,这些通道都只支持单向传输。
作为类比,SPI 总线有 2 条单向传输通道:MISO, MOSI。SPI 输入和输出的数据,大路朝天,各走一条。
而作为对比, IIC 协议则只有 SDA 一条双向通道,输入输出数据只能在这一条通道上分时双向传输。
单向传输的通道意味着两端的终端节点是有身份差距的,好比水只能从上游流到下流。在 AXI 总线传输中,通道两端分为 Master 主机与 Slave 从机,主机总是发起读写请求的一方。常见的主机有CPU、DMA,而存储介质控制器(比如 DDR 控制器)则是典型的从机。主机可能通过从机读取或者写入存储介质。而显然从机不可能主动向 CPU 写入数据。
通道的读/写定义都是根据主机来定义的,那么五个通道都有谁呢:
读地址 (AR) read address
读数据 (R) read data
写地址 (AW) write address
写数据 (W) write data
写回复 (R) write response
以上内容摘自空白MAX的微博,对AXI协议有更加详细的描述!

以下为fifo2axi模块的设计思路:

localparam  S_IDLE    = 7'b0000001,S_ARB     = 7'b0000010,S_WR_ADDR = 7'b0000100,S_WR_DATA = 7'b0001000,S_WR_RESP = 7'b0010000,S_RD_ADDR = 7'b0100000,S_RD_RESP = 7'b1000000;reg [6:0]c_state;
reg [6:0]n_state;
wire wr_ddr3_req;
wire rd_ddr3_req;
reg wr_rd_poll;
reg [7:0]wr_data_cnt ;
wire[7:0]wr_req_cnt_thresh;
wire[7:0]rd_req_cnt_thresh;assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
assign rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <= rd_req_cnt_thresh) ? 1'b1:1'b0;//wr_rd_poll读写仲裁,默认先写后读
always@(posedge ui_clk or posedge ui_clk_sync_rst)
beginif(ui_clk_sync_rst)wr_rd_poll <= 1'b0;else if(c_state == S_ARB)wr_rd_poll <= ~wr_rd_poll;elsewr_rd_poll <= wr_rd_poll;
end  always@(posedge ui_clk or posedge ui_clk_sync_rst)
beginif(ui_clk_sync_rst)c_state <= S_IDLE;elsec_state <= n_state;
end
//状态机
always@(*)
begincase(c_state)S_IDLE:beginif(init_calib_complete && mmcm_locked)n_state = S_ARB;elsen_state = S_IDLE;endS_ARB:beginif((wr_ddr3_req == 1'b1) && (wr_rd_poll == 1'b0))n_state = S_WR_ADDR;else if((rd_ddr3_req == 1'b1) && (wr_rd_poll == 1'b1))n_state = S_RD_ADDR;elsen_state = S_ARB;end
//写地址S_WR_ADDR:beginif(m_axi_awvalid && m_axi_awready)n_state = S_WR_DATA;elsen_state = S_WR_ADDR;end
//写数据S_WR_DATA:beginif(m_axi_wready && m_axi_wvalid && m_axi_wlast)n_state = S_WR_RESP;elsen_state = S_WR_DATA;end
//写反馈 S_WR_RESP:beginif(m_axi_bvalid && m_axi_bready && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID))n_state = S_ARB;else if(m_axi_bvalid && m_axi_bready)n_state = S_IDLE;else n_state = S_WR_RESP;end
//读地址S_RD_ADDR:beginif(m_axi_arvalid && m_axi_arready)n_state = S_RD_RESP;elsen_state = S_RD_ADDR;end
//读数据+读反馈S_RD_RESP: beginif(m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00) && (m_axi_rid == AXI_ID))n_state = S_ARB;else if(m_axi_rvalid && m_axi_rready && m_axi_rlast)n_state = S_IDLE;elsen_state = S_RD_RESP;enddefault: n_state = S_IDLE ;endcase
end

在5条通道的状态机完成之后,就是产生状态机中各判断信号。
包括写地址/写地址有效,写数据有效/写最后,读地址/读地址有效。

//awaaddr 的产生
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_awaddr <= WR_DDR_ADDR_BEGIN;else if(wr_addr_clr)m_axi_awaddr <= WR_DDR_ADDR_BEGIN;else if(m_axi_awaddr >= WR_DDR_ADDR_END)m_axi_awaddr <= WR_DDR_ADDR_BEGIN;else if((c_state == S_WR_RESP) && m_axi_bvalid && m_axi_bready && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID))m_axi_awaddr <= m_axi_awaddr + ((m_axi_awlen + 1'b1) << 4);else m_axi_awaddr <= m_axi_awaddr;end//awvalid
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_awvalid <= 1'b0;else if((c_state == S_WR_ADDR) && m_axi_awready && m_axi_awvalid)m_axi_awvalid <= 1'b0;      else if(c_state == S_WR_ADDR)m_axi_awvalid <= 1'b1;else m_axi_awvalid <= m_axi_awvalid;end//wvalid
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_wvalid <= 1'b0;else if(c_state == S_WR_DATA)m_axi_wvalid <= 1'b1;else if((c_state == S_WR_DATA) && m_axi_wvalid && m_axi_wready && m_axi_wlast)m_axi_wvalid <= 1'b0;else m_axi_wvalid <= m_axi_wvalid;end//wlast
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)wr_data_cnt <= 1'b0;else if(c_state == S_ARB)wr_data_cnt <= 1'b0;else if((c_state == S_WR_DATA) && m_axi_wvalid && m_axi_wready )wr_data_cnt <= wr_data_cnt + 1'b1;else wr_data_cnt <= wr_data_cnt;
endalways@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_wlast <= 1'b0;else if((c_state == S_WR_DATA) && m_axi_wvalid && m_axi_wready && m_axi_wlast)m_axi_wlast <= 1'b0;else if((c_state == S_WR_DATA) && m_axi_awlen == 8'd0 )m_axi_wlast <= 1'b1;       else if((c_state == S_WR_DATA) && m_axi_wvalid && m_axi_wready && (wr_data_cnt == m_axi_awlen - 1'b1))m_axi_wlast <= 1'b1;else m_axi_wlast <= m_axi_wlast;
end//araddr
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_araddr <= RD_DDR_ADDR_BEGIN;else if(rd_addr_clr)m_axi_araddr <= RD_DDR_ADDR_BEGIN;else if(m_axi_araddr >= RD_DDR_ADDR_END)m_axi_araddr <= RD_DDR_ADDR_BEGIN;else if((c_state == S_RD_RESP) && m_axi_rvalid && m_axi_rready && (m_axi_rresp == 2'b00) && m_axi_rlast  && (m_axi_rid == AXI_ID))m_axi_araddr <= m_axi_araddr + ((m_axi_awlen + 1'b1) << 4);else m_axi_araddr <= m_axi_araddr;end//arvalid
always@(posedge ui_clk or posedge ui_clk_sync_rst) begin if(ui_clk_sync_rst)m_axi_arvalid <= 1'b0;else if(c_state == S_RD_ADDR && m_axi_arready && m_axi_arvalid)m_axi_arvalid <= 1'b0;else if(c_state == S_RD_ADDR)m_axi_arvalid <= 1'b1;else m_axi_arvalid <= m_axi_arvalid;end

DDR3的读写请求是根据读写FIFO数据量确定的,当写FIFO中数据大于一定量(设置为突发读写数据长度)时,产生写DDR请求;当读FIFO中的数据小于一定量时,产生读DDR请求。

assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
assign rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <= rd_req_cnt_thresh) ? 1'b1:1'b0;

基于FPGA的目标颜色识别追踪三——FIFO(同/异步FIFO)、DDR3相关推荐

  1. 基于FPGA的目标颜色识别追踪一——图像预处理

    为了解决后续对于固定颜色的识别,需要对OV5640摄像头采集的图像进行预处理.预处理的思路为rgb2YCbCr corrode expand 之后对预处理的图片进行边界提取. 图像预处理 下面给出了常 ...

  2. 毕业设计-基于机器视觉的电线颜色识别系统软件- OpenCV

    目录 前言 课题背景和意义 实现技术思路 一.图像预处理 三.电线颜色特征提取方法的研究 四.基于 BP神经网络的颜色识别算法实现 实现效果图样例 最 前言

  3. (ICCV-2021)TransReID:基于transformer的目标重识别

    TransReID:基于transformer的目标重识别 paper题目:TransReID: Transformer-based Object Re-Identification paper是浙大 ...

  4. 认知电子战 (2.2):基于机器学习的目标状态识别

    文章目录 引入 1 已知目标状态 1.1 基于有监督分类 1.2 基于无监督聚类 2 未知目标状态 2.1 未知状态检测 2.2 增量式机器学习 引入   本节主要介绍基于机器学习的目标状态识别.以下 ...

  5. 数字图像处理(DIP)实验4 目标颜色识别

    数字图像处理(DIP)实验4 目标颜色识别 数字图像处理课程相关文章 传送门 https://blog.csdn.net/qq_46164507/article/details/122503851 博 ...

  6. 基于FPGA的VGA接口设计(三)

    关于VGA系列文章的所有链接: 基于FPGA的VGA接口设计(一) 基于FPGA的VGA接口设计(二) 基于FPGA的VGA接口设计(三) 结篇   在之前的文章中介绍了有关VGA的扫描方式.行场同步 ...

  7. 物联网毕设 - 基于Kociemba算法和颜色识别的解魔方机器人 - 单片机

    ⭐️基于Kociemba算法和颜色识别的解魔方机器人⭐️ STM32开发板 亚克力机械手 全志 ARMPC C30 开发板 Kociemba算法 安卓APP 源码: https://blog.csdn ...

  8. 基于FPGA的快速傅里叶变换加速(三)

    基于FPGA的快速傅里叶变换加速(三) 硬件加速介绍及部分verilog代码实现 1. 硬件加速 1.1 FPGA 1.1.1 FPGA介绍 概念: 基本结构: 工作原理: 1.1.2 开发板 开发板 ...

  9. 目标检测YOLO实战应用案例100讲-基于FPGA的目标检测硬件加速技术及其应用研究

    目录 基于FPGA的目标检测加速器设计 目标检测算法与加速方法 2.1 YOLO v2算法

最新文章

  1. R语言计算平均值的标准误差(standard error of the mean):自定义函数计算平均值的标准误差、使用plotrix包的std.error函数计算平均值的标准误差
  2. 【Android RTMP】音频数据采集编码 ( 音频数据采集编码 | AAC 高级音频编码 | FAAC 编码器 | Ubuntu 交叉编译 FAAC 编码器 )
  3. Linux系统管理的基本入手点
  4. 得到按钮所在DIV的ID
  5. 洛谷 U3357 C2-走楼梯
  6. jquery.cookie中的操作
  7. mysql mha reference_MySQL MHA配置常见问题
  8. 写给大忙人看的java_《写给大忙人看的Java核心技术》读后感
  9. 大数据对医学发展有什么影响
  10. redis客户端分析
  11. android jni 多线程,[Android]JNI进阶--线程池
  12. 反转链表与链表实现两数相加(简单思路)
  13. ImageView和ImageButton的区别
  14. Topaz Video Enhance Al视频无损放大软件常见问题指南
  15. vue下拉el-select三级联动(公司-部门-人员)
  16. 为kubernetes(k8s)单独配置kubectl工具
  17. C++_primer_plus学习笔记 第14章 C++中的代码重用
  18. VScode插件Markdown Preview Enhanced自定义字体style
  19. 2022年计算机软件水平考试信息系统项目管理(高级)练习题及答案
  20. 算法 - 求两个自然数的最大公约数(C++)

热门文章

  1. Windows11 ISO原版系统镜像
  2. 冬天吃柿子养颜防衰老
  3. javascript大全
  4. 【python】自动登录51cto家园
  5. 满分作文生成器:生活在代码上
  6. java导出乱码_导出文件乱码问题处理(java)
  7. Qt编写自定义控件37-发光按钮(会呼吸的痛)
  8. 极限、可导、可微、连续之间的关系
  9. iOS模拟器不能输入中文解决
  10. 光伏电站运维管理方案