文章目录
- 第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