Verilog同步FIFO设计

news2025/3/13 11:48:27

同步FIFO(synchronous)的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。

异步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous)

在这里插入图片描述

典型同步FIFO有三部分组成:

(1) FIFO写控制逻辑;

(2)FIFO读控制逻辑;

(3)FIFO 存储实体(如Memory、Reg)。

FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写 满、写错等状态信号;

FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读 空、读错等状态信号。

  • 基本概念

    FIFO:先进先出(First-in-first-out) FIFO的深度 同一块数据内存的大小

    FIFO的宽度:写指针:Write-pointer 读指针:Read-pointer

    一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。 初始化时,读写指针指向同一数据地址。下图可见,FIFO初始化时,WP和RP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元
    在这里插入图片描述

    2种方法判断空满:

    1. counter计数器:判断有效数据是否等于FIFO的深度,为0就表示空

      使用fifo_counter记录FIFO RAM中的数据个数,等于0时,给出empty信号,等于BUF_LENGTH时,给出full信号。

      写而未满时增加1 读而未空时减1 同时发生读写操作时,fifo_counter不变

    2. pointer:如果深度为8,那么3bit就可以表示8个数,但是为了判断空满,会多定义一位,也即4bit,WP为1000,RP为0000,我们使用最高位去判断是否在同一单元,用高位判断空满,如果高位相异,就表示满,如果相同表示空。

  • 程序代码

    `define BUF_WIDTH 4 // 地址宽度为3+1,
    `define BUF_SIZE 8 // 数据个数,FIFO深度
    module fifo_counter( clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
        input clk,rst_n; // 时钟与复位信号
        input wr_en,rd_en; // 读写使能信号
        input [7:0] buf_in; // 写数据
        output reg [7:0] buf_out; // 读数据
        output wire buf_empty,buf_full; // 空满两个状态信号
        output reg [`BUF_WIDTH-1:0] fifo_cnt;  //判断空满计数器
    
        // 读写指针:数据指针3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7
        reg [`BUF_WIDTH-2:0] rd_ptr,wr_ptr;
        // 读写容器
        reg [7:0] buf_mem[0:`BUF_SIZE-1];
    
        //判断空满 方式1
        assign buf_empty = (fifo_cnt == 0); //buf_empty若是reg类型则错,不能使用assign持续赋值
        assign buf_full = (fifo_cnt == `BUF_SIZE);// fifo_cnt = 8就是满的
    
        //判断空满 方式2
        assign buf_empty = (rd_ptr[3] == wr_ptr[3])&&(rd_ptr[2:0] == wr_ptr[2:0]); 
        assign buf_full = (rd_ptr[3] != wr_ptr[3])&&(rd_ptr[2:0] == wr_ptr[2:0]); // 前后必须同时为1
    
        //读数据
        always @(posedge clk or negedge rst_n) 
            begin 
                if(!rst_n)
                    buf_out <= 0;
                if(rd_en && !buf_empty)
                    buf_out <= buf_mem[rd_ptr];
            end
    
        // 写数据
        always @(posedge clk) 
            begin
                if(wr_en && !buf_full)
                    buf_mem[wr_ptr] <= buf_in;
            end
    
        // 更改读写指针
        always @(posedge clk or negedge rst_n)
            begin
                if(!rst_n)
                    begin
                        wr_ptr <= 0;
                        rd_ptr <= 0;
                    end
                else 
                    begin
                        // 满足写的条件,就把写指针+1
                        if(!buf_full && wr_en)
                            wr_ptr <= wr_ptr + 1;
                        // 满足读的条件,就把读指针+1
                        if(!buf_empty && rd_en)
                            rd_ptr <= rd_ptr + 1;
                    end
            end
    
        // 监控fifo_cnt
        always @(posedge clk or negedge rst_n)begin
            if(!rst_n)
                fifo_cnt <= 0;
            else if((!buf_full&&wr_en)&&(!buf_empty&&rd_en)) // 同时读写,数量不变
                fifo_cnt <= fifo_cnt;
            else if(!buf_full && wr_en) // 写数据:写而未满增加1
                fifo_cnt <= fifo_cnt + 1;
            else if(!buf_empty && rd_en) // 读数据:读而未空减1
                fifo_cnt <= fifo_cnt-1;
            else
                fifo_cnt <= fifo_cnt; // 维持
        end
    endmodule
    
  • TestBench

    `define BUF_WIDTH 4 //地址宽度为3+1,
    `define BUF_SIZE (8) //数据个数,FIFO深度
    module tb_fifo_counter;
        reg clk,rst_n;
        reg wr_en,rd_en;
        reg [7:0] buf_in; // data input to be pushed to buffer
        wire [7:0] buf_out; // port to output the data using pop. wire buf_empty,buf_full; // buffer empty and full indication
        wire [`BUF_WIDTH-1:0] fifo_cnt; // number of data pushed in to buffer
        fifo_counter dut(
            .clk(clk),
            .rst_n(rst_n),
            .buf_in(buf_in),
            .buf_out(buf_out),
            .wr_en(wr_en),
            .rd_en(rd_en),
            .buf_empty(buf_empty),
            .buf_full(buf_full),
            .fifo_cnt(fifo_cnt));
        fifo_counter dut(
            .clk		(clk),
            .rst_n	(rst_n),
            .buf_in	(buf_in),
            .buf_out	(buf_out),
            .wr_en	(wr_en),
            .rd_en	(rd_en),
            .buf_empty	(buf_empty),
            .buf_full	(buf_full),
            .fifo_cnt	(fifo_cnt));
        always #10 clk = ~clk;
        // 定义一个临时的数据,将读出来的数据暂存
        reg [7:0] tempdata;
        initial begin
            clk = 0;
            rst_n = 0;
            wr_en = 0;
            rd_en = 0;
            buf_in = 0;
            #15; rst_n = 1;
            push(1);
    
            // 同时读写
            fork
                push(2);
                pop(tempdata); // 读取tempdata = 1
            join
    
            push(10);
            push(20);
            push(30);
            push(40);
            push(50);
            push(60);
            push(70);
            // 70push 就会满
            push(80);
            push(90);
            push(100);
            push(110);
            push(120);
            push(130);
            pop(tempdata); // 读取tempdata = 2
            push(tempdata); 
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            push(140); // 可以写进去
            pop(tempdata);
            push(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            pop(tempdata);
            push(5);
            pop(tempdata);// 读取tempdata = 5
            #50 $finish;
        end
        // 将data写入fifo
        task push (input [7:0] data);
            if(buf_full)
                $display("---Cannot push %d: Buffer Full---",data);
            else begin
                $display("Push",,data);
                buf_in = data;
                wr_en = 1;
                @(posedge clk);
                #5 wr_en = 0;
            end
        endtask
        // 将data读取出来
        task pop(output[7:0] data);
            if(buf_empty)
                $display("---Cannot Pop: Buffer Empty---");
            else begin
                rd_en = 1;
                @(posedge clk);
                #3 rd_en = 0;
                data = buf_out;
                $display("------Poped:",,data);
            end
        endtask
    endmodule
    

    find -name "*.v" > file.list

    makefile文件:

    all:clean com sim
    SEED=1
    com:
    	vcs -full64 -R -sverilog -debug_all -f file.list -l comp.log +ntb_random_seed=$(SEED) \
    	-cm line+cond+fsm+branch+tgl -cm_name simv -cm_dir ./covdir.vdb
    sim:
    	./simv -l sim.log
    rung:
    	./simv -gui -l sim.log
    cov:
    	dve -full64 -covdir *.vdb &
    clean:
    	rm -rf ./csrc *.daidir *.log *.vpd *.vdb simv* *.key *race.out*
    	rm -rf AN.DB
    	rm -rf novas*
    	rm -rf DVEfiles
    	rm -rf urgReport
    
    VCS Coverage Metrics Release O-2018.09-1_Full64 Copyright (c) 1991-2018 by Synopsys Inc.
    Push   1
    Push   2
    ------Poped:   1
    Push  10
    Push  20
    Push  30
    Push  40
    Push  50
    Push  60
    Push  70
    ---Cannot push  80: Buffer Full---
    ---Cannot push  90: Buffer Full---
    ---Cannot push 100: Buffer Full---
    ---Cannot push 110: Buffer Full---
    ---Cannot push 120: Buffer Full---
    ---Cannot push 130: Buffer Full---
    ------Poped:   2
    Push   2
    ------Poped:  10
    ------Poped:  20
    ------Poped:  30
    ------Poped:  40
    Push 140
    ------Poped:  50
    Push  50
    ------Poped:  60
    ------Poped:  70
    ------Poped:   2
    ------Poped: 140
    ------Poped:  50
    ---Cannot Pop: Buffer Empty---
    ---Cannot Pop: Buffer Empty---
    ---Cannot Pop: Buffer Empty---
    ---Cannot Pop: Buffer Empty---
    ---Cannot Pop: Buffer Empty---
    ---Cannot Pop: Buffer Empty---
    Push   5
    ------Poped:   5
    

    查看波形:make rung

在这里插入图片描述

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

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

相关文章

vue项目的实用性总结

1、mockjs 基本使用 ★ 安装&#xff1a;npm i mockjs。 在src/mock/index.js内容如下&#xff1a; import Mock from mockjs //制订拦截规则 Mock.mock(http://www.0313.com,get,你好啊)记得在main.js中引入一下&#xff0c;让其参与整个项目的运行。 只要发出去的是get类型…

印度货代专线【我国到印度专线有哪些方式】

随着全球贸易的不断发展&#xff0c;我国与印度之间的贸易往来也日益频繁。作为两个人口最多的国家之一&#xff0c;中国和印度之间的货物运输需求不断增长。为了满足这一需求&#xff0c;印度货代专线应运而生&#xff0c;为进出口商提供高效、可靠的货物运输服务。本文将探索…

零零信安:暗网分析报告——Part 4 商业黑客组织,“流星街”的原住

暗网&#xff0c;作为互联网的一部分&#xff0c;充满了神秘而又复杂的活动。更重要的是&#xff0c;其背后的主要参与者——商业黑客和各种有组织的犯罪集团&#xff0c;揭示了这是一个怎样的世界。本报告将试图带您了解这些原住民的身份、行为方式、商业逻辑、受害者以及他们…

JMeter接口自动化测试实例—JMeter引用javaScript

Jmeter提供了JSR223 PreProcessor前置处理器&#xff0c;通过该工具融合了Java 8 Nashorn 脚本引擎&#xff0c;可以执行js脚本以便对脚本进行前置处理。其中比较典型的应用就是通过执行js脚本对前端数据进行rsa加密&#xff0c;如登录密码加密。但在这里我就简单的应用javaScr…

No view found for id 0x7f0901c3 for fragment解决以及线上bug排查技巧

情景再现 开发这么久&#xff0c;不知道你们是否也经历过这样的情况&#xff0c;测试或者用户&#xff0c;反馈app闪退&#xff0c;结果你自己打开开发工具&#xff0c;去调试&#xff0c;一切正常&#xff0c;然后闪退还是存在&#xff0c;只是在开发环境中不能重现。这种情况…

11 - git stash 开发中临时加塞了紧急任务怎么处理

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 开发中临时加塞了紧急任务怎么处理 开发中临时加塞了紧急任务怎么处理 当你此时工作区已经修改了 Readme 文件&#xff0c;然后突然需要解决其他问题&#xff08;紧急问题、新任务&…

ML-fairness-gym入门教学

1、ML-fairness-gym简介 ML-fairness-gym是一个探索机器学习系统长期影响的工具。可以用于评估机器学习系统的公平性和评估静态数据集上针对各种输入的误差度量的差异。开源网站&#xff1a;GitHub - google/ml-fairness-gym 2、安装ML-fairness-gym&#xff08;Windows&…

【贪心】CF1841 D

Codeforces 题意&#xff1a; 思路&#xff1a; 首先模拟一下样例 并没有发现什么 那么就去考虑特殊情况&#xff0c;看看有没有什么启发 考虑一个大区间包含所有小区间的情形&#xff0c;这种情况就是在这么多区间中找出两个区间 换句话说&#xff0c;这么多区间组成一个…

容器虚拟化基础之cgroups/LXC

"你真的&#xff0c;自由了~" 容器虚拟化基础之Cgroups: (1) 什么是cgroups cgroups是 linux 内核提供的一种机制&#xff0c; 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内&#xff0c;从而为系统资源管理提供一个统一…

嵌入式电火花线切割控制系统总体设计

2.1 电火花线切割机床的特点与结构 电火花线切割加工&#xff08; Wire Cut EDM &#xff09;是特种加工中电火花加工方式的一种&#xff0c;是 直接利用电能或热能进行加工的工艺方法。加工基本原理是利用在导丝架固定的轨 道上连续移动电极丝&#xff08;钼丝 / 铜丝&…

Spring框架【IOC详解】

目录 一、前言 1.1.Spring简介 1.2.使用Spring的优点 1.3.Spring组成 二、Spring之IOC详解 2.1.IOC理论推导 2.1.1.IOC分析实现 2.1.2.IOC本质 2.2.Maven项目导入Jar包 2.3.依赖注入 2.3.1.Set注入&#xff08;重点&#xff09; 2.3.2.构造注入 无参构造创建对象 …

STM32F4X-GPIO输入功能使用

STM32F4 GPIO输入模式配置 上一节讲GPIO的时候说到了将GPIO设置成输出模式&#xff0c;并通过将GPIO的电平拉高拉低控制LED灯的例程。GPIO除了用作输出功能之外&#xff0c;还可以用作输入功能。最常用的就是检测按键的输入电平。 硬件设计 本章的硬件是基于正点原子的探索者…

UI设计师个人工作总结范文精选

UI设计师个人工作总结范文(一) 在忙忙碌碌中&#xff0c;2019年又将过去了&#xff0c;在这一年当中&#xff0c;设计部无论是在运作模式、设计产值、还是人员结构&#xff0c;各方面的变化都比较大。 设计部的运作模式是从7月底开始进行调整的&#xff0c;以独立承包制的运营方…

Linux / Ubuntu磁盘扩容

测试时遇到了shell脚本执行错误的问题&#xff0c;找到脚本编写的楼哥&#xff0c;才发现自己给虚拟机的磁盘已经满了&#xff0c;没想到啊&#xff0c;业务的解压操作&#xff0c;这么费磁盘&#xff0c;那就需要进行磁盘的扩展&#xff0c;记录一下 1、首先停掉虚拟机&#…

只需四步,让Vscode连接远程服务器中的docker容器进行开发

0. 前提条件 本地windows或其他环境中安装了Vscode&#xff0c;Vscode中安装了Remote-SSH拓展&#xff08;用于利用SSH连接docker容器&#xff09;远程服务器中安装了docker&#xff0c;并且拉取了自己需要的镜像&#xff08;image&#xff09;有root权限&#xff0c;能使用su…

【环境配置】Windows10终端和VSCode下能够直接打开Anaconda-Prompt

很多小伙伴在 Windows 下做深度学习开发的时候&#xff0c;遇到终端没有在 Linux 那么方便&#xff0c;那么我们现在就可以来设置一下&#xff1b;这样我们也可以在文件夹内部右键打开终端&#xff0c;也可以在 VS Code 里面新建一个虚拟环境的控制台&#xff1b;这里主要是针对…

HCIP-OpenStack组件介绍

OpenStack排错思路&#xff1a; OpenStack查询日志&#xff0c;所有日志都在/var/log/模块名称下面。 OpenStack修改配置&#xff0c;所有配置文件都在/etc/模块名称下面。 openstack把这些组件服务都集成到httpd服务中了&#xff0c;目的是为了提升性能。登入不了openstack在…

Spark MLlib机器学习库(一)决策树和随机森林案例详解

Spark MLlib机器学习库(一)决策树和随机森林案例详解 1 决策树预测森林植被 1.1 Covtype数据集 数据集的下载地址&#xff1a; https://www.kaggle.com/datasets/uciml/forest-cover-type-dataset 该数据集记录了美国科罗拉多州不同地块的森林植被类型&#xff0c;每个样本…