• 野火FPGA入门(7):IP核调用


    第24讲:快速开发的法宝:IP核

    IP核是什么?

    IP(Intellectual Property)即知识产权。在半导体产业将IP核定义为“用于ASIC或FPGA中的预先设计好的电路功能模块”。简而言之,这里的IP即电路功能模块。
    在数字电路中,将常用的且比较复杂的功能模块设计成参数可修改的模块,让其他用户可以直接调用这些模块,这就是IP核。

    为什么要使用IP核?

    随着FPGA的规模越来越大,它的设计也是越来越复杂。随着设计规模增大,复杂度提高,使用IP核可以提高开发效率,减少设计和调试时间,加速开发进程,降低开发成本,是业界的发展趋势。

    IP核的存在形式

    分类依据:产品交付方式
    1、HDL语言形式–软核
    硬件描述语言;可进行参数调整,复杂性强,布局布线灵活,设计周期短,设计投入少
    2、网表形式–固核
    完成了综合的功能块,可预布线特定信号或分配特定的布线资源
    3、版图形式–硬核
    硬核是完成提供设计的最终阶段产品:掩膜(Mask);缺乏灵活性,可移植性差,更易于实现IP核的保护

    IP核的缺点

    1、IP核往往不能跨平台使用
    2、IP核不透明,看不到内部核心代码
    3、定制IP需额外收费

    Quartus II软件件下IP核的调用

    1、Mega Wizard插件管理器
    2、SOPC构造器
    3、DSP构造器
    4、Qsys设计系统例化

    Altera IP核的分类


    第25讲:PLL-IP核的调用

    PLL IP核简介

    PLL(Phase Locked Loop,即锁相环)是最常用的IP核之一,其性能强大,可以对输入到FPGA的时钟信号进行任意分频、倍频、相位调整、占空比调整,从而输出一个期望时钟。

    PLL的基本工作原理

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    pll.v

    `timescale  1ns/1ns
    
    module  pll
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
    
        output  wire    clk_mul_2   ,   //系统时钟经过2倍频后的时钟
        output  wire    clk_div_2   ,   //系统时钟经过2分频后的时钟
        output  wire    clk_phase_90,   //系统时钟经过相移90°后的时钟
        output  wire    clk_ducle_20,   //系统时钟变为占空比为20%的时钟
        output  wire    locked          //检测锁相环是否已经锁定,
                                        //只有该信号为高时输出的时钟才是稳定的
    );
    
    //------------------------ pll_inst ------------------------
    pll_ip  pll_ip_inst
    (
        .inclk0 (sys_clk        ),  //input     inclk0
    
        .c0     (clk_mul_2      ),  //output    c0
        .c1     (clk_div_2      ),  //output    c1
        .c2     (clk_phase_90   ),  //output    c2
        .c3     (clk_ducle_20   ),  //output    c3
        .locked (locked         )   //output    locked
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    tb_pll.v

    `timescale  1ns/1ns
    module tb_pll();
    
    //wire  define
    wire    clk_mul_2   ;
    wire    clk_div_2   ;
    wire    clk_phase_90;
    wire    clk_ducle_20;
    wire    locked      ;
    
    //reg   define
    reg     sys_clk     ;
    
    //初始化系统时钟
    initial sys_clk = 1'b1;
    
    //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
    always #10 sys_clk = ~sys_clk;
    
    //------------------------pll_inst------------------------
    pll pll_inst
    (
        .sys_clk        (sys_clk        ),  //input     sys_clk
    
        .clk_mul_2      (clk_mul_2      ),  //output    clk_mul_2
        .clk_div_2      (clk_div_2      ),  //output    clk_div_2
        .clk_phase_90   (clk_phase_90   ),  //output    clk_phase_90
        .clk_ducle_20   (clk_ducle_20   ),  //output    clk_ducle_20
        .locked         (locked         )   //output    locked
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述


    第26讲:ROM-IP核的调用

    ROM IP核简介

    ROM是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特点是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。
    在这里插入图片描述

    系统框图
    在这里插入图片描述
    波形图
    在这里插入图片描述

    rom_ctrl.v

    `timescale  1ns/1ns
    
    module  rom_ctrl
    #(
    	parameter   CNT_MAX =   24'd9_999_999;  //0.2s计数器最大值
    )
    (
        input   wire        sys_clk     ,   //系统时钟,频率50MHz
        input   wire        sys_rst_n   ,   //复位信号,低有效
        input   wire        key1_flag   ,   //按键1消抖后有效信号
        input   wire        key2_flag   ,   //按键2消抖后有效信号
    
        output  reg [7:0]   addr            //输出读ROM地址
    );
    
    //reg   define
    reg             addr_flag1      ;   //特定地址1标志信号
    reg             addr_flag2      ;   //特定地址2标志信号
    reg     [23:0]  cnt_200ms       ;   //0.2s计数器
    
    //产生特定地址1标志信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            addr_flag1   <=  1'b0;
        else    if(key2_flag == 1'b1)
            addr_flag1  <=  1'b0;
        else    if(key1_flag == 1'b1)
            addr_flag1   <=  ~addr_flag1;
    
    //产生特定地址2标志信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            addr_flag2   <=  1'b0;
        else    if(key1_flag == 1'b1)
            addr_flag2  <=  1'b0;
        else    if(key2_flag == 1'b1)
            addr_flag2   <=  ~addr_flag2;
    
    //0.2s循环计数
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_200ms    <=  24'd0;
        else    if(cnt_200ms == CNT_MAX || addr_flag1 == 1'b1 || addr_flag2 == 1'b1)
            cnt_200ms   <=  24'd0;
        else
            cnt_200ms   <=  cnt_200ms + 1'b1;
    
    //让地址从0~255循环,其中两个按键控制两个特定地址的跳转
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            addr    <=  8'd0;
        else    if(addr == 8'd255 && cnt_200ms == CNT_MAX)
            addr    <=  8'd0;
        else    if(addr_flag1 == 1'b1)
            addr    <=  8'd99;
        else    if(addr_flag2 == 1'b1)
            addr    <=  8'd199;
        else    if(cnt_200ms == CNT_MAX)
            addr    <=  addr + 1'b1;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    rom.v

    `timescale  1ns/1ns
    
    module  rom
    (
        input   wire            sys_clk     ,   //系统时钟,频率50MHz
        input   wire            sys_rst_n   ,   //复位信号,低电平有效
        input   wire    [1:0]   key         ,   //输入按键信号
        
        output  wire            stcp        ,   //输出数据存储器时钟
        output  wire            shcp        ,   //移位寄存器的时钟输入
        output  wire            ds          ,   //串行数据输入
        output  wire            oe              //输出使能信号
    );
    
    //wire  define
    wire    [7:0]   addr        ;   //地址线
    wire    [7:0]   rom_data    ;   //读出ROM数据
    wire            key1_flag   ;   //按键1消抖信号
    wire            key2_flag   ;   //按键2消抖信号
    
    //----------------rom_ctrl_inst----------------
    rom_ctrl    rom_ctrl_inst
    (
        .sys_clk     (sys_clk   ),   //系统时钟,频率50MHz
        .sys_rst_n   (sys_rst_n ),   //复位信号,低有效
        .key1_flag   (key1_flag ),   //按键1消抖后有效信号
        .key2_flag   (key2_flag ),   //按键2消抖后有效信号
                                    
        .addr        (addr      )    //输出读ROM地址
    );
    
    //----------------key1_filter_inst--------------
    key_filter  key1_filter_inst
    (
        .sys_clk     (sys_clk   ),   //系统时钟50Mhz
        .sys_rst_n   (sys_rst_n ),   //全局复位
        .key_in      (key[0]    ),   //按键输入信号
    
        .key_flag    (key1_flag )    //key_flag为1时表示消抖后检测到按键被按下
                                     //key_flag为0时表示没有检测到按键被按下
    );
    
    //----------------key2_filter_inst--------------
    key_filter  key2_filter_inst
    (
        .sys_clk     (sys_clk   ),   //系统时钟50Mhz
        .sys_rst_n   (sys_rst_n ),   //全局复位
        .key_in      (key[1]    ),   //按键输入信号
    
        .key_flag    (key2_flag )    //key_flag为1时表示消抖后检测到按键被按下
                                     //key_flag为0时表示没有检测到按键被按下
    );
    
    //----------------seg_595_dynamic_inst--------------
    seg_595_dynamic     seg_595_dynamic_inst
    (
        .sys_clk     (sys_clk         ), //系统时钟,频率50MHz
        .sys_rst_n   (sys_rst_n       ), //复位信号,低有效
        .data        ({12'd0,rom_data}), //数码管要显示的值
        .point       (0               ), //小数点显示,高电平有效
        .seg_en      (1'b1            ), //数码管使能信号,高电平有效
        .sign        (0               ), //符号位,高电平显示负号
    
        .stcp        (stcp            ), //输出数据存储寄时钟
        .shcp        (shcp            ), //移位寄存器的时钟输入
        .ds          (ds              ), //串行数据输入
        .oe          (oe              )  //输出使能信号
    );
    
    //----------------rom_256x8_inst---------------
    rom_256x8   rom_256x8_inst
    (
        .address    (addr       ),
        .clock      (sys_clk    ),
        .q          (rom_data   )
    ); 
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    在这里插入图片描述


    第27讲:RAM-IP核的调用

    RAM是随机存取存储器(Random Access Memory)的简称,是一个易失性存储器;其工作时可以随时对任何一个指定的地址写入或读出数据。这是ROM所并不具备的功能。

    在这里插入图片描述


    波形图绘制
    在这里插入图片描述

    在这里插入图片描述
    子功能模块绘制
    在这里插入图片描述
    系统框图
    在这里插入图片描述

    key_filter

    `timescale  1ns/1ns
    
    module  key_filter
    #(
        parameter CNT_MAX = 20'd999_999 //计数器计数最大值
    )
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
        input   wire    sys_rst_n   ,   //全局复位
        input   wire    key_in      ,   //按键输入信号
    
        output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                        //key_flag为0时表示没有检测到按键被按下
    );
    
    //reg   define
    reg     [19:0]  cnt_20ms    ;   //计数器
    
    //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_20ms <= 20'b0;
        else    if(key_in == 1'b1)
            cnt_20ms <= 20'b0;
        else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    
    //key_flag:当计数满20ms后产生按键有效标志位
    //且key_flag在999_999时拉高,维持一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else    if(cnt_20ms == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    ram_crtl

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    `timescale  1ns/1ns
    
    module  ram_ctrl
    (
        input   wire         sys_clk   , //系统时钟,频率50MHz
        input   wire         sys_rst_n , //复位信号,低有效
        input   wire         key1_flag , //按键1消抖后有效信号,作为写标志信号
        input   wire         key2_flag , //按键2消抖后有效信号,作为读标志信号
     
        output  reg          wr_en     , //输出写RAM使能,高点平有效
        output  reg          rd_en     , //输出读RAM使能,高电平有效
        output  reg  [7:0]   addr      , //输出读写RAM地址
        output  wire [7:0]   wr_data     //输出写RAM数据
    );
    
    //parameter define
    parameter   CNT_MAX =   9_999_999;  //0.2s计数器最大值
    
    //reg   define
    reg     [23:0]  cnt_200ms       ;   //0.2s计数器
    
    //让写入的数据等于地址数,即写入数据0~255
    assign  wr_data =   (wr_en == 1'b1) ? addr : 8'd0;
    
    //wr_en:产生写RAM使能信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            wr_en   <=  1'b0;
        else    if(addr == 8'd255)
            wr_en  <=  1'b0;
        else    if(key1_flag == 1'b1)
            wr_en  <=  1'b1;
    
    //rd_en:产生读RAM使能信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            rd_en   <=  1'b0;
        else    if(key2_flag == 1'b1 && wr_en == 1'b0)
            rd_en   <=  1'b1;
        else    if(key1_flag == 1'b1)
            rd_en   <=  1'b0;
        else
            rd_en   <=  rd_en;
    
    //0.2s循环计数
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_200ms    <=  24'd0;
        else    if(cnt_200ms == CNT_MAX || key2_flag == 1'b1)
            cnt_200ms   <=  24'd0;
        else    if(rd_en == 1'b1)
            cnt_200ms   <=  cnt_200ms + 1'b1;
    
    //写使能有效时,
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            addr    <=  8'd0;
        else if((addr==8'd255 && cnt_200ms==CNT_MAX) || (addr==8'd255 && wr_en==1'b1) || key2_flag==1'b1 || key1_flag==1'b1)
            addr    <=  8'd0;
        else if((wr_en == 1'b1) || (rd_en == 1'b1 && cnt_200ms == CNT_MAX))
            addr    <=  addr + 1'b1;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    ram

    `timescale  1ns/1ns
    
    module  ram
    (
        input   wire            sys_clk     ,   //系统时钟,频率50MHz
        input   wire            sys_rst_n   ,   //复位信号,低电平有效
        input   wire    [1:0]   key         ,   //输入按键信号
        
        output  wire            stcp        ,   //输出数据存储器时钟
        output  wire            shcp        ,   //移位寄存器的时钟输入
        output  wire            ds          ,   //串行数据输入
        output  wire            oe              //输出使能信号
    );
    
    //wire  define
    wire            wr_en       ;   //写使能
    wire            rd_en       ;   //读使能
    wire    [7:0]   addr        ;   //地址线
    wire    [7:0]   wr_data     ;   //写数据
    wire    [7:0]   rd_data     ;   //读出RAM数据
    wire            key1_flag   ;   //按键1消抖信号
    wire            key2_flag   ;   //按键2消抖信号
    
    //----------------ram_ctrl_inst----------------
    ram_ctrl    ram_ctrl_inst
    (
        .sys_clk   (sys_clk     ), //系统时钟,频率50MHz
        .sys_rst_n (sys_rst_n   ), //复位信号,低有效
        .key1_flag (key1_flag   ), //按键1消抖后有效信号,作为写标志信号
        .key2_flag (key2_flag   ), //按键2消抖后有效信号,作为读标志信号
    
        .wr_en     (wr_en       ), //输出写RAM使能,高点平有效
        .rd_en     (rd_en       ), //输出读RAM使能,高电平有效
        .addr      (addr        ), //输出读写RAM地址
        .wr_data   (wr_data     )  //输出写RAM数据
    );
    
    //----------------key1_filter_inst----------------
    key_filter  key1_filter_inst
    (
        .sys_clk     (sys_clk   ),   //系统时钟50Mhz
        .sys_rst_n   (sys_rst_n ),   //全局复位
        .key_in      (key[0]    ),   //按键输入信号
    
        .key_flag    (key1_flag )    //key_flag为1时表示消抖后检测到按键被按下
                                     //key_flag为0时表示没有检测到按键被按下
    );
    
    //----------------key2_filter_inst----------------
    key_filter  key2_filter_inst
    (
        .sys_clk     (sys_clk   ),   //系统时钟50Mhz
        .sys_rst_n   (sys_rst_n ),   //全局复位
        .key_in      (key[1]    ),   //按键输入信号
    
        .key_flag    (key2_flag )    //key_flag为1时表示消抖后检测到按键被按下
                                     //key_flag为0时表示没有检测到按键被按下
    );
    
    //----------------seg_595_dynamic_inst----------------
    seg_595_dynamic     seg_595_dynamic_inst
    (
        .sys_clk     (sys_clk         ), //系统时钟,频率50MHz
        .sys_rst_n   (sys_rst_n       ), //复位信号,低有效
        .data        ({12'd0,rd_data} ), //数码管要显示的值
        .point       (0               ), //小数点显示,高电平有效
        .seg_en      (1'b1            ), //数码管使能信号,高电平有效
        .sign        (0               ), //符号位,高电平显示负号
    
        .stcp        (stcp            ), //输出数据存储寄时钟
        .shcp        (shcp            ), //移位寄存器的时钟输入
        .ds          (ds              ), //串行数据输入
        .oe          (oe              )  //输出使能信号
    );
    
    //---------------rom_256x8_inst--------------
    ram_256x8   ram_256x8_inst 
    (
        .aclr       (~sys_rst_n ),  //异步清零信号
        .address    (addr       ),  //读写地址线
        .clock      (sys_clk    ),  //使用系统时钟作为读写时钟
        .data       (wr_data    ),  //输入写入RAM的数据
        .rden       (rd_en      ),  //读RAM使能
        .wren       (wr_en      ),  //写RAM使能
        .q          (rd_data    )   //输出读RAM数据
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    第28讲:FIFO-IP核的调用

    FIFO(First in First out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。
    FIFO存储器主要是作为缓存,应用在同步时钟系统和异步时钟系统中,在很多的设计中都会使用;如:多比特数据做跨时钟域处理、前后带宽不同步等都用到了FIFO。

    跨时钟域处理
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    同步FIFO

    fifo.v

    `timescale  1ns/1ns
    
    module fifo
    (
        input   wire            sys_clk     ,   //系统时钟50Mhz
        input   wire    [7:0]   pi_data     ,   //输入顶层模块的数据
                                                //要写入到FIFO中的数据
        input   wire            pi_flag     ,   //输入数据有效标志信号
                                                //也作为FIFO的写请求信号
        input   wire            rdreq       ,   //FIFO读请求信号
    
        output  wire    [7:0]   po_data     ,   //FIFO读出的数据
        output  wire            empty       ,   //FIFO空标志信号,高有效
        output  wire            full        ,   //FIFO满标志信号,高有效
        output  wire    [7:0]   usedw           //FIFO中存在的数据个数
    );
    
    //---------------scfifo_256x8_inst-------------------
    scfifo_256x8    scfifo_256x8_inst(
        .clock  (sys_clk    ),  //input            clock
        .data   (pi_data    ),  //input    [7:0]   data
        .rdreq  (rdreq      ),  //input            rdreq
        .wrreq  (pi_flag    ),  //input            wrreq
    
        .empty  (empty      ),  //output           empty
        .full   (full       ),  //output           full
        .q      (po_data    ),  //output   [7:0]   q
        .usedw  (usedw      )   //output   [7:0]   usedw
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    tb_fifo.v

    `timescale  1ns/1ns
    module tb_fifo();
    
    //reg   define
    reg         sys_clk     ;
    reg [7:0]   pi_data     ;
    reg         pi_flag     ;
    reg         rdreq       ;
    reg         sys_rst_n   ;
    reg [1:0]   cnt_baud    ;
    
    //wire  define
    wire    [7:0]   po_data ;
    wire            empty   ;
    wire            full    ;
    wire    [7:0]   usedw   ;
    
    //初始化系统时钟、复位
    initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #100;
        sys_rst_n <= 1'b1;
    end
    
    //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
    always #10 sys_clk = ~sys_clk;
    
    //cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_baud <= 2'b0;
        else    if(&cnt_baud == 1'b1)
            cnt_baud <= 2'b0;
        else
            cnt_baud <= cnt_baud + 1'b1;
    
    //pi_flag:输入数据有效标志信号,也作为FIFO的写请求信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_flag <= 1'b0;
        //每4个时钟周期且没有读请求时产生一个数据有效标志信号
        else    if((cnt_baud == 2'd0) && (rdreq == 1'b0))
            pi_flag <= 1'b1;
        else
            pi_flag <= 1'b0;
    
    //pi_data:输入顶层模块的数据,要写入到FIFO中的数据
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_data <= 8'b0;
        //pi_data的值为0~255依次循环
        else    if((pi_data == 8'd255) && (pi_flag == 1'b1))
            pi_data <= 8'b0;
        else    if(pi_flag  == 1'b1)    //每当pi_flag有效时产生一个数据
            pi_data <= pi_data + 1'b1;
    
    //rdreq:FIFO读请求信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            rdreq <= 1'b0;
        else    if(full == 1'b1)  //当FIFO中的数据存满时,开始读取FIFO中的数据
            rdreq <= 1'b1;
        else    if(empty == 1'b1) //当FIFO中的数据被读空时停止读取FIFO中的数据
            rdreq <= 1'b0;
    
    //------------------------fifo_inst------------------------
    fifo fifo_inst(
        .sys_clk    (sys_clk    ),  //input             sys_clk
        .pi_data    (pi_data    ),  //input     [7:0]   pi_data
        .pi_flag    (pi_flag    ),  //input             pi_flag
        .rdreq      (rdreq      ),  //input             rdreq
    
        .po_data    (po_data    ),  //output    [7:0]   po_data
        .empty      (empty      ),  //output            empty
        .full       (full       ),  //output            full
        .usedw      (usedw      )   //output    [7:0]   usedw
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    异步FIFO

    fifo.v

    `timescale  1ns/1ns
    
    module fifo
    (
        //如果端口信号较多,我们可以将端口信号进行分组
        //把相关的信号放在一起,使代码更加清晰
        //FIFO写端
        input   wire         wrclk     ,   //同步于FIFO写数据的时钟50MHz
        input   wire  [7:0]  pi_data   ,   //输入顶层模块的数据,要写入到FIFO中
                                           //的数据同步于wrclk时钟
        input   wire         pi_flag   ,   //输入数据有效标志信号,也作为FIFO的
                                           //写请求信号,同步于wrclk时钟
        //FIFO读端
        input   wire         rdclk     ,   //同步于FIFO读数据的时钟25MHz
        input   wire         rdreq     ,   //FIFO读请求信号,同步于rdclk时钟
    
        //FIFO写端
        output  wire         wrempty   ,   //FIFO写端口空标志信号,高有效,
                                           //同步于wrclk时钟
        output  wire         wrfull    ,   //FIFO写端口满标志信号,高有效,
                                           //同步于wrclk时钟
        output  wire  [7:0]  wrusedw   ,   //FIFO写端口中存在的数据个数,
                                           //同步于wrclk时钟
        //FIFO读端
        output  wire  [15:0] po_data   ,   //FIFO读出的数据,同步于rdclk时钟
        output  wire         rdempty   ,   //FIFO读端口空标志信号,高有效,
                                           //同步于rdclk时钟
        output  wire         rdfull    ,   //FIFO读端口满标志信号,高有效,
                                           //同步于rdclk时钟
        output  wire  [6:0]  rdusedw       //FIFO读端口中存在的数据个数,
                                           //同步于rdclk时钟
    );
    
    //----------------------dcfifo_256x8to128x16_inst-----------------------
    dcfifo_256x8to128x16    dcfifo_256x8to128x16_inst
    (
        .data   (pi_data),  //input     [7:0]   data
        .rdclk  (rdclk  ),  //input             rdclk
        .rdreq  (rdreq  ),  //input             rdreq
        .wrclk  (wrclk  ),  //input             wrclk
        .wrreq  (pi_flag),  //input             wrreq
    
        .q      (po_data),  //output    [15:0]  q
        .rdempty(rdempty),  //output            rdempty
        .rdfull (rdfull ),  //output            rdfull
        .rdusedw(rdusedw),  //output    [6:0]   rdusedw
        .wrempty(wrempty),  //output            wrempty
        .wrfull (wrfull ),  //output            wrfull
        .wrusedw(wrusedw)   //output    [7:0]   wrusedw
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    tb_fifo.v

    `timescale  1ns/1ns
    module tb_fifo();
    
    //reg   define
    reg          wrclk          ;
    reg  [7:0]   pi_data        ;
    reg          pi_flag        ;
    reg          rdclk          ;
    reg          rdreq          ;
    reg          sys_rst_n      ;
    reg  [1:0]   cnt_baud       ;
    reg          wrfull_reg0    ;
    reg          wrfull_reg1    ;
    
    //wire  define
    wire            wrempty     ;
    wire            wrfull      ;
    wire    [7:0]   wrusedw     ;
    wire    [15:0]  po_data     ;
    wire            rdempty     ;
    wire            rdfull      ;
    wire    [6:0]   rdusedw     ;
    
    //初始化时钟、复位
    initial begin
        wrclk      = 1'b1;
        rdclk      = 1'b1;
        sys_rst_n <= 1'b0;
        #100;
        sys_rst_n <= 1'b1;
    end
    
    //wrclk:模拟FIFO的写时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
    always #10 wrclk = ~wrclk;
    
    //rdclk:模拟FIFO的读时钟,每20ns电平翻转一次,周期为40ns,频率为25MHz
    always #20 rdclk = ~rdclk;
    
    //cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
    always@(posedge wrclk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_baud <= 2'b0;
        else    if(&cnt_baud == 1'b1)
            cnt_baud <= 2'b0;
        else
            cnt_baud <= cnt_baud + 1'b1;
    
    //pi_flag:输入数据有效标志信号,也作为FIFO的写请求信号
    always@(posedge wrclk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_flag <= 1'b0;
        //每4个时钟周期且没有读请求时产生一个数据有效标志信号
        else    if((cnt_baud == 2'd0) && (rdreq == 1'b0))
            pi_flag <= 1'b1;
        else
            pi_flag <= 1'b0;
    
    //pi_data:输入顶层模块的数据,要写入到FIFO中的数据
    always@(posedge wrclk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_data <= 8'b0;
        pi_data的值为0~255依次循环
        else    if((pi_data == 8'd255) && (pi_flag == 1'b1))
            pi_data <= 8'b0;
        else    if(pi_flag  == 1'b1)    //每当pi_flag有效时产生一个数据
            pi_data <= pi_data + 1'b1;
    
    //将同步于rdclk时钟的写满标志信号wrfull在rdclk时钟下打两拍
    always@(posedge rdclk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)   
            begin
                wrfull_reg0 <= 1'b0;
                wrfull_reg1 <= 1'b0;
            end
        else
            begin
                wrfull_reg0 <= wrfull;
                wrfull_reg1 <= wrfull_reg0;
            end
    
    //rdreq:FIFO读请求信号同步于rdclk时钟
    always@(posedge rdclk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            rdreq <= 1'b0;
    //如果wrfull信号有效就立刻读,则不会看到rd_full信号拉高,
    //所以此处使用wrfull在rdclk时钟下打两拍后的信号
        else    if(wrfull_reg1 == 1'b1)
            rdreq <= 1'b1;
        else    if(rdempty == 1'b1)//当FIFO中的数据被读空时停止读取FIFO中的数据
            rdreq <= 1'b0;
    
    //------------------------fifo_inst------------------------
    fifo    fifo_inst(
        //FIFO写端
        .wrclk  (wrclk  ),  //input             wclk
        .pi_data(pi_data),  //input     [7:0]   pi_data
        .pi_flag(pi_flag),  //input             pi_flag
        //FIFO读端
        .rdclk  (rdclk  ),  //input             rdclk
        .rdreq  (rdreq  ),  //input             rdreq
    
        //FIFO写端
        .wrempty(wrempty),  //output            wrempty
        .wrfull (wrfull ),  //output            wrfull
        .wrusedw(wrusedw),  //output    [7:0]   wrusedw
        //FIFO读端
        .po_data(po_data),  //output    [15:0]  po_data
        .rdempty(rdempty),  //output            rdempty
        .rdfull (rdfull ),  //output            rdfull
        .rdusedw(rdusedw)   //output    [6:0]   rdusedw
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
  • 相关阅读:
    qlib的工作流管理:mlflow机器学习生命周期管理平台
    Linus shell 在一个脚本中调用另外一个脚本变量
    在ubuntu上搭建nexus私有仓库(指定版本以及jdk!)
    主成分分析笔记
    前端Vue 页面滑动监听 拿到滑动的坐标值
    Python:每日一题之拉马车
    Go语言 接口与类型
    新版本的AndroidStudio生产签名文件打包失败
    SPI协议
    【Servlet】1:踏入JavaWeb的第一把钥匙
  • 原文地址:https://blog.csdn.net/qq_39236499/article/details/127918542