软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述
uifdma_dbuf IP是米联客研发用于配合FDMA完成数据传输控制的IP模块。FDMA-DBUF IP代码采用“对称设计”方法,读写代码对称,好处是代码结构清晰,读写过程一致,代码效率高,更加容易维护。本文只用到了写通道部分,读通道部分没有用到,但是我们还是介绍下。 uifdma_dbuf的信号接口包含了: AXI-LITE接口:用于PS端获取当前的中断帧号; FDMA_M接口:和FDMA相连接的数据接口 ud_wx接口: uifdma_dbuf写数据通路接口 ud_rx接口: uifdma_dbuf读数据通路接口 以下只介绍uifdma_dbuf 的ud写数据通道和ud读数据通道 2uifdma_dbuf 的ud信号定义写数据接口信号定义 信号名称 | 方向 | 位宽 | 功能描述 | ud_wclk | input | 1 | 写数据通路时钟 | ud_wvs | input | 1 | 写数据帧同步信号,当使能视频功能,每个ud_wvs的上升沿进行帧同步,否则该值设置为1 | ud_wde | input | 1 | 写数据数据有效,高电平有效 | ud_wdata | input | 32~128 | 写数据 | ud_wfull | output | 1 | 写数据FIFO满,当FIFO满,继续写入会导致数据溢出 | wbuf_sync_o | output | 7 | 写数据帧同步输出,代表了正在操作的缓存号 | wbuf_sync_i | input | 7 | 写数据帧同步输入, 帧同步关系到内部缓存地址切换 | fdma_wbuf | output | 7 | 当fdma_wirq中断产生后,代表了当前已经写到DDR完成的帧缓存号 | fdma_wirq | output | 1 | 写数据完成中断 |
读数据接口信号定义 信号名称 | 方向 | 位宽 | 功能描述 | ud_rclk | input | 1 | 读数据通路时钟 | ud_rvs | input | 1 | 读数据帧同步信号,当使能视频功能,每个ud_rvs的上升沿进行帧同步,否则该值设置为1 | ud_rde | input | 1 | 读数据数据有效,高电平有效 | ud_rdata | input | 32~128 | 读数据 | ud_rempty | output | 1 | 读数据FIFO空,当FIFO非空,可以读出数据,当fdma工作在非视频模式下,可以用该信号去使能ud_rde | rbuf_sync_o | output | 7 | 读数据帧同步输出 ,代表了正在操作的缓存号 | rbuf_sync_i | input | 7 | 读数据帧同步输入, 帧同步关系到内部缓存地址切换 | fdma_rbuf | output | 7 | 当fdma_rirq中断产生后,代表了当前已经读到DDR完成的帧缓存号 | fdma_rirq | output |
| 读数据完成中断 | 3FDMA-DBUF IP代码分析FDMA-DBUF IP代码采用“对称设计”方法,读写代码对称,好处是代码结构清晰,读写过程一致,代码效率高,更加容易维护。 1:FDMA-DBUF写状态机为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。
2:FDMA的写时序波形图
fdma_wready设置为1,当fdma_wbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_wreq=1,同时设置fdma burst的起始地址和fdma_wsize本次需要传输的数据大小(以bytes为单位)。当fdma_wvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_wvalid和fdma_wbusy变为0。 AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。 3:FDMA-DBUF写状态机读数据的过程和写数据的过程是对称的,状态机如下:
为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。 4:FDMA的读时序波形图
fdma_rready设置为1,当fdma_rbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_rreq=1,同时设置fdma burst的起始地址和fdma_rsize本次需要传输的数据大小(以bytes为单位)。当fdma_rvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_rvalid和fdma_rbusy变为0。 同样对于AXI4总线的读操作,AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。 4源码分析米联客自定义IP路径在配套的soc_prj/uisrc/03_ip路径下,如下图所示:
代码的层次结构如下:
源码部分一共有包括4个文件: fs_cap.v该文件用于帧同步信号的抓取,采样边沿抓取方式。 uidbufirq.v文件用来保存一帧数据发送完毕后产生的中断,ps部分可以通过axi-lite接口读取中断值知道哪一个地址完成数据传输。 uidbuf.v文件是完成用户数据到FDMA接口数据转换的关键代码,同时该代码完成了中断控制,帧缓存控制。 uifdma_dbuf.v该文件是该IP的顶层文件 1:uidbuf.v
- /*******************************MILIANKE*******************************
- *Company : MiLianKe Electronic Technology Co., Ltd.
- *WebSite:https://www.milianke.com
- *TechWeb:https://www.uisrc.com
- *tmall-shop:https://milianke.tmall.com
- *jd-shop:https://milianke.jd.com
- *taobao-shop1: https://milianke.taobao.com
- *Create Date: 2021/10/15
- *File Name: uidbuf.v
- *Description:
- *Declaration:
- *The reference demo provided by Milianke is only used for learning.
- *We cannot ensure that the demo itself is free of bugs, so users
- *should be responsible for the technical problems and consequences
- *caused by the use of their own products.
- *Copyright: Copyright (c) MiLianKe
- *All rights reserved.
- *Revision: 3.1
- *Signal description
- *1) I_ input
- *2) O_ output
- *3) IO_ input output
- *4) S_ system internal signal
- *5) _n activ low
- *6) _dg debug signal
- *7) _r delay or register
- *8) _s state mechine
- *********************************************************************/
- /*********uidbuf(fdma data buffer controller)基于FDMA信号时序的缓存控制器***********
- --以下是米联客设计的uidbuf(fdma data buffer controller)基于FDMA信号时序的缓存控制器
- --1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨
- --2.读写通道独立,FIFO大小,数据位宽可以灵活设置,适合用于基于RGB时序的视频数据或者数据流传输
- 版本说明:
- --3.1 修改信号命名规则
- *********************************************************************/
- `timescale 1ns / 1ns
- module uidbuf#(
- parameter integer VIDEO_ENABLE = 1,//使能视频帧支持功能
- parameter integer ENABLE_WRITE = 1,//使能写通道
- parameter integer ENABLE_READ = 1,//使能读通道
- parameter integer AXI_DATA_WIDTH = 128,//AXI总线数据位宽
- parameter integer AXI_ADDR_WIDTH = 32, //AXI总线地址位宽
- parameter integer W_BUFDEPTH = 2048, //写通道AXI设置FIFO缓存大小
- parameter integer W_DATAWIDTH = 32, //写通道AXI设置数据位宽大小
- parameter [AXI_ADDR_WIDTH -1'b1: 0] W_BASEADDR = 0, //写通道设置内存起始地址
- parameter integer W_DSIZEBITS = 24, //写通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
- parameter integer W_XSIZE = 1920, //写通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
- parameter integer W_XSTRIDE = 1920, //写通道设置X方向的Stride值,主要用于图形缓存应用
- parameter integer W_YSIZE = 1080, //写通道设置Y方向值,代表了进行了多少次XSIZE传输
- parameter integer W_XDIV = 2, //写通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
- parameter integer W_BUFSIZE = 3, //写通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数
- parameter integer R_BUFDEPTH = 2048, //读通道AXI设置FIFO缓存大小
- parameter integer R_DATAWIDTH = 32, //读通道AXI设置数据位宽大小
- parameter [AXI_ADDR_WIDTH -1'b1: 0] R_BASEADDR = 0, //读通道设置内存起始地址
- parameter integer R_DSIZEBITS = 24, //读通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
- parameter integer R_XSIZE = 1920, //读通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
- parameter integer R_XSTRIDE = 1920, //读通道设置X方向的Stride值,主要用于图形缓存应用
- parameter integer R_YSIZE = 1080, //读通道设置Y方向值,代表了进行了多少次XSIZE传输
- parameter integer R_XDIV = 2, //读通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
- parameter integer R_BUFSIZE = 3 //读通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数
- )
- (
- input wire I_ui_clk, //和FDMA AXI总线时钟一致
- input wire I_ui_rstn, //和FDMA AXI复位一致
- //sensor input -W_FIFO--------------
- input wire I_W_wclk, //用户写数据接口时钟
- input wire I_W_FS, //用户写数据接口同步信号,对于非视频帧一般设置为1
- input wire I_W_wren, //用户写数据使能
- input wire [W_DATAWIDTH-1'b1 : 0] I_W_data, //用户写数据
- output reg [7 :0] O_W_sync_cnt =0, //写通道BUF帧同步输出
- input wire [7 :0] I_W_buf, // 写通道BUF帧同步输入
- output wire O_W_full,
- //----------fdma signals write-------
- output wire [AXI_ADDR_WIDTH-1'b1: 0] O_fdma_waddr, //FDMA写通道地址
- output wire O_fdma_wareq, //FDMA写通道请求
- output wire [15 :0] O_fdma_wsize, //FDMA写通道一次FDMA的传输大小
- input wire I_fdma_wbusy, //FDMA处于BUSY状态,AXI总线正在写操作
- output wire [AXI_DATA_WIDTH-1'b1:0] O_fdma_wdata, //FDMA写数据
- input wire I_fdma_wvalid, //FDMA 写有效
- output wire O_fdma_wready, //FDMA写准备好,用户可以写数据
- output reg [7 :0] O_fdma_wbuf =0, //FDMA的写帧缓存号输出
- output wire O_fdma_wirq, //FDMA一次写完成的数据传输完成后,产生中断。
- //----------fdma signals read-------
- input wire I_R_rclk, //用户读数据接口时钟
- input wire I_R_FS, //用户读数据接口同步信号,对于非视频帧一般设置1
- input wire I_R_rden, //用户读数据使能
- output wire [R_DATAWIDTH-1'b1 : 0] O_R_data, //用户读数据
- output reg [7 :0] O_R_sync_cnt =0, //读通道BUF帧同步输出
- input wire [7 :0] I_R_buf, //写通道BUF帧同步输入
- output wire O_R_empty,
- output wire [AXI_ADDR_WIDTH-1'b1: 0] O_fdma_raddr, // FDMA读通道地址
- output wire O_fdma_rareq, // FDMA读通道请求
- output wire [15: 0] O_fdma_rsize, // FDMA读通道一次FDMA的传输大小
- input wire I_fdma_rbusy, // FDMA处于BUSY状态,AXI总线正在读操作
- input wire [AXI_DATA_WIDTH-1'b1:0] I_fdma_rdata, // FDMA读数据
- input wire I_fdma_rvalid, // FDMA 读有效
- output wire O_fdma_rready, // FDMA读准备好,用户可以读数据
- output reg [7 :0] O_fdma_rbuf =0, // FDMA的读帧缓存号输出
- output wire O_fdma_rirq // FDMA一次读完成的数据传输完成后,产生中断
- );
- // 计算Log2
- function integer clog2;
- input integer value;
- begin
- for (clog2=0; value>0; clog2=clog2+1)
- value = value>>1;
- end
- endfunction
- //FDMA读写状态机的状态值,一般4个状态值即可
- localparam S_IDLE = 2'd0;
- localparam S_RST = 2'd1;
- localparam S_DATA1 = 2'd2;
- localparam S_DATA2 = 2'd3;
- // 通过设置通道使能,可以优化代码的利用率
- generate if(ENABLE_WRITE == 1)begin : FDMA_WRITE_ENABLE
- localparam WFIFO_DEPTH = W_BUFDEPTH; //写通道FIFO深度
- localparam W_WR_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH); //计算FIFO的写通道位宽
- localparam W_RD_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH*W_DATAWIDTH/AXI_DATA_WIDTH);//clog2(WFIFO_DEPTH/(AXI_DATA_WIDTH/W_DATAWIDTH))+1;
- localparam WYBUF_SIZE = (W_BUFSIZE - 1'b1); //写通道需要完成多少次XSIZE操作
- localparam WY_BURST_TIMES = (W_YSIZE*W_XDIV); //写通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
- localparam FDMA_WX_BURST = (W_XSIZE*W_DATAWIDTH/AXI_DATA_WIDTH)/W_XDIV; //FDMA BURST 一次的大小
- localparam WX_BURST_ADDR_INC = (W_XSIZE*(W_DATAWIDTH/8))/W_XDIV; //FDMA每次burst之后的地址增加
- localparam WX_LAST_ADDR_INC = (W_XSTRIDE-W_XSIZE)*(W_DATAWIDTH/8) + WX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址
- (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire W_wren_ri = I_W_wren;
- assign O_fdma_wready = 1'b1;
- reg O_fdma_wareq_r= 1'b0;
- reg W_FIFO_Rst=0;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire W_FS;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [1 :0] W_MS=0;
- reg [W_DSIZEBITS-1'b1:0] W_addr=0;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [15:0] W_bcnt=0;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire[W_RD_DATA_COUNT_WIDTH-1'b1 :0] W_rcnt;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg W_REQ=0;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [5 :0] wirq_dly_cnt =0;
- reg [3 :0] wdiv_cnt =0;
- reg [7 :0] wrst_cnt =0;
- reg [7 :0] O_fdma_wbufn;
- (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire wirq= O_fdma_wirq;
- assign O_fdma_wsize = FDMA_WX_BURST;
- assign O_fdma_wirq = (wirq_dly_cnt>0);
- assign O_fdma_waddr = W_BASEADDR + {O_fdma_wbufn,W_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置
- reg [1:0] W_MS_r =0;
- always @(posedge I_ui_clk) W_MS_r <= W_MS;
- //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
- always @(posedge I_ui_clk) begin
- if(I_ui_rstn == 1'b0)begin
- wirq_dly_cnt <= 6'd0;
- O_fdma_wbuf <=0;
- end
- else if((W_MS_r == S_DATA2) && (W_MS == S_IDLE))begin
- wirq_dly_cnt <= 60;
- O_fdma_wbuf <= O_fdma_wbufn;
- end
- else if(wirq_dly_cnt >0)
- wirq_dly_cnt <= wirq_dly_cnt - 1'b1;
- end
- //帧同步,对于视频有效
- fs_cap #
- (
- .VIDEO_ENABLE(VIDEO_ENABLE)
- )
- fs_cap_W0
- (
- .I_clk(I_ui_clk),
- .I_rstn(I_ui_rstn),
- .I_vs(I_W_FS),
- .O_fs_cap(W_FS)
- );
- assign O_fdma_wareq = O_fdma_wareq_r;
- //写通道状态机,采用4个状态值描述
- always @(posedge I_ui_clk) begin
- if(!I_ui_rstn)begin
- W_MS <= S_IDLE;
- W_FIFO_Rst <= 0;
- W_addr <= 0;
- O_W_sync_cnt <= 0;
- W_bcnt <= 0;
- wrst_cnt <= 0;
- wdiv_cnt <= 0;
- O_fdma_wbufn <= 0;
- O_fdma_wareq_r <= 1'd0;
- end
- else begin
- case(W_MS)
- S_IDLE:begin
- W_addr <= 0;
- W_bcnt <= 0;
- wrst_cnt <= 0;
- wdiv_cnt <=0;
- if(W_FS) begin //帧同步,对于非视频数据一般常量为1
- W_MS <= S_RST;
- if(O_W_sync_cnt < WYBUF_SIZE) //输出帧同步计数器
- O_W_sync_cnt <= O_W_sync_cnt + 1'b1;
- else
- O_W_sync_cnt <= 0;
- end
- end
- S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
- O_fdma_wbufn <= I_W_buf;
- wrst_cnt <= wrst_cnt + 1'b1;
- if((VIDEO_ENABLE == 1) && (wrst_cnt < 40))
- W_FIFO_Rst <= 1;
- else if((VIDEO_ENABLE == 1) && (wrst_cnt < 100))
- W_FIFO_Rst <= 0;
- else if(O_fdma_wirq == 1'b0) begin
- W_MS <= S_DATA1;
- end
- end
- S_DATA1:begin //发送写FDMA请求
- if(I_fdma_wbusy == 1'b0 && W_REQ )begin
- O_fdma_wareq_r <= 1'b1;
- end
- else if(I_fdma_wbusy == 1'b1) begin
- O_fdma_wareq_r <= 1'b0;
- W_MS <= S_DATA2;
- end
- end
- S_DATA2:begin //写有效数据
- if(I_fdma_wbusy == 1'b0)begin
- if(W_bcnt == WY_BURST_TIMES - 1'b1) //判断是否传输完毕
- W_MS <= S_IDLE;
- else begin
- if(wdiv_cnt < W_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
- W_addr <= W_addr + WX_BURST_ADDR_INC; //计算地址增量
- wdiv_cnt <= wdiv_cnt + 1'b1;
- end
- else begin
- W_addr <= W_addr + WX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
- wdiv_cnt <= 0;
- end
- W_bcnt <= W_bcnt + 1'b1;
- W_MS <= S_DATA1;
- end
- end
- end
- default: W_MS <= S_IDLE;
- endcase
- end
- end
- //写通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储的数据阈值达到一定量,一般满足一次FDMA的burst即可发出请求
- wire W_rbusy;
- always@(posedge I_ui_clk)
- W_REQ <= (W_rcnt > FDMA_WX_BURST - 2)&&(~W_rbusy);
- xpm_fifo_async # (
- .FIFO_MEMORY_TYPE ("auto"), //string; "auto", "block", or "distributed";
- .ECC_MODE ("no_ecc"), //string; "no_ecc" or "en_ecc";
- .RELATED_CLOCKS (0), //positive integer; 0 or 1
- .FIFO_WRITE_DEPTH (WFIFO_DEPTH), //positive integer
- .WRITE_DATA_WIDTH (W_DATAWIDTH), //positive integer
- .WR_DATA_COUNT_WIDTH (W_WR_DATA_COUNT_WIDTH), //positive integer
- .PROG_FULL_THRESH (20), //positive integer
- .FULL_RESET_VALUE (0), //positive integer; 0 or 1
- .USE_ADV_FEATURES ("0707"), //string; "0000" to "1F1F";
- .READ_MODE ("fwft"), //string; "std" or "fwft";
- .FIFO_READ_LATENCY (0), //positive integer;
- .READ_DATA_WIDTH (AXI_DATA_WIDTH), //positive integer
- .RD_DATA_COUNT_WIDTH (W_RD_DATA_COUNT_WIDTH), //positive integer
- .PROG_EMPTY_THRESH (10), //positive integer
- .DOUT_RESET_VALUE ("0"), //string
- .CDC_SYNC_STAGES (2), //positive integer
- .WAKEUP_TIME (0) //positive integer; 0 or 2;
- ) xpm_fifo_W_inst (
- .rst ((I_ui_rstn == 1'b0) || (W_FIFO_Rst == 1'b1)),
- .wr_clk (I_W_wclk),
- .wr_en (I_W_wren),
- .din (I_W_data),
- .full (O_W_full),
- .overflow (),
- .prog_full (),
- .wr_data_count (),
- .almost_full (),
- .wr_ack (),
- .wr_rst_busy (),
- .rd_clk (I_ui_clk),
- .rd_en (I_fdma_wvalid),
- .dout (O_fdma_wdata),
- .empty (),
- .underflow (),
- .rd_rst_busy (W_rbusy),
- .prog_empty (),
- .rd_data_count (W_rcnt),
- .almost_empty (),
- .data_valid (),
- .sleep (1'b0),
- .injectsbiterr (1'b0),
- .injectdbiterr (1'b0),
- .sbiterr (),
- .dbiterr ()
- );
- end
- else begin : FDMA_WRITE_DISABLE
- //----------fdma signals write-------
- assign O_fdma_waddr = 0;
- assign O_fdma_wareq = 0;
- assign O_fdma_wsize = 0;
- assign O_fdma_wdata = 0;
- assign O_fdma_wready = 0;
- assign O_fdma_wirq = 0;
- assign O_W_full = 0;
- end
- endgenerate
- generate if(ENABLE_READ == 1)begin : FDMA_READ// 通过设置通道使能,可以优化代码的利用率
- localparam RYBUF_SIZE = (R_BUFSIZE - 1'b1); //读通道需要完成多少次XSIZE操作
- localparam RY_BURST_TIMES = (R_YSIZE*R_XDIV); //读通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
- localparam FDMA_RX_BURST = (R_XSIZE*R_DATAWIDTH/AXI_DATA_WIDTH)/R_XDIV; //FDMA BURST 一次的大小
- localparam RX_BURST_ADDR_INC = (R_XSIZE*(R_DATAWIDTH/8))/R_XDIV; //FDMA每次burst之后的地址增加
- localparam RX_LAST_ADDR_INC = (R_XSTRIDE-R_XSIZE)*(R_DATAWIDTH/8) + RX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址
- localparam RFIFO_DEPTH = R_BUFDEPTH*R_DATAWIDTH/AXI_DATA_WIDTH;//R_BUFDEPTH/(AXI_DATA_WIDTH/R_DATAWIDTH);
- localparam R_WR_DATA_COUNT_WIDTH = clog2(RFIFO_DEPTH); //读通道FIFO 输入部分深度
- localparam R_RD_DATA_COUNT_WIDTH = clog2(R_BUFDEPTH); //写通道FIFO输出部分深度
- assign O_fdma_rready = 1'b1;
- reg O_fdma_rareq_r= 1'b0;
- reg R_FIFO_Rst=0;
- wire R_FS;
- reg [1 :0] R_MS=0;
- reg [R_DSIZEBITS-1'b1:0] R_addr=0;
- reg [15:0] R_bcnt=0;
- wire[R_WR_DATA_COUNT_WIDTH-1'b1 :0] R_wcnt;
- reg R_REQ=0;
- reg [5 :0] rirq_dly_cnt =0;
- reg [3 :0] rdiv_cnt =0;
- reg [7 :0] rrst_cnt =0;
- reg [7 :0] O_fdma_rbufn;
- assign O_fdma_rsize = FDMA_RX_BURST;
- assign O_fdma_rirq = (rirq_dly_cnt>0);
- assign O_fdma_raddr = R_BASEADDR + {O_fdma_rbufn,R_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置
- reg [1:0] R_MS_r =0;
- always @(posedge I_ui_clk) R_MS_r <= R_MS;
- //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
- always @(posedge I_ui_clk) begin
- if(I_ui_rstn == 1'b0)begin
- rirq_dly_cnt <= 6'd0;
- O_fdma_rbuf <=0;
- end
- else if((R_MS_r == S_DATA2) && (R_MS == S_IDLE))begin
- rirq_dly_cnt <= 60;
- O_fdma_rbuf <= O_fdma_rbufn;
- end
- else if(rirq_dly_cnt >0)
- rirq_dly_cnt <= rirq_dly_cnt - 1'b1;
- end
- //帧同步,对于视频有效
- fs_cap #
- (
- .VIDEO_ENABLE(VIDEO_ENABLE)
- )
- fs_cap_R0
- (
- .I_clk(I_ui_clk),
- .I_rstn(I_ui_rstn),
- .I_vs(I_R_FS),
- .O_fs_cap(R_FS)
- );
- assign O_fdma_rareq = O_fdma_rareq_r;
- //读通道状态机,采用4个状态值描述
- always @(posedge I_ui_clk) begin
- if(!I_ui_rstn)begin
- R_MS <= S_IDLE;
- R_FIFO_Rst <= 0;
- R_addr <= 0;
- O_R_sync_cnt <= 0;
- R_bcnt <= 0;
- rrst_cnt <= 0;
- rdiv_cnt <= 0;
- O_fdma_rbufn <= 0;
- O_fdma_rareq_r <= 1'd0;
- end
- else begin
- case(R_MS) //帧同步,对于非视频数据一般常量为1
- S_IDLE:begin
- R_addr <= 0;
- R_bcnt <= 0;
- rrst_cnt <= 0;
- rdiv_cnt <=0;
- if(R_FS) begin
- R_MS <= S_RST;
- if(O_R_sync_cnt < RYBUF_SIZE) //输出帧同步计数器,当需要用读通道做帧同步的时候使用
- O_R_sync_cnt <= O_R_sync_cnt + 1'b1;
- else
- O_R_sync_cnt <= 0;
- end
- end
- S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
- O_fdma_rbufn <= I_R_buf;
- rrst_cnt <= rrst_cnt + 1'b1;
- if((VIDEO_ENABLE == 1) && (rrst_cnt < 40))
- R_FIFO_Rst <= 1;
- else if((VIDEO_ENABLE == 1) && (rrst_cnt < 100))
- R_FIFO_Rst <= 0;
- else if(O_fdma_rirq == 1'b0) begin
- R_MS <= S_DATA1;
- end
- end
- S_DATA1:begin
- if(I_fdma_rbusy == 1'b0 && R_REQ)begin
- O_fdma_rareq_r <= 1'b1;
- end
- else if(I_fdma_rbusy == 1'b1) begin
- O_fdma_rareq_r <= 1'b0;
- R_MS <= S_DATA2;
- end
- end
- S_DATA2:begin //写有效数据
- if(I_fdma_rbusy == 1'b0)begin
- if(R_bcnt == RY_BURST_TIMES - 1'b1) //判断是否传输完毕
- R_MS <= S_IDLE;
- else begin
- if(rdiv_cnt < R_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
- R_addr <= R_addr + RX_BURST_ADDR_INC; //计算地址增量
- rdiv_cnt <= rdiv_cnt + 1'b1;
- end
- else begin
- R_addr <= R_addr + RX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
- rdiv_cnt <= 0;
- end
- R_bcnt <= R_bcnt + 1'b1;
- R_MS <= S_DATA1;
- end
- end
- end
- default:R_MS <= S_IDLE;
- endcase
- end
- end
- //读通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储空间有足够空余,满足一次FDMA的burst即可发出请求
- wire R_wbusy;
- always@(posedge I_ui_clk)
- R_REQ <= (R_wcnt < FDMA_RX_BURST - 2)&&(~R_wbusy);
- xpm_fifo_async # (
- .FIFO_MEMORY_TYPE ("auto"), //string; "auto", "block", or "distributed";
- .ECC_MODE ("no_ecc"), //string; "no_ecc" or "en_ecc";
- .RELATED_CLOCKS (0), //positive integer; 0 or 1
- .FIFO_WRITE_DEPTH (RFIFO_DEPTH), //positive integer
- .WRITE_DATA_WIDTH (AXI_DATA_WIDTH), //positive integer
- .WR_DATA_COUNT_WIDTH (R_WR_DATA_COUNT_WIDTH), //positive integer
- .PROG_FULL_THRESH (20), //positive integer
- .FULL_RESET_VALUE (0), //positive integer; 0 or 1
- .USE_ADV_FEATURES ("0707"), //string; "0000" to "1F1F";
- .READ_MODE ("fwft"), //string; "std" or "fwft";
- .FIFO_READ_LATENCY (0), //positive integer;
- .READ_DATA_WIDTH (R_DATAWIDTH), //positive integer
- .RD_DATA_COUNT_WIDTH (R_RD_DATA_COUNT_WIDTH), //positive integer
- .PROG_EMPTY_THRESH (10), //positive integer
- .DOUT_RESET_VALUE ("0"), //string
- .CDC_SYNC_STAGES (2), //positive integer
- .WAKEUP_TIME (0) //positive integer; 0 or 2;
- ) xpm_fifo_R_inst (
- .rst ((I_ui_rstn == 1'b0) || (R_FIFO_Rst == 1'b1)),
- .wr_clk (I_ui_clk),
- .wr_en (I_fdma_rvalid),
- .din (I_fdma_rdata),
- .full (),
- .overflow (),
- .prog_full (),
- .wr_data_count (R_wcnt),
- .almost_full (),
- .wr_ack (),
- .wr_rst_busy (R_wbusy),
- .rd_clk (I_R_rclk),
- .rd_en (I_R_rden),
- .dout (O_R_data),
- .empty (O_R_empty),
- .underflow (),
- .rd_rst_busy (),
- .prog_empty (),
- .rd_data_count (),
- .almost_empty (),
- .data_valid (),
- .sleep (1'b0),
- .injectsbiterr (1'b0),
- .injectdbiterr (1'b0),
- .sbiterr (),
- .dbiterr ()
- );
- end
- else begin : FDMA_READ_DISABLE
-
- assign O_fdma_raddr = 0;
- assign O_fdma_rareq = 0;
- assign O_fdma_rsize = 0;
- //assign I_fdma_rdata = 0;
- assign O_fdma_rready = 0;
- assign O_fdma_rirq = 0;
- assign O_R_empty = 1'b0;
- end
- endgenerate
- endmodule
复制代码
3:uidbufirq.v 这个部分的功能只有在PS需要获取中断后的帧缓存通道才会用到,PS通过axi-lite接口可以读取到寄存器的值。 关于更多AXI4总线相关知识可以阅读《3-2-03米联客2024版AXI4总线专题》这个章节,这个章节专讲AXI4总线,其中也详细讲解了FDMA的代码分析。 这里主要看代码的最后,根据中断信号寄存内存的中断号 - `timescale 1ns / 1ps
- /*******************************MILIANKE*******************************
- *Company : MiLianKe Electronic Technology Co., Ltd.
- *WebSite:https://www.milianke.com
- *TechWeb:https://www.uisrc.com
- *tmall-shop:https://milianke.tmall.com
- *jd-shop:https://milianke.jd.com
- *taobao-shop: https://milianke.taobao.com
- *Create Date: 2022/09/25
- *Module Name:
- *File Name:
- *Description:
- *The reference demo provided by Milianke is only used for learning.
- *We cannot ensure that the demo itself is free of bugs, so users
- *should be responsible for the technical problems and consequences
- *caused by the use of their own products.
- *Copyright: Copyright (c) MiLianKe
- *All rights reserved.
- *Revision: 3.1
- *Signal description
- *1) I_ input
- *2) O_ output
- *3) IO_ input output
- *4) S_ system internal signal
- *5) _n activ low
- *6) _dg debug signal
- *7) _r delay or register
- *8) _s state mechine
- *********************************************************************/
- module fs_cap#(
- parameter integer VIDEO_ENABLE = 1
- )
- (
- input I_clk,
- input I_rstn,
- input I_vs,
- output reg O_fs_cap
- );
-
- //--------------------
- reg[4:0]CNT_FS = 6'b0;
- reg[4:0]CNT_FS_n = 6'b0;
- reg FS = 1'b0;
- (* ASYNC_REG = "TRUE" *) reg vs_i_r1;
- (* ASYNC_REG = "TRUE" *) reg vs_i_r2;
- (* ASYNC_REG = "TRUE" *) reg vs_i_r3;
- (* ASYNC_REG = "TRUE" *) reg vs_i_r4;
- always@(posedge I_clk) begin
- vs_i_r1 <= I_vs;
- vs_i_r2 <= vs_i_r1;
- vs_i_r3 <= vs_i_r2;
- vs_i_r4 <= vs_i_r3;
- end
- always@(posedge I_clk) begin
- if(!I_rstn)begin
- O_fs_cap <= 1'd0;
- end
- else if(VIDEO_ENABLE == 1)begin
- if({vs_i_r4,vs_i_r3} == 2'b01)begin
- O_fs_cap <= 1'b1;
- end
- else begin
- O_fs_cap <= 1'b0;
- end
- end
- else begin
- O_fs_cap <= vs_i_r4;
- end
- end
-
- endmodule
复制代码
|