数字IC手撕代码-同步FIFO

news2024/10/7 10:22:57

 前言: 

        本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。

目录如下:

1.数字IC手撕代码-分频器(任意偶数分频)

2.数字IC手撕代码-分频器(任意奇数分频)

3.数字IC手撕代码-分频器(任意小数分频)

4.数字IC手撕代码-异步复位同步释放

5.数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

6.数字IC手撕代码-序列检测(状态机写法)

7.数字IC手撕代码-序列检测(移位寄存器写法)

8.数字IC手撕代码-半加器、全加器

9.数字IC手撕代码-串转并、并转串

10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)

11.数字IC手撕代码-有限状态机FSM-饮料机

12.数字IC手撕代码-握手信号(READY-VALID)

13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

14.数字IC手撕代码-泰凌微笔试真题

15.数字IC手撕代码-平头哥技术终面手撕真题

16.数字IC手撕代码-兆易创新笔试真题

17.数字IC手撕代码-乐鑫科技笔试真题(4倍频)

18.数字IC手撕代码-双端口RAM(dual-port-RAM)

        ...持续更新

为了方便可以收藏导览博客:


目录

原理介绍

同步FIFO的工作方式

FIFO空满的产生

计数器判断空满

代码

Dual Port RAM

sync_FIFO

         testbench

波形图


原理介绍

        在设计系统时,会包含工作在不同时钟频率下的元件,例如处理器和外设。数据在这些元件之间传输时先进先出(FIFO first in first out)阵列起到了重要作用。FIFO是用于对在通信总线上传输的数据进行排列的简单存储结构。

        因此,FIFO常用来传输跨不同时钟域的数据。

        本节介绍简单的同步FIFO架构,读写使用同样的时钟,为我们后续写异步FIFO(读写时钟非同源)做铺垫。

        下面给出了一个同步FIFO的通用架构,DPRAM(Dual Port RAM 双端口RAM)作为存储器来存储信息,在此之上添加判断DPRAM空满信息的组件后,整个模块就是一个同步FIFO了,读写分别使用不同的使能和地址信号(读、写使能,读、写地址分立),使得整个模块可以进行同时读写。

         通过读、写指针产生各自的读、写地址,送到读、写端口。写指针指向下一个要写入的地址,读指针指向下一个要读取的地址。有效写使能使得写指针递增, 有效的读使能使读指针递增。

        图中的“状态模块”产生FIFO的空满信号,如果“fifo_full”有效,则说明FIFO内部的空间已满,不能再写入数据。如果“fifo_empty”有效,则说明FIFO内乜有可供读取的下一个有效数据。通过对读写指针位置的判断,该模块也可以指示出任意时刻FIFO中空或满区域的个数。

同步FIFO的工作方式

        复位后,读写指针都归0。此时“fifo_empty”信号置为有效而“fifo_full”保持低电平。因为FIFO为空,所以阻止对FIFO的读操作,只能进行写操作。后续的写操作会增加写指针的值,并将“fifo_empty”信号置为无效。在写到最后一个数据时,写指针等于RAM_SIZE-1。此时进行一个写指针会使写指针滚回到0,并将“fifo_full”信号置为高电平。

        总之,在读、写指针相等时,FIFO要么空要么满,所以需要对两种情况进行区分。

FIFO空满的产生

        以深度为4的FIFO为例,一开始读写指针指向同一个位置,FIFO为空。写入三个数据之后,写指针指向RAM_SIZE-1=3的位置,此时再写入一个数据,写指针(wr_ptr)滚回0,和读指针指向同一个位置,此时FIFO为满。

        根据这种逻辑,很容易推导出这么一个结论:无论读写指针此时指向什么位置,当wr_ptr+1==rd_ptr时,FIFO再写入一个数据就满了,所以有: 

        fifo_full = (rd_ptr == (wr_ptr + 1'b1))&& wr_fifo

从而有判断FIFO为满的RTL 代码:

always@(posedge clk or negedge rstn)begin
    if(!rstn)
        fifo_full <= 1'b0;
    else if(wr_fifo && rd_fifo)
        ;//do nothing
    else if(rd_fifo)
        fifo_full <= 1'b0;
    else if((rd_ptr = wr_ptr + 1'b1) && wr_fifo)
        fifo_full <= 1'b1;
end

        类似的,当读操作使得两个指针在下一个时钟相等时,FIFO变空,产生“fifo_empty”信号。有如下关系:无论读写指针此时指向什么位置,当rd_ptr+1==wr_ptr时,FIFO再读出一个数据就空了。

        fifo_empty = (wr_ptr == (rd_ptr + 1'b1))&& rd_fifo

从而有判断FIFO为空的RTL 代码: 

always @(posedge clk or negedge rstn)begin
    if(!rstn)
        fifo_empty <= 1'b1;
    else if(wr_fifo && rd_fifo)
        ;//do nothing
    else if(wr_fifo)
        fifo_empty <= 1'b0;
    else if((wr_ptr = rd_ptr + 1'b1) && rd_fifo)
        fifo_empty <= 1'b1;
end

计数器判断空满

        FIFO还有另一种利用计数器来指示FIFO空满的方法。

        计数器的宽度要与FIFO的深度相等,这样计数器才能记录FIFO数据的最大个数。计数器在复位时初始化为0,随后的任何写操作会将其递增1,任何读操作会使其递减1。

        在计数器为0时,很容易判断FIFO处于空状态,而当计数器的值等于FIFO的大小时,就能判断FIFO处于满状态。

        对于这种采用计数器来判断空满的方式实现比较简单,但是和上一个比较读写指针位置的方法相比资源占用会高一些。因为这种方法要求增加额外的硬件(计数器)来进行计数。

代码

        简单来说,FIFO就是一个有判断空满逻辑的双端口RAM,下面我们来写一下以指针循环一周期判断空满的方式的同步FIFO,但是在之前我们先要写一个双端口RAM来存储数据。

Dual Port RAM

module dual_port_ram#(
    parameter DEPTH = 16,
    parameter WIDTH = 8
)(
    input                       wr_clk      ,
    input                       wr_en       ,
    input   [$clog(DEPTH)-1:0]  wr_addr     ,
    input   [WIDTH-1:0]         wr_data     , 

    input                       rd_clk      ,
    input                       rd_en       ,
    input   [$clog(DEPTH)-1:0]  rd_addr     ,
    output  [WIDTH-1:0]         rd_data
);
reg [WIDTH-1:0] RAM_MEM [DEPTH-1:0];

always @(posedge wr_clk)begin
    if(wr_en)
        RAM_MEM[wr_addr] <= wr_data;
end

always @(posedge rd_clk)begin
    if(rd_en)
        RAM_MEM[rd_addr] <= rd_data;     
end

endmodule

        整个双端口RAM其实就是一个简单的写使能时,把数据写入输入的写地址。读使能时,把数据从地址里读出来的那么一个功能,后续在FIFO中例化该Dual Port RAM模块。

sync_FIFO

`include "clog.v"

module sync_fifo#(
    parameter WIDTH = 8     ,
    parameter DEPTH = 16 
)(
    input                       clk         ,       
    input                       rstn        ,   // reset while rstn is negative
    
    //write interface
    input       [WIDTH-1:0]     data_in     ,   // input data 
    input                       wr_en       ,   // write enable 
    
    //read interface
    input                       rd_en       ,   // read enable
    output      [WIDTH-1:0]     data_out    ,

    output  reg                 fifo_empty  ,
    output  reg                 fifo_full     
);

//signal define 
reg [clog(DEPTH)-1:0]    wr_ptr;
reg [clog(DEPTH)-1:0]    rd_ptr;

wire wr_fifo;
wire rd_fifo;

//write data opration
always @(posedge clk or negedge rstn)begin
    if(!rstn)
        wr_ptr <= 1'b0;
    else if(wr_fifo)
        wr_ptr <= wr_ptr + 1'b1;  
end

assign wr_fifo = wr_en && !fifo_full;

//read data opration
always @(posedge clk or negedge rstn)begin
    if(!rstn)
        rd_ptr <= 1'b0;
    else if(rd_fifo)
        rd_ptr <= rd_ptr + 1'b1;
end

assign rd_fifo = rd_en && !fifo_empty;

//full signal judgment
always@(posedge clk or negedge rstn)begin
    if(!rstn)
        fifo_full <= 1'b0;
    else if(wr_fifo && rd_fifo)
        ;//do nothing
    else if(rd_fifo)
        fifo_full <= 1'b0;
    else if((rd_ptr == wr_ptr + 1'b1) && wr_fifo)
        fifo_full <= 1'b1;
end

//empty signal judgment
always @(posedge clk or negedge rstn)begin
    if(!rstn)
        fifo_empty <= 1'b1;
    else if(wr_fifo && rd_fifo)
        ;//do nothing
    else if(wr_fifo)
        fifo_empty <= 1'b0;
    else if((wr_ptr == rd_ptr + 1'b1) && rd_fifo)
        fifo_empty <= 1'b1;
end


dual_port_ram #(
    .DEPTH      (DEPTH)     ,
    .WIDTH      (WIDTH)
)u_dual_port_ram
(
    .wr_clk      (clk)       ,       //sync FIFO ,wr_clk = rd_clk
    .wr_en       (wr_fifo)   ,
    .wr_addr     (wr_ptr)    ,
    .wr_data     (data_in)   ,

    .rd_clk      (clk)       ,
    .rd_en       (rd_fifo)   ,
    .rd_addr     (rd_ptr)    ,
    .rd_data     (data_out)
);

endmodule

        此外,还写了一个判断位宽的函数clog.v

`ifndef MY_CLOG
`define MY_CLOG

function integer clog (input integer depth);
    begin
        for (clog=0; depth-1>0; clog=clog+1) 
            depth = depth >>1;                          
    end
endfunction

`endif

testbench

`timescale 1ns/1ns

module sync_fifo_tb();

parameter WIDTH = 8;
parameter DEPTH = 16;

reg                     clk         ;
reg                     rstn        ;

reg     [WIDTH-1:0]     data_in     ;
reg                     rd_en       ;
reg                     wr_en       ;

wire    [WIDTH-1:0]     data_out    ;
wire                    empty       ;
wire                    full        ;

always #5 clk = ~clk;

initial begin
    clk     <= 1'b0;
    rstn    <= 1'b0;
    data_in <= 'd0;
    rd_en   <= 1'b0;
    wr_en   <= 1'b0;
    
    //write 16 times to make fifo full
    #10
    rstn    <= 1'b1;
    repeat(16)begin
        @(negedge clk)begin
            wr_en   <= 1'b1;
            data_in <= $random; // generate 8bit random number data_in
        end
    end
    
    //read 16 times to make fifo empty
    repeat(16)begin
        @(negedge clk)begin
            wr_en   <= 1'b0;
            rd_en   <= 1'b1;
        end
    end

    //read and write 8 times
    repeat(8)begin
        @(negedge clk)begin
            wr_en   <= 1'b1;
            data_in <= $random; 
            rd_en   <= 1'b0;
        end
    end

    //Continuous read and write
    forever begin
        @(negedge clk)begin
            wr_en   <= 1'b1;
            data_in <= $random;
            rd_en   <= 1'b1;
        end
    end
end

initial begin
    #800
    $finish();
end

initial begin
    $fsdbDumpfile("sync_fifo.fsdb");
    $fsdbDumpvars(0);
end

sync_fifo #(
    .WIDTH      (WIDTH)     ,
    .DEPTH      (DEPTH)
)u_sync_fifo
(
    .clk        (clk)       ,
    .rstn       (rstn)      ,
    .data_in    (data_in)   ,
    .rd_en      (rd_en)     ,
    .wr_en      (wr_en)     ,

    .data_out   (data_out)  ,
    .fifo_empty (empty)     ,
    .fifo_full  (full)
);

endmodule

波形图

        仿真结果和分析的结果一致, 写入16个数据将FIFO写满,此时full信号拉高;读出16个数据将FIFO读空,此时empty信号拉高;写入8个数据之后,同时读写,写入数据和读出数据均保持一致,功能正确。

        了解了同步FIFO的设计方法后,再进行异步FIFO的设计就比较简单了,下篇博客记录如何写一个异步FIFO,解决FIFO中跨时钟域的问题。

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

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

相关文章

磁环选型攻略及EMC整改技巧

磁环选型攻略及EMC整改技巧 今天跟大家分享一下磁环选型及应用相关的知识&#xff0c;希望对你有帮助。 本文将从以下四个方面对磁环进行阐述。 一、磁环的应用场景 首先我们来看几张图片 图1 显示屏VGA线 图2 适配器连接线 图3 USB通信线 这三根线都是我们生活中常见的供电…

简单个人网页设计作业 静态HTML个人博客主页——HTML+CSS+JavaScript 明星鹿晗(7页)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

ping回显间隔长或第一个包很久才显示是怎么回事?

问题现象 在ping某些域名的时候&#xff0c;第一个回显十几秒才出现&#xff0c;但时延time正常&#xff0c;第二个包开始回显频率正常且最终统计结果为不丢包&#xff1b;或是每一个回显均间隔数秒才显示&#xff0c;但时延time又都是正常的&#xff0c;且统计结果为不丢包。…

U-Net 模型改进和应用场景研究性综述

U-Net综述1 文章介绍2 U-Net介绍3 结构改进4 非结构改进4.1 预处理——数据增强4.2 训练——数据归一化4.3 训练——激活函数4.4 训练——损失函数4.5 结构改进总结5 U-Net应用场景5.1 视网膜血管分割5.2 肺结节分割5.3 肝脏和肝脏肿瘤分割5.4 脑肿瘤分割5.5 不同应用场景总结6…

[附源码]计算机毕业设计基于Springboot校刊投稿系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Vue学习:模板语法

容器里面的模板&#xff1a;对应的模板语法 {{xxx}}:插值语法 指令语法&#xff1a; v-bind&#xff1a;vue指令 绑定 后面的数据会变成属性或者方法 <h1>指令语法</h1><!-- v-bind会将"xxx"里面的内容当成表达式执行 --><a v-bind:href&quo…

这些 MySQL 最朴素的监控方式!用完爱不释手!

对于当前数据库的监控方式有很多&#xff0c;分为数据库自带、商用、开源三大类&#xff0c;每一种都有各自的特色&#xff1b;而对于 mysql 数据库由于其有很高的社区活跃度&#xff0c;监控方式更是多种多样&#xff0c;不管哪种监控方式最核心的就是监控数据&#xff0c;获取…

嵌入式之总线协议:1、UART

嵌入式之总线协议&#xff1a;1、UART 目录 第一章 UART 帧格式讲解 第二章 UART 寄存器讲解 第三章 UART 编程 第四章 输出重定向 第五章 RS232、RS485协议原理与应用 第一章 UART嵌入式之总线协议&#xff1a;1、UART前言一、UART简介1、串行/并行1.1 并行1.2 串行2、异步3、…

C语言第十八课:初阶结构体

目录 前言&#xff1a; 一、结构体类型的声明&#xff1a; 1.结构的基础知识&#xff1a; 2.结构的声明&#xff1a; 3.结构成员允许的类型&#xff1a; 4.结构体变量的定义&#xff1a; 5.结构体变量的初始化&#xff1a; 二、结构体成员的访问&#xff1a; 1.结构体变量访…

[附源码]计算机毕业设计实验室管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

4 第一个程序

第一个程序 1 源程序 源程序中包括两种指令&#xff1a;伪指令和汇编指令 汇编指令是有对应机器码的指令&#xff0c;可以用CPU直接执行 伪指令没有对应的机器码&#xff0c;只有编译器执行不用CPU执行 1.1 segment ends segment和ends的功能是定义一个段。使用格式如下 …

[附源码]计算机毕业设计三星小区车辆登记系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

多线程中的公平锁、非公平锁、可重入锁、死锁【详细总结】

目录公平锁非公平锁公平锁和非公平锁的用法可重入锁synchronized可重入锁示例ReentrantLock的示例代码死锁死锁产生的原因常用解决死锁的方法判断程序是否发生死锁死锁的案例&#xff08;面试会问&#xff09;公平锁 多个线程按照申请锁的顺序去获得锁&#xff0c;线程会直接进…

第十六章 Dijkstra算法的讲解以及证明(与众不同的通俗证明)

第十六章 Dijsktra算法的讲解以及粗略证明一、Dijkstra的用途二、Dijkstra的思想及证明&#xff08;1&#xff09;相关结论及证明&#xff1a;结论1&#xff1a;必须借助中间点时某个点到终点的最短路程&#xff1d;该点到中间点的最短距离&#xff0b;中间点到终点的最短距离结…

数据分析思维(一)|信度与效度思维

信度与效度思维 1、概念 信度与效度思维通常用于在数据分析中进行更有价值的指标选择。 信度&#xff1a;指标的可靠程度。包括一致性及稳定性。&#xff08;口径是否一致&#xff0c;是否具有波动性&#xff09; 效度&#xff1a;指标的有效性。一个数据或指标的生成&…

JavaFX项目打包成可安装exe文件

开发环境&#xff1a;Windows 10 2H JDK&#xff1a;jdk1.8.0_112 IDEA&#xff1a;2020.3 1. 项目中导入插件依赖 <plugin><groupId>io.github.fvarrui</groupId><artifactId>javapackager</artifactId><version>1.6.6</version>&…

[附源码]计算机毕业设计JAVA婴幼儿玩具共享租售平台

[附源码]计算机毕业设计JAVA婴幼儿玩具共享租售平台 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM …

Java学习之多态一

目录 一、入门案例 Food类 Animal类 Master类 运行测试 分析 运行结果 问题总结 二、方法的多态 三、对象的多态&#xff08;重难点/核心&#xff09; 四个非常重要的知识点&#xff08;背诵&#xff09; 举例说明 父类-Animal类 子类-Dog类 子类-Cat类 运行-Po…

如何将数据库从 CloudKit 迁移到 Firebase

为什么要迁移 如果该服务仅支持 Apple 设备,则使用 CloudKit 和 CoreData 可能是最佳选择。但是,如果您还需要支持 Web 和 Android,情况就不同了。 当同时支持Web和Android时,可以使用CloudKit JS访问iCloud DB。但是实施起来比较困难,需要有苹果账号。 如果未来有同时支…

【滤波跟踪】基于北方苍鹰和粒子群算法优化粒子滤波器实现目标滤波跟踪附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …