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

news2024/11/29 10:58:51

文章目录

    • 第24讲:快速开发的法宝:IP核
    • 第25讲:PLL-IP核的调用
    • 第26讲:ROM-IP核的调用
      • rom_ctrl.v
      • rom.v
    • 第27讲:RAM-IP核的调用
      • key_filter
      • ram_crtl
      • ram
    • 第28讲:FIFO-IP核的调用
      • 同步FIFO
      • 异步FIFO

第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

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

在这里插入图片描述


第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

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

在这里插入图片描述


第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

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

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

第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

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

异步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

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

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

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

相关文章

Docker的四种网络模式和相关网络命令

一、Docker网络 1.实现原理 docker 使用linux 桥接&#xff0c;在宿主机虚拟一个docker 容器网桥&#xff08;docker0) &#xff0c;docker 启动一个容器时会根据docker 网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker 网桥是每个容器的…

建议别瞎卷,GitHub乱杀,MySQL DBA 攻坚指南一出,阿里数据库专家都解脱了

我先叭叭哈~ 大家可能并不觉得&#xff0c;数据库其实非常重要&#xff0c;每个业内巨头&#xff0c;每个成熟的互联网产品都有多个数据库系统&#xff0c;能保证大量并发场景下不出错&#xff0c;并非易事。尤其是银行、电商、电信、电力、航空等实时交易重要的环境中&#x…

微服务远程调用组件Feign的使用详解

一. 概要 我们知道&#xff0c;现在最火且最有技术含量的技术莫过于SpringCloud微服务了&#xff0c;所以今天百泽老师就带大家来学习一下微服务的核心的组件之一&#xff0c;Feign的基本使用及其工作机制。 二. Feign简介 1.概念 在学习Feign的使用之前&#xff0c;我们先…

贝赛尔曲线

<template><svg width"400" height"400" class"BG" mousemove"mousemove"><!-- 这样拼字符串&#xff0c;少些点加号&#xff0c;方便改一些 --><path class"Line":d"M${StartPoint[0]} ${StartP…

CNN经典模型之ALexNet、ResNet、DenseNet总结和比较

目录 ALexNet(2012 研究背景 思路和主要过程 网络模型 数据增强 主要贡献点 ResNet(2015 研究背景 思路和主要过程 Residual block(残差块)和shortcut connections(捷径连接) bottleneck block-瓶颈模块 主要贡献点: Denset(2017 研究背景 思路和主要过程 Dense…

mysql忘记密码怎么解决(几乎囊括你可能遇到的所有问题)

mysql忘记密码解决&#xff1a; 多次输入都错误&#xff0c;出现下面的提示 Access denied for user rootlocalhost (using password: YES)解决&#xff1a; 1.关闭mysql service mysqld stop2.mysql停止后输入 mysqld --shared-memory --skip-grant-tables3.重新打开一个…

利用Pinpoint搭建全链路监控系统

随着项目微服务的进行&#xff0c;微服务数量逐渐增加&#xff0c;服务间的调用也越来越复杂&#xff0c;我们急切需要一个APM工具帮我们监控各个服务的性能及对服务间的调用进行跟踪&#xff0c;而通过调研多个开源APM工具后&#xff0c;最终我们选择了Pinpoint。 简介 Pinp…

PyQt5 QDialog对话框(QMessageBox,QInputDialog,QFontDialog,QFileDialog,QColorDialog)

PyQt5 QDialog对话框QDialog类图QDialogQMessageBoxQInputDialogQFontDialogQFileDialogQColorDialogQDialog类图 QDialog import sys from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtCore import Qtclass MyDialogWindow…

数据结构之插入排序

插入排序 一种思想我们所在指向位是需要进行插入的数据 我们锁指向位置前的数据是被插入的数据组 我们根据与前面的数据元素一个个比较来确定插入位置的排序 比如现在的76比前面的97小&#xff0c;97进行一个右移&#xff0c;就是赋值给data[4]&#xff08;我这里猜测是76应该是…

QT 介绍

QT 介绍 Qt是一个跨平台的 C 开发库&#xff0c;主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;程序&#xff0c;当然也可以开发不带界面的命令行&#xff08;Command User Interface&#xff0c;CUI&#xff09;程序。且完全面向对…

项目实战——匹配系统(上)

ps&#xff1a;从这篇文章开始&#xff0c;整个项目最精华&#xff08;困难&#xff09;的部分就要来了&#xff0c;因此这里会把每一个步骤细分&#xff0c;并且说一下自己对于每个步骤的思考和理解&#xff08;博主水平有限&#xff0c;错误在所难免&#xff0c;欢迎指出讨论…

MyBatis-Plus(第一篇开山篇)

MyBatis-Plus&#xff08;第一篇开山篇&#xff09; 目录MyBatis-Plus&#xff08;第一篇开山篇&#xff09;结论&#xff1a;使用mybatis plus步骤1.导包2.整合(修改spring-dao配置文件)3.使用3.1 javaBean3.2 mapper层3.3 service层3.3.1 service接口3.3.2 service实现类3.4 …

【MEF:深度感知增强(细节增强:色彩映增强)】

Multi-exposure image fusion via deep perceptual enhancement &#xff08;基于深度感知增强的多曝光图像融合&#xff09; 多曝光图像融合 (MEF) 是通过将具有不同曝光的多个镜头集成在一起来解决此问题的有效方法&#xff0c;这在本质上是一个增强问题。在融合过程中&…

IDEA最实用的设置

​ 目录 全局设置 全局设置 选择 Configure->settings进入设置界面 1.设置主题 2.设置编码区的字体及大小 3.设置控制台的字体及大小 4.修改编码方式 解决有时我们在导入项目时&#xff0c;代码中的中文注释变成乱码问题。 5.设置按住Ctrl鼠标滚轮改变字体大小 6.设置…

Java基础深化和提高 ---- 函数式编程 Lambda

Lambda表达式介绍 Lambda简介 Lambda 表达式是 JDK8 的一个新特性&#xff0c;可以取代大部分的匿名内 部类&#xff0c;写出更优雅的 Java 代码&#xff0c;尤其在集合的遍历和其他集合操作 中&#xff0c;可以极大地优化代码结构。 在Java语言中&#xff0c;可以为变量赋予一…

计算机网络-传输层(TCP可靠传输(校验,序号,确认,重传),TCP流量控制,TCP拥塞控制(慢开始,拥塞避免)(快重传,快恢复))

文章目录1. TCP可靠传输2. TCP流量控制3. TCP拥塞控制1. TCP可靠传输 网络层&#xff1a;提供尽最大努力交付&#xff0c;不可靠传输。 传输层&#xff1a;使用TCP实现可靠传输 可靠&#xff1a;保证接收方进程从缓存区读出的字节流与发送方发出的字节流是完全一样的。 TCP可…

redis 哨兵

哨兵可以帮助我们解决主从架构中的单点故障问题 哨兵的配置&#xff1a; 新建目录&#xff1a;usr/local/docker/redis/sen/ docker-compose.yml文件如下 version: "3.1" services:redis1:image: daocloud.io/library/redis:5.0.7restart: alwayscontainer_name: …

STL技巧大赏

STL技巧大赏 map insertinsertinsert 不允许有重复的键值的&#xff0c;如果新插入的键值与原有的键值重复则插入无效beginbeginbeginendendendclearclearclear 清除所有countcountcount 某个元素出现次数lower/upperboundlower/upper boundlower/upperbound set 比 mapmapm…

半导体新能源智能装备上位机工业软件设计方案

一、什么是上位机软件 如果说PLC是工业控制的小脑&#xff0c;那么上位机软件就是其大脑。在概念上&#xff0c;控制者和提供服务者是上位机&#xff0c;被控制者和被服务者是下位机&#xff0c;上位机往往是数字信号的处理和命令的下发&#xff0c;下位机往往是模拟量的处理和…

【Linux】初识系统调用进程状态

文章目录1. 什么是系统调用1.1 通过系统调用获取进程标示符通过其他方式查看PIDTopPS使用PID1.2 通过系统调用创建进程-fork初识2. 进程状态看看Linux内核源代码怎么定义查看状态Z(zombie)-僵尸进程僵尸进程危害孤儿进程1. 什么是系统调用 在linux中&#xff0c;系统调用是指操…