[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-26浅谈XILINX FIFO的基本使用

news2024/9/23 0:23:47

软件版本: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概述

2配置FIFO IP

3半空/半满法控制读写FIFO

3.1测试代码程序

3.2 RTL仿真

3.2.1仿真激励文件

3.2.2仿真结果

4关键信号法

4.1 almost_full 和almost_empty

4.1.1测试代码

4.1.2 RTL仿真结果

4.2 almost_full和valid

4.2.1测试代码

4.2.2 RTL仿真结果


1概述

首先来大概了解下什么是FIFO ,FIFO( First Input First Output)简单说就是指先进先出。FIFO也是缓存机制的一种,下面是我总结的FIFO的三大用途:

1)、提高传输效率,增加DDR带宽的利用率。比如我们有4路视频数据缓存到DDR中去,比较笨的方法是,每个通道视频数据对应一颗DDR。现在对于DDR来说非常浪费,因为现在的DDR3可以跑1600Mbps DDR4可以跑到2400Mbps,如果你还是把一路视频数据对应一颗DDR显然严重浪费了带宽。加入FIFO后,只要把4路数据先缓存进入DDR,在缓存的过程中,快速得把数据从FIFO取出并且写入到DDR中,只要FIFO没有满就不会出现数据丢失。现在我们带宽够用,FIFO给的足够大就可以确保数据不丢失。

2)、数据位宽转换,比如我们有32bit的数据需要转换成128bit或者32bit的数据需要转换成8bit,那么用FIFO来转换也是非常方便的。

3)、跨时钟域的应用,比如数据是2个不同步的时钟,那么我们就可以用FIFO实现跨时钟域的传输。

以上总计的三点,很多时候是混合使用的。FIFO的用途非常大,我们在后面的例子中也看到,只要涉及到DDR传输的都和FIFO有关系。

我们这里的例子通过仿真告诉大家FIFO的基本用法,有两条我总结的办法,包括:

1)半空半满法

2)关键信号法

2配置FIFO IP

点击软件左侧的IP Catalog

输入关键词fifo,会出来非常多的FIFO类型

1)、AXI4-Stream FIFO内核旨在提供对与其他IP连接的AXI4-Stream接口(例如AXI以太网内核)的内存映射访问。 必须通过Vivado Design Suite构建系统,以连接AXI4-Stream FIFO内核,AXI以太网内核,处理器,内存,互连总线,时钟和其他嵌入式组件。

2)、AXI4-Stream Data FIFO 支持AXI4-Stream协议,具备packet包传输模式。

3)、AXI Data FIFO 就是数据FIFO 功能较为单一,接口为Stream接口

4)、FIFO Generator 支持Native 模式,AXI Memory Mapped模式 AXI Steam模式功能比较齐全,在没有AXI4或者AXI Stream协议的场合下,我们更多使用Native模式,这里的课程也以Native模式讲解。

使用Block RAM,BlockRAM是FPGA内部 集成的重要内存单元,速度高资源有限,所以得充分合理利用。时钟模式采用异步方式,也就是这里选择Independent Clocks,可以把IP名字改为FIFO32_2_128

选择First Word Fall Through 这样写入的数据,会先在读端口准备好,否则如果选择Standard FIFO需要读使能后一个时钟输出才有效。

观察almost full 和almost empty flag 这两个信号是可编程的,一些应用场景也是可以用到。

设置读计数器和写计数器,这不是必须的,我们第一个半空半满方法需要用到。

3半空/半满法控制读写FIFO

半空/半满法,功法要点:半空是针对读FIFO计数器而言,半满是针对写FIFO计数器而言;这里强调一点,FIFO的计数器并不精准,计数器会有几个时钟的延迟,所以这里的半空,半满的计数器大部分时候都是不精确的,除非你把程序停下来等几个时钟周期,显然这样不科学,会降低程序的效率。虽然不精确但是完全够用。因为读写FIFO一直在一个动态平衡中。

3.1测试代码程序

比如,我们这里的FIFO输入32bit 深度1024;输出128bit 深度256,这里的半空值就是128,半满值就是512。下面设计我们的状态机:

1)、状态0:当写入FIFO计数器小于512 则进入状态1

2)、状态1:当连续写入FIFO 512个数据后,再次进入状态0等待

读状态机的设计,每次读出128bt数据:

1)、状态0:当读FIFO计数器大于128 则进入状态1

2)、状态1:连续读出FIFO 128个数据后,再次进入状态0等待

fifo_test.v

/*************FIFO IP的仿真测试***************************************
--版本号1.0
--FIFO通常用于异步数据传输、数据缓存、数据位宽转换,本使用基于XILINX FIFO IP实现数据的位宽转换实验
--通过FIFO实现数据缓存,以及数据位宽从32bits转为128btis
--写状态机和读状态机分开运行
*********************************************************************/
`timescale 1ns / 1ns //仿真时间刻度/精度

module fifo_test1
(
input I_sysclk_p,
input I_sysclk_n, //系统时钟输入
input I_rstn     //系统复位
);

wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);
wire clk_100m,clk_200m,clk_locked;//MMCM/PLL 时钟信号
wire [127:0]rd_data; //读数据信号
wire fifo_rst;       //fifo 复位,高电平有效
wire full;           //FIFO满,这里没用到
wire empty;          //FIFO空,这里没用到
wire almost_full;    //FIFO将满,代表FIFO再写入1个数据就会满,这里没用到
wire almost_empty;   //FIFO将空,代表FIFO再读出1个数据就会空,这里没用到
wire [7 : 0] rd_data_count;//读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
wire [9 : 0] wr_data_count;//写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数

//写状态机信号
reg WR_REQ = 1'b0;   //写请求信号
reg [0 :0]WR_S;      //写状态机
reg [10:0]wr_cnt;    //写数据计数器
reg wr_en;           //写使能寄存器

// 读状态机
reg RD_REQ = 1'b0;  //读请求信号
reg [0:0]RD_S;      //读状态机
reg [7:0]rd_cnt;    //读数据计数器
reg rd_en;          //读使能寄存器

reg[9:0] rst_cnt = 10'd0; //复位计数器

assign fifo_rst = (rst_cnt[9:7] == 3'b010); //产生一个高脉冲复位
//MMCM/PLL 产生200M和100M时钟
clk_wiz_0 clk_inst(.clk_out1(clk_200m),.clk_out2(clk_100m),.resetn(I_rstn),.locked(clk_locked),.clk_in1(I_clk));  

//复位计数器模块
always @(posedge clk_100m)begin
    if(!clk_locked)
        rst_cnt <= 10'd0;
    else if(rst_cnt[9] == 1'b0)
        rst_cnt <= rst_cnt + 1'b1;
end

// FIFO写状态机
always @(posedge clk_200m)begin //写数据用200MHZ 时钟写
    if(!rst_cnt[9])begin //复位,重置相关寄存器
        WR_S   <= 1'b0;
        wr_cnt <= 11'd0;
        wr_en  <= 1'b0;
    end
    else begin
        case(WR_S) //状态机
        0:begin
            wr_cnt <= 11'd0;
            if(WR_REQ) //当WR_REQ信号有效,代表了FIFO已经可以写入数据
                WR_S <= 1'b1;//进入下一状态
        end
        1:begin
            if(wr_cnt < 512)begin //如果写入的数据小于512
                wr_en  <= 1'b1; //设置写使能
                wr_cnt <= wr_cnt+1'b1;//写计数器累加
            end
            else begin //否则,重置使能,并且回到状态0
                wr_en <= 1'b0;
                WR_S  <= 1'b0;
            end
        end
        endcase
    end
end

always @(posedge clk_100m)begin//读使用100M时钟
    if(!rst_cnt[9])begin //复位,重置相关寄存器
        RD_S <= 1'b0;
        rd_cnt <= 8'd0;
        rd_en <= 1'b0;
    end
    else begin
        case(RD_S)//读状态机
        0:begin
            rd_cnt <= 8'd0;
            if(RD_REQ) //RD_REQ代表FIFO中有足够的数据
                RD_S <= 1'b1;//下一状态
        end
        1:begin
            if(rd_cnt < 128)begin //判断FIFO中读部分的数据,已经读的数量是否小于128个128bits
                rd_en  <= 1'b1;   //使能读信号
                rd_cnt <= rd_cnt+1'b1;//每读一个数据,累加1
            end
            else begin //否则重置读使能,状态机回到0
                rd_en <= 1'b0;
                RD_S <= 1'b0;
            end
        end
        endcase
    end
end

//判断写FIFO中是否有足够的空间存放下一次写的数据
always @(posedge clk_200m)begin
    WR_REQ <= (wr_data_count < 10'd511);
end

//判断读FIFO中是否有足够的数据可以被读出
always @(posedge clk_100m)begin
    RD_REQ <= (rd_data_count > 8'd127);
end

FIFO32_2_128 FIFO32_2_128_inst0 (
  .rst(fifo_rst),                  //FIFO 复位,高电平有效
  .wr_clk(clk_200m),               //FIFO 写时钟输入
  .rd_clk(clk_100m),               //FIFO 读时钟输入
  .din({24'd0,wr_cnt[7:0]}),       //FIFO 写数据输入,测试数据用wr_cnt[7:0]计数器作为输入,其他高位为0
  .wr_en(wr_en),                   //FIFO 写数据使能
  .rd_en(rd_en),                   //FIFO 读数据使能
  .dout(rd_data),                  //FIFO 读数据输出
  .full(full),                     //FIFO 写通道满,该信号这里没使用
  .almost_full(almost_full),       //FIFO 写通道将满,该信号这里没使用
  .empty(empty),                   //FIFO 读通道空,该信号这里没使用
  .almost_empty(almost_empty),     //FIFO 读通道将空,该信号这里没使用
  .rd_data_count(rd_data_count),   //FIFO 读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
  .wr_data_count(wr_data_count)    //FIFO 写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
);
endmodule

以上代码中关键的控制状态机写FIFO读FIFO的代码如下:

/*************FIFO IP的仿真测试***************************************
--版本号1.0
--FIFO通常用于异步数据传输、数据缓存、数据位宽转换,本使用基于XILINX FIFO IP实现数据的位宽转换实验
--通过FIFO实现数据缓存,以及数据位宽从32bits转为128btis
--写状态机和读状态机分开运行
*********************************************************************/
`timescale 1ns / 1ns //仿真时间刻度/精度

module fifo_test1
(
input I_sysclk_p,
input I_sysclk_n, //系统时钟输入
input I_rstn     //系统复位
);

wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);
wire clk_100m,clk_200m,clk_locked;//MMCM/PLL 时钟信号
wire [127:0]rd_data; //读数据信号
wire fifo_rst;       //fifo 复位,高电平有效
wire full;           //FIFO满,这里没用到
wire empty;          //FIFO空,这里没用到
wire almost_full;    //FIFO将满,代表FIFO再写入1个数据就会满,这里没用到
wire almost_empty;   //FIFO将空,代表FIFO再读出1个数据就会空,这里没用到
wire [7 : 0] rd_data_count;//读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
wire [9 : 0] wr_data_count;//写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数

//写状态机信号
reg WR_REQ = 1'b0;   //写请求信号
reg [0 :0]WR_S;      //写状态机
reg [10:0]wr_cnt;    //写数据计数器
reg wr_en;           //写使能寄存器

// 读状态机
reg RD_REQ = 1'b0;  //读请求信号
reg [0:0]RD_S;      //读状态机
reg [7:0]rd_cnt;    //读数据计数器
reg rd_en;          //读使能寄存器

reg[9:0] rst_cnt = 10'd0; //复位计数器

assign fifo_rst = (rst_cnt[9:7] == 3'b010); //产生一个高脉冲复位
//MMCM/PLL 产生200M和100M时钟
clk_wiz_0 clk_inst(.clk_out1(clk_200m),.clk_out2(clk_100m),.resetn(I_rstn),.locked(clk_locked),.clk_in1(I_clk));  

//复位计数器模块
always @(posedge clk_100m)begin
    if(!clk_locked)
        rst_cnt <= 10'd0;
    else if(rst_cnt[9] == 1'b0)
        rst_cnt <= rst_cnt + 1'b1;
end

// FIFO写状态机
always @(posedge clk_200m)begin //写数据用200MHZ 时钟写
    if(!rst_cnt[9])begin //复位,重置相关寄存器
        WR_S   <= 1'b0;
        wr_cnt <= 11'd0;
        wr_en  <= 1'b0;
    end
    else begin
        case(WR_S) //状态机
        0:begin
            wr_cnt <= 11'd0;
            if(WR_REQ) //当WR_REQ信号有效,代表了FIFO已经可以写入数据
                WR_S <= 1'b1;//进入下一状态
        end
        1:begin
            if(wr_cnt < 512)begin //如果写入的数据小于512
                wr_en  <= 1'b1; //设置写使能
                wr_cnt <= wr_cnt+1'b1;//写计数器累加
            end
            else begin //否则,重置使能,并且回到状态0
                wr_en <= 1'b0;
                WR_S  <= 1'b0;
            end
        end
        endcase
    end
end

always @(posedge clk_100m)begin//读使用100M时钟
    if(!rst_cnt[9])begin //复位,重置相关寄存器
        RD_S <= 1'b0;
        rd_cnt <= 8'd0;
        rd_en <= 1'b0;
    end
    else begin
        case(RD_S)//读状态机
        0:begin
            rd_cnt <= 8'd0;
            if(RD_REQ) //RD_REQ代表FIFO中有足够的数据
                RD_S <= 1'b1;//下一状态
        end
        1:begin
            if(rd_cnt < 128)begin //判断FIFO中读部分的数据,已经读的数量是否小于128个128bits
                rd_en  <= 1'b1;   //使能读信号
                rd_cnt <= rd_cnt+1'b1;//每读一个数据,累加1
            end
            else begin //否则重置读使能,状态机回到0
                rd_en <= 1'b0;
                RD_S <= 1'b0;
            end
        end
        endcase
    end
end

//判断写FIFO中是否有足够的空间存放下一次写的数据
always @(posedge clk_200m)begin
    WR_REQ <= (wr_data_count < 10'd511);
end

//判断读FIFO中是否有足够的数据可以被读出
always @(posedge clk_100m)begin
    RD_REQ <= (rd_data_count > 8'd127);
end

FIFO32_2_128 FIFO32_2_128_inst0 (
  .rst(fifo_rst),                  //FIFO 复位,高电平有效
  .wr_clk(clk_200m),               //FIFO 写时钟输入
  .rd_clk(clk_100m),               //FIFO 读时钟输入
  .din({24'd0,wr_cnt[7:0]}),       //FIFO 写数据输入,测试数据用wr_cnt[7:0]计数器作为输入,其他高位为0
  .wr_en(wr_en),                   //FIFO 写数据使能
  .rd_en(rd_en),                   //FIFO 读数据使能
  .dout(rd_data),                  //FIFO 读数据输出
  .full(full),                     //FIFO 写通道满,该信号这里没使用
  .almost_full(almost_full),       //FIFO 写通道将满,该信号这里没使用
  .empty(empty),                   //FIFO 读通道空,该信号这里没使用
  .almost_empty(almost_empty),     //FIFO 读通道将空,该信号这里没使用
  .rd_data_count(rd_data_count),   //FIFO 读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
  .wr_data_count(wr_data_count)    //FIFO 写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
);
endmodule

对于初学者一定要注意,FIFO复位需要经过多个时钟周期后才能完成复位,所以我们这里把FIFO复位设置了常量0,让FIFO在有时钟后就能自己完成复位,FIFO 复位的信号可以根据实际情况去应用,比如我们在后面的图像缓存方面,我们会用图像的VS去复位和同步FIFO,具体的理解还要在实际应用中加深。

3.2 RTL仿真

3.2.1仿真激励文件

下面我们进行仿真,如何编写tb文件,和调用仿真波形图,我就不详细讲了,不懂的人看前面FPGA入门的几个例子。我下面直接给出仿真代码。

仿真TB文件

/*********************仿真文件****************************************

`timescale 1ns / 1ns//仿真时间刻度/精度

module tb_fifo_test;

localparam SYS_TIME = 10;//系统时钟周期10ns

reg I_sysclk_p;
reg I_sysclk_n;//系统时钟
reg I_rstn;//系统复位

//例化fifo_test1
fifo_test1 fifo_test1_inst
(
.I_sysclk_p(I_sysclk_p),
.I_sysclk_n(I_sysclk_n),
.I_rstn(I_rstn)
);

//例化fifo_test2
fifo_test2 fifo_test2_inst
(
.I_sysclk_p(I_sysclk_p),
.I_sysclk_n(I_sysclk_n),
.I_rstn(I_rstn)
);

//例化fifo_test2
fifo_test2 fifo_test2_inst
(
.I_sysclk_p(I_sysclk_p),
.I_sysclk_n(I_sysclk_n),
.I_rstn(I_rstn)
);

//初始化
initial begin
    I_sysclk_p  = 1'b0;
    I_sysclk_n  = 1'b1;
    I_rstn = 1'b0;
    #100;//产生100ns的系统复位
    I_rstn = 1'b1;//复位完成
    #20000 $finish; 
end

//产生仿真时钟
always #(SYS_TIME/2) I_sysclk_p= ~I_sysclk_p;
always #(SYS_TIME/2) I_sysclk_n= ~I_sysclk_n;         
                  
endmodule
3.2.2仿真结果

写入FIFO的计数器值第一个值是1,注意不是0

每次写数据计数器最后一个值是512

读出FIFO的计数器值1

最后一个读出的值,由于采取8bit写入FIFO,所以512对应的是0

4关键信号法

关键信号法就是利用关键的信号,比如FIFO满标志,FIFO将满标志,FIFO空标志,FIFO将空标志,FIFO可编程空标志,和FIFO可编程满标志,FIFO读Valid标志,去控制FIFO的读写。

事实上,FIFO的满标志是正确的,也就是说FIFO输出满标志的。

修改FIFO IP 增加valid信号观测    

4.1 almost_full 和almost_empty

1)、当FIFO非满的时候写

2)、当FIFO非空的时候读

4.1.1测试代码
/*************FIFO IP的仿真测试***************************************
--版本号1.0
--FIFO通常用于异步数据传输、数据缓存、数据位宽转换,本使用基于XILINX FIFO IP实现数据的位宽转换实验
--通过FIFO实现数据缓存,以及数据位宽从32bits转为128btis
--写状态机和读状态机分开运行
*********************************************************************/

`timescale 1ns / 1ns //仿真时间刻度/精度

module fifo_test2
(
input I_sysclk_p,
input I_sysclk_n, //系统时钟输入
input I_rstn     //系统复位
);
wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);
wire clk_100m,clk_200m,clk_locked;//MMCM/PLL 时钟信号
wire [127:0]rd_data; //读数据信号
wire fifo_rst;       //fifo 复位,高电平有效
wire full;           //FIFO满
wire empty;          //FIFO空,这里没用到
wire almost_full;    //FIFO将满,代表FIFO再写入1个数据就会满
wire almost_empty;   //FIFO将空,代表FIFO再读出1个数据就会空
wire [7 : 0] rd_data_count;//读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
wire [9 : 0] wr_data_count;//写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
reg  [10: 0] wr_cnt;    //写数据计数器

reg[9:0] rst_cnt = 10'd0; //复位计数器

assign fifo_rst = (rst_cnt[9:7] == 3'b010); //产生一个高脉冲复位

//MMCM/PLL 产生200M和100M时钟
clk_wiz_0 clk_inst(.clk_out1(clk_200m),.clk_out2(clk_100m),.resetn(I_rstn),.locked(clk_locked),.clk_in1(I_clk));  

//复位计数器模块
always @(posedge clk_100m)begin
    if(!clk_locked)
        rst_cnt <= 10'd0;
    else if(rst_cnt[9] == 1'b0)
        rst_cnt <= rst_cnt + 1'b1;
end

// FIFO写状态机
always @(posedge clk_200m)begin //写数据用200MHZ 时钟写
    if(!rst_cnt[9]) //复位,重置相关寄存器
        wr_cnt <= 11'd0;
    else 
       wr_cnt <= full ? wr_cnt : wr_cnt+1'b1;//写计数器累加
end

FIFO32_2_128 FIFO32_2_128_inst0 (
  .rst(fifo_rst),                  //FIFO 复位,高电平有效
  .wr_clk(clk_200m),               //FIFO 写时钟输入
  .rd_clk(clk_100m),               //FIFO 读时钟输入
  .din({24'd0,wr_cnt[7:0]}),       //FIFO 写数据输入,测试数据用wr_cnt[7:0]计数器作为输入,其他高位为0
  .wr_en(!almost_full&rst_cnt[9]),                   //FIFO 写数据使能
  .rd_en(!almost_empty&rst_cnt[9]),                   //FIFO 读数据使能
  .dout(rd_data),                  //FIFO 读数据输出
  .full(full),                     //FIFO 写通道满
  .almost_full(almost_full),       //FIFO 写通道将满
  .empty(empty),                   //FIFO 读通道空,该信号这里没使用
  .almost_empty(almost_empty),     //FIFO 读通道将空
  .rd_data_count(rd_data_count),   //FIFO 读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
  .wr_data_count(wr_data_count)    //FIFO 写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
);

endmodule

使用almost_full和almost_empyt可以防止FIFO写满和FIFO空读

4.1.2 RTL仿真结果

4.2 almost_full和valid

当读FIFO里面数据有效的时候Valid为1,所以也可以用valid信号读数据

4.2.1测试代码
/*************FIFO IP的仿真测试***************************************
--版本号1.0
--FIFO通常用于异步数据传输、数据缓存、数据位宽转换,本使用基于XILINX FIFO IP实现数据的位宽转换实验
--通过FIFO实现数据缓存,以及数据位宽从32bits转为128btis
--写状态机和读状态机分开运行
*********************************************************************/

`timescale 1ns / 1ns //仿真时间刻度/精度

module fifo_test3
(
input I_sysclk_p,
input I_sysclk_n, //系统时钟输入
input I_rstn     //系统复位
);
wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);
wire clk_100m,clk_200m,clk_locked;//MMCM/PLL 时钟信号
wire [127:0]rd_data; //读数据信号
wire fifo_rst;       //fifo 复位,高电平有效
wire full;           //FIFO满,这里没用到
wire empty;          //FIFO空,这里没用到
wire almost_full;    //FIFO将满,代表FIFO再写入1个数据就会满
wire almost_empty;   //FIFO将空,代表FIFO再读出1个数据就会空,这里没用到
wire [7 : 0] rd_data_count;//读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
wire [9 : 0] wr_data_count;//写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
reg [10 : 0] wr_cnt;    //写数据计数器
wire         valid ;    //读通道数据有效

reg[9:0] rst_cnt = 10'd0; //复位计数器

assign fifo_rst = (rst_cnt[9:7] == 3'b010); //产生一个高脉冲复位

//MMCM/PLL 产生200M和100M时钟
clk_wiz_0 clk_inst(.clk_out1(clk_200m),.clk_out2(clk_100m),.resetn(I_rstn),.locked(clk_locked),.clk_in1(I_clk));  

//复位计数器模块
always @(posedge clk_100m)begin
    if(!clk_locked)
        rst_cnt <= 10'd0;
    else if(rst_cnt[9] == 1'b0)
        rst_cnt <= rst_cnt + 1'b1;
end

// FIFO写状态机
always @(posedge clk_200m)begin //写数据用200MHZ 时钟写
    if(!rst_cnt[9]) //复位,重置相关寄存器
        wr_cnt <= 11'd0;
    else 
       wr_cnt <= almost_full? wr_cnt : wr_cnt+1'b1;//写计数器累加
end

FIFO32_2_128 FIFO32_2_128_inst0 (
  .rst(fifo_rst),                  //FIFO 复位,高电平有效
  .wr_clk(clk_200m),               //FIFO 写时钟输入
  .rd_clk(clk_100m),               //FIFO 读时钟输入
  .din({24'd0,wr_cnt[7:0]}),       //FIFO 写数据输入,测试数据用wr_cnt[7:0]计数器作为输入,其他高位为0
  .wr_en(!almost_full&rst_cnt[9]),                   //FIFO 写数据使能
  .rd_en(valid),                   //FIFO 读数据使能
  .dout(rd_data),                  //FIFO 读数据输出
  .full(full),                     //FIFO 写通道满,该信号这里没使用
  .almost_full(almost_full),       //FIFO 写通道将满,该信号这里没使用
  .empty(empty),                   //FIFO 读通道空,该信号这里没使用
  .valid(valid),                  // FIFO 读通道,数据有效
  .almost_empty(almost_empty),     //FIFO 读通道将空,该信号这里没使用
  .rd_data_count(rd_data_count),   //FIFO 读FIFO的计数器,这个计数器不精准,只是非常接近读FIFO中具有的数据个数
  .wr_data_count(wr_data_count)    //FIFO 写FIFO的计数器,这个计数器不精准,只是非常接近写FIFO中写入的数据个数
);

endmodule
4.2.2 RTL仿真结果

对于代码简单修改,仿真可以线点击1的位置重新load代码,然后再点击2运行。运行一会可以手动停止。

相信阅读完本文,根据本文把FIFO的例子做一遍你就能基本掌握FIFO的使用了,我是比较推荐第一种的半空半满法。

对于Stream接口的FIFO使用起来也差不多,因为本质上他们都是FIFO。在后期的课程中你们会看到Stream FIFO的应用,使用Steam接口有利于在新的FPGA设计中统一接口,方便代码的标准化,我们这里暂时就不讨论了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2100074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

9、Django Admin优化查询

如果你的Admin后台中有很多计算字段&#xff0c;那么你需要对每个对象运行多个查询&#xff0c;这会使你的Admin后台变得非常慢。要解决此问题&#xff0c;你可以重写管理模型中的get_queryset方法使用annotate聚合函数来计算相关的字段。 以下示例为Origin模型的中ModelAdmin…

Spring6梳理5——基于XML管理Bean环境搭建

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 目录 ①搭建模块 ②引入配置文件 ③创建BeanXML文件 ④创建Java类文件&#xff08;User…

在K8s上运行GitHub Actions的自托管运行器

1&#xff1a;添加Actions Runner Controller的Helm仓库 helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller helm repo update2&#xff1a;创建GitHub Personal Access Token (PAT) 登录到你的GitHub账户。访…

SQL语句(数据更新、查询操作)

数据库表操作 创建数据库语法格式 create table 表名(字段名1 类型 约束&#xff0c;字段名2 类型 约束&#xff0c;..... ..... )创建学生表&#xff0c;字段要求如下&#xff1a; 姓名&#xff08;长度为10&#xff09;、年龄、身高&#xff08;保留2位小数&#xff09; cre…

安卓shiply热更新入门

目录 一。我的开发环境 二。集成shiply热更新sdk 三。编写代码 1。创建一个CustomRFixLog类 2。创建一个MyApplication类 3。配置AndroidManifest.xml 4。创建一个新的Activity继承AbsRFixDevActivity 用于测试 四。登录shiply后台配置 1。创建项目 五。制作补丁 1。在app…

Ae关键帧动画基础练习-街道汽车超车

目录 1.让背景向左移动 2.让小红车匀速移动 3.实现小黄车的超车 完成街道汽车超车的一个简单动画&#xff0c;背景向左移动看起来就如同画面向右移动了一般&#xff0c;根据这个原理&#xff0c;可以完成这个动画。 导入素材时&#xff0c;要选择不同的图层&#xff0c;这样…

微软AD替代方案统一管理Windows和信创电脑的登录认证与网络准入认证

自国资委79号文明确了2027年底前信息系统全面国产化的目标后&#xff0c;金融单位、央国企集团及各子公司纷纷加大国产化改造力度。不少子、孙公司表示&#xff0c;集团要求到2024年底或2025年底国外的关键IT基础设施要停止使用&#xff0c;如微软AD、云桌面等。 信创国产化是大…

Mybatis链路分析:JDK动态代理和责任链模式的应用

背景 此前写过关于代理模式的文章&#xff0c;参考&#xff1a;代理模式 动态代理功能&#xff1a;生成一个Proxy代理类&#xff0c;Proxy代理类实现了业务接口&#xff0c;而通过调用Proxy代理类实现的业务接口&#xff0c;实际上会触发代理类的invoke增强处理方法。 责任链功…

艾体宝洞察丨透过语义缓存,实现更快、更智能的LLM应用程序

传统的缓存只存储数据而不考虑上下文&#xff0c;语义缓存则不同&#xff0c;它能理解用户查询背后的含义。它使数据访问更快&#xff0c;系统响应更智能&#xff0c;对 GenAI 应用程序至关重要。 什么是语义缓存&#xff1f; 语义缓存解释并存储用户查询的语义&#xff0c;使…

功率谱密度估计(Power Spectral Density Estimation, PSD)介绍,轴承磨损检测

介绍 功率谱密度估计&#xff08;Power Spectral Density Estimation, PSD&#xff09;是信号处理中的一项重要技术&#xff0c;用于描述信号在频率域中的能量分布。PSD提供了信号的功率随频率变化的情况&#xff0c;是分析随机信号和确定信号频率特性的常用工具。 功率谱密度…

美团代付支持多模板全开源多种支付通道 多模版三合一源码附教程

美团代付 支持多模板全开源多种支付通道 多模版三合一源码附教程 美团代付源码&#xff0c;支持多模板&#xff0c;全开源&#xff0c;多种支付通道&#xff0c;其它的就没什么好介绍的了&#xff0c;有兴趣的自行去体验吧。

驱动(RK3588S)第五课时:字符设备驱动编程

目录 一、操作系统的框架二、设备的类型三、什么是设备四、杂项字符设备的 API五、代码实现1、底层实现&#xff08;内核&#xff09;2、应用层代码3、交叉编译环境4、结果展示 一、操作系统的框架 二、设备的类型 硬件设备其实是分类型的&#xff1a; 字符设备&#xff1a;所…

TCP/IP 报文传输过程

目录 1. 概念理解2. 传输过程 原文回到 TCP/IP 强烈推荐下面博客&#xff0c;详细阐述了TCP/IP协议概念和传输过程 TCP协议详解 (史上最全) 1. 概念理解 2. 传输过程 以一个具体例子为例&#xff0c;如下图所示&#xff0c;由A 给 F 发送一个数据包整个过程是怎样的

Windows 安装 MySQL8

目录 前言 下载 安装 配置 连接 前言 一般数据库都是部署在 Linux 服务器上&#xff0c;在 Windows 上开发&#xff0c;通过数据库连接工具来连接数据库。在工作中&#xff0c;如果条件允许&#xff0c;会有单独的开发库给开发人员使用&#xff0c;否则开发人员就只能连接…

U盘损坏深度解析与高效数据恢复指南

一、U盘损坏现象初探 在数字化时代&#xff0c;U盘作为便捷的数据存储与传输工具&#xff0c;几乎成为了我们日常生活与工作中的必需品。然而&#xff0c;不少用户都曾遭遇过U盘损坏的困境&#xff0c;面对无法读取、文件丢失或系统提示错误等问题&#xff0c;往往感到束手无策…

【概率与统计 动态规划】 808. 分汤

本文涉及知识点 C动态规划 数学 概率与统计 LeetCode 808. 分汤 有 A 和 B 两种类型 的汤。一开始每种类型的汤有 n 毫升。有四种分配操作&#xff1a; 提供 100ml 的 汤A 和 0ml 的 汤B 。 提供 75ml 的 汤A 和 25ml 的 汤B 。 提供 50ml 的 汤A 和 50ml 的 汤B 。 提供 25…

Chapter 10 async函数 await关键字

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 前言一、async 函数二、await 关键字 前言 在现代 JavaScript 开发中&#xff0c;异步编程是一个重要的概念。随着 ES2017 的引入&#xff0c;async 函数和 await 关键字为处…

一文读懂Mysql连接数的基本知识

目录 前言1. 基本知识2. 常用命令 前言 原先写过一篇SQL Server的知识点&#xff0c;推荐阅读&#xff1a;Sql Server缓冲池、连接池等基本知识&#xff08;附Demo&#xff09; 其余知识点推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&…

Windows10上Nginx如何通过自签名证书方式发布Https服务(上)

背景 在Android开发中使用平板设备进行调试,Android版本是13。在调试中遇到一个这样的报错信息:“java.io.IOException: Cleartext HTTP traffic to 192.168.137.1 not permitted”,然后查了下报错原因是:应用正在尝试通过不安全的HTTP协议进行网络通信,而你的应用运行的环…

计时计费收银软件如何控制设备 佳易王电玩城PS5机计时定时提醒管理系统操作教程

一、前言 计时计费收银软件如何控制设备 佳易王电玩城PS5机计时定时提醒管理系统操作教程 【特别说明&#xff1a;下载的文件为压缩包文件&#xff0c;右键 解压到当前文件夹&#xff0c;将解压后 的文件夹复制到电脑的D盘或E盘&#xff0c;打开该文件夹&#xff0c;将程序图…