赞
踩
XPM全称是Xilinx Parameterized Macros,也就是Xilinx的参数化的宏,跟原语的例化和使用方式相同;
本文有问题的地方请大佬们评论区指正
XPM主要可以用于三种功能模块的例化,包含:跨时钟域、FIFO和RAM;
Xilinx的使用介绍中写到
//
// The following instructions describe how to prepare Vivado to use the XPM libraries:
// 1. Ensure Vivado can identify the XPMs:
// Flow 1: Using the Vivado IDE and/or Project Flow
// When using the IDE and/or the project flow, you do not need to take any additional steps.
// The tools will parse the files added to the project and setup Vivado to recognize the XPMs
// Flow 2: Using the non-Project based flow
// When using the non-project flow, the following command must be issued:
// auto_detect_xpm
//
// 2. Select the XPM template that you wish to use from below
// 3. Copy the contents of the template and paste into your own source file.
// Set parameters/generics, and wire ports according to the documentation provided as code comments.
//
// Note: Be sure to read and comply with all code comments to properly use the XPMs.
//
笔者认为XPM可以比作不需要Generate Output Products的IP核,只需将Language Templates中的语法复制粘贴即可使用;
文档
7系列FPGA和Zynq 7000 SoC UG953
UltraScale和UltraScales UG974
这个宏用来实例化和区分异步FIFO;
注意:在异步FIFO(即两个独立的时钟)中,只有当wr_clk和rd_clk从同一源生成时,RELATED_CLOCKS属性才能设置为TRUE。
复位
在用户使能复位信号后,用户应该等到busy信号变低后再使能另一次复位;
可以看到,在复位信号RST
开始有效的下一个时钟周期,WR_RST_BUSY
信号拉高;
FULL
、PROG_FULL
和ALMOST_FULL
信号在WR_RST_BUSY
信号拉高后的下一个时钟周期上升沿拉高,写使能WR_EN
在复位有效时已经拉低;
而在复位RST
结束后的若干个时钟周期才正式表示复位结束,此时WR_RST_BUSY
、FULL
、PROG_FULL
和ALMOST_FULL
信号在复位结束后拉低,写使能WR_EN
复位结束的下一个时钟周期可以正常工作;
异步FIFO读写时钟的时钟频率并不一定相同,同步复位情况下,读时钟在复位结束后的下一个时钟周期上升沿RD_RST_BUSY
拉高;
再之后的下一个时钟周期EMPTY
、PROG_EMPTY
和ALMOST_EMPTY
信号拉高,读使能在读复位忙信号RD_RST_BUSY
拉高的那一个时钟周期上升沿发生变化;
读使能拉高的下一个时钟周期输出数据,所以DOUT
在下一个时钟周期依然有数据;
在写复位wr_rst_busy
拉低后的下一个时钟周期读数据可以正常工作,可以继续拉高读使能输出数据;
标准写操作
可以发现,在写使能拉高的下一个时钟周期,写计数wr_data_count
并未发生变化,而是再等一个时钟周期之后开始计数;
而深度为16的fifo最多可以写入16个数据,写计数wr_data_count
最高为15,并且满信号full
在写入第15个数据之后的那个时钟周期拉高,代表fifo满;
而将满信号水线prog_full
为6的时候,计数wr_data_count
为5的时候下一拍就已经拉高,此时fifo内的数据已经为6;
满信号full
拉高后的下一个时钟周期,表示fifo溢出的信号overflow
拉高,直到读出数据或者写使能拉低;
标准读操作
FIFO_READ_LATENCY
表示读出的数据经过几级寄存器缓存也就是几个时钟周期输出;
读使能拉高的下一个时钟周期输出数据,但是读计数rd_data_count
会在出数据的下一个时钟周期改变,出完最后一个数据的下一个时钟周期计数rd_data_count
置零,输出到最后一个数据的那个时钟周期empty拉高;其他信号同之前的分析;
写操作
读操作
预读模式下,读使能拉高时数据就有效,出数据快;
注意:对于LUTRAM,FIFO_READ_LATENCY
=0和READ_MODE="std"
的auto、mixed和分布式RAM等基本类型将推断为是没有rd_clk的LUTRAM内存。在这种配置中,dout端口将仅相对于写入时钟的posedge进行切换。因为这条路径下的数据是直接从LUT中驱动出来的,没有任何rd_clk域触发器。
Note: For LUTRAM, auto, mixed, and distributed RAM primitive types with FIFO_READ_LATENCY = 0 and READ_MODE = “std” will infer LUTRAM memory without a rd_clk. In this configuration, the dout port will toggle with respect to the posedge of the write clock only. Take care of this path as data is driven out of the LUT directly without any rd_clk domain flop.
写入操作导致写入端口标志更新延迟
读取操作而导致的读取端口标志更新延迟
读取操作而导致的写入端口标志更新延迟,N是同步级的数量
写入操作而导致的读取端口标志更新延迟,N是同步级的数量
写入操作导致的读端口标志更新延迟
写入操作导致的写入端口标志更新延迟
读取操作而导致的读取端口标志更新延迟
读取操作而导致的写入端口标志更新延迟,N是同步级的数量
写入操作而导致的读取端口标志更新延迟,N是同步级的数量
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/06/14 11:26:34
// Design Name:
// Module Name: fifo_sync
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module fifo_async(
input rst,
input wr_clk,
input rd_clk,
input wr_en,
input [31:0] din,
input rd_en,
output [31:0] dout,
output data_valid );
// xpm_fifo_async: Asynchronous FIFO
// Xilinx Parameterized Macro, version 2022.1
wire almost_empty;
wire almost_full;
wire prog_full;
wire prog_empty;
wire wr_rst_busy;
wire rd_rst_busy;
wire empty;
wire full;
wire [6:0] wr_count;
wire [6:0] rd_count;
wire sleep = 1'b0;
wire wr_ack;
wire underflow;
wire overflow;
xpm_fifo_async #(
.CASCADE_HEIGHT(0), // DECIMALram最大级联数
.CDC_SYNC_STAGES(2), // DECIMAL异步时钟时候读时钟域读计数count开始有值的时间
.DOUT_RESET_VALUE("0"), // String 复位时候读通道出的数据
.ECC_MODE("no_ecc"), // String 是不是开启纠错检测,auto时候只能选no_ecc
.FIFO_MEMORY_TYPE("auto"), // String dram还是bram
// 但是当使能了ECC 或者A/B侧非对称时,这个参数会被强制置为“auto”,想设置“block” 或 “ultra”都无法生效。
// 这里所谓的A/B侧非对称,是指数据位宽和地址位宽不一致。
.FIFO_READ_LATENCY(1), // DECIMAL输出打几拍,fwft模式只能为0
.FIFO_WRITE_DEPTH(128), // DECIMALfifo深度 = FIFO_WRITE_DEPTH*WRITE_DATA_WIDTH/READ_DATA_WIDTH
.FULL_RESET_VALUE(0), // DECIMAL复位时候full almost_full prog_full的默认值
.PROG_EMPTY_THRESH(10), // DECIMAL手动线Min_Value = 3 + (READ_MODE_VAL*2*(FIFO_WRITE_DEPTH/FIFO_READ_DEPTH))+CDC_SYNC_STAGES
.PROG_FULL_THRESH(100), // DECIMAL手动线Max_Value = (FIFO_WRITE_DEPTH-3) - (READ_MODE_VAL*2*(FIFO_WRITE_DEPTH/FIFO_READ_DEPTH))
.RD_DATA_COUNT_WIDTH(7), // DECIMAL读计数的宽度 = log2(FIFO_READ_DEPTH)+1
.READ_DATA_WIDTH(32), // DECIMAL读数据的位宽 读写位宽可以不一致 但是必须有关
.READ_MODE("std"), // String 标准读模式还是预读模式,标准读模式就是下一拍有效,预读模式就是当拍有效
.RELATED_CLOCKS(0), // DECIMAL同源时钟,频率不同
.SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
.USE_ADV_FEATURES("1f1f"), // String 很多有效位的使能0000_0111_0000_0111 wr_ack写应答 overflow写溢出 underflow读溢出
// Enables data_valid, almost_empty, rd_data_count, prog_empty, underflow, wr_ack, almost_full, wr_data_count
.WAKEUP_TIME(0), // DECIMAL使不使用sleep 使用sleep就是节能模式
.WRITE_DATA_WIDTH(32), // DECIMAL写数据宽度 auto是必须读写一致位宽
.WR_DATA_COUNT_WIDTH(7) // DECIMAL写数据的位宽
)
xpm_fifo_async_inst (
.almost_empty(almost_empty),
.almost_full(almost_full),
.data_valid(data_valid),
.dout(dout),
.empty(empty),
.full(full),
.overflow(overflow),
.prog_empty(prog_empty),
.prog_full(prog_full),
.rd_data_count(rd_count),
.rd_rst_busy(rd_rst_busy),
.underflow(underflow),
.wr_ack(wr_ack),
.wr_data_count(wr_count),
.wr_rst_busy(wr_rst_busy),
.din(din),
.rd_clk(rd_clk),
.rd_en(rd_en),
.rst(rst),
.sleep(sleep),
.wr_clk(wr_clk),
.wr_en(wr_en)
);
// End of xpm_fifo_async_inst instantiation
endmodule
写个TB仿真如下
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/06/24 15:22:03
// Design Name:
// Module Name: tb_sim_async
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module tb_sim_async();
reg rst ;
reg wr_clk ;
reg rd_clk ;
reg wr_en ;
reg [31:0] din ;
reg rd_en ;
wire [31:0] dout ;
wire data_valid ;
reg [31:0] cnt;
genvar i;
initial begin
wr_clk = 'b0;
rd_clk = 'b0;
end
always#10 rd_clk = ~rd_clk;
always#12.5 wr_clk = ~wr_clk;
initial begin
rst = 1'b0;
wr_en = 1'b0;
rd_en = 1'b0;
din = 32'd0;
cnt = 32'd300;
#10;
rst = 1'b1;
wr_en = 1'b0;
rd_en = 1'b0;
#100;
rst = 1'b0;
wr_en = 1'b0;
rd_en = 1'b0;
#5000;
wr_en = 1'b0;
rd_en = 1'b0;
#250;
repeat(30)begin
@(posedge wr_clk)begin
wr_en = 1'b1;
cnt = cnt - 32'd1;
din = cnt;
end
end
repeat(50)begin
@(posedge wr_clk)begin
rd_en = 1'b1;
cnt = cnt - 32'd1;
din = cnt;
end
end
repeat(50)begin
@(posedge wr_clk)begin
rd_en = 1'b0;
cnt = cnt - 32'd1;
din = cnt;
end
end
wr_en = 1'b0;
#900;
rd_en = 1'b1;
#2000;
rd_en = 1'b0;
#2000;
$stop;
end
fifo_async u_fifo_async(
.rst (rst ),
.wr_clk (wr_clk ),
.rd_clk (rd_clk ),
.wr_en (wr_en ),
.din (din ),
.rd_en (rd_en ),
.dout (dout ),
.data_valid (data_valid )
);
endmodule
仿真结果如下,可以发现wr_count
在延迟两拍后开始拉高;复位刚结束两个busy信号均处于拉高状态,此时尽量不对active的wr_rst_busy
做写操作,不对active的rd_rst_busy
做读操作;
读使能下一拍出数据,此处是正常模式;
改为FWFT预读模式之后,与正常模式std下最大的区别就是,data_valid
信号会在rd_count
前一个时钟周期开始拉高,一直拉高到rd_count
为零时data_valid
拉低,而std
模式是跟着读使能的数据有效的;
详细如下:
同步fifo要比异步fifo简单一些,基本上所有的接口都能在异步fifo中找到,在此接口和时序方面我们不再赘述,直接上源程序和仿真;
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/06/25 20:01:40
// Design Name:
// Module Name: fifo_sync_real
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module fifo_sync_real(
input rst,
input wr_clk,
input wr_en,
input [31:0] din,
input rd_en,
output [31:0] dout,
output data_valid
);
wire almost_empty;
wire almost_full;
wire prog_full;
wire prog_empty;
wire wr_rst_busy;
wire rd_rst_busy;
wire empty;
wire full;
wire [6:0] wr_count;
wire [6:0] rd_count;
wire sleep = 1'b0;
wire wr_ack;
wire underflow;
wire overflow;
// | In standard READ_MODE, the effective depth = FIFO_WRITE_DEPTH
// | In First-Word-Fall-Through READ_MODE, the effective depth = FIFO_WRITE_DEPTH+2
xpm_fifo_sync #(
.CASCADE_HEIGHT(0), // DECIMAL
.DOUT_RESET_VALUE("0"), // String
.ECC_MODE("no_ecc"), // String
.FIFO_MEMORY_TYPE("auto"), // String
.FIFO_READ_LATENCY(1), // DECIMAL
.FIFO_WRITE_DEPTH(128), // DECIMAL
.FULL_RESET_VALUE(0), // DECIMAL
.PROG_EMPTY_THRESH(10), // DECIMAL
.PROG_FULL_THRESH(100), // DECIMAL
.RD_DATA_COUNT_WIDTH(7), // DECIMAL
.READ_DATA_WIDTH(32), // DECIMAL
.READ_MODE("std"), // String
.SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
.USE_ADV_FEATURES("1f1f"), // String
.WAKEUP_TIME(0), // DECIMAL
.WRITE_DATA_WIDTH(32), // DECIMAL
.WR_DATA_COUNT_WIDTH(7) // DECIMAL
)
xpm_fifo_sync_inst (
.almost_empty(almost_empty),
.almost_full(almost_full),
.data_valid(data_valid),
.dout(dout),
.empty(empty),
.full(full),
.overflow(overflow),
.prog_empty(prog_empty),
.prog_full(prog_full),
.rd_data_count(rd_data_count),
.rd_rst_busy(rd_rst_busy),
.underflow(underflow),
.wr_ack(wr_ack),
.wr_data_count(wr_data_count),
.wr_rst_busy(wr_rst_busy),
.din(din),
.rd_en(rd_en),
.rst(rst),
.sleep(sleep),
.wr_clk(wr_clk),
.wr_en(wr_en)
);
endmodule
std
模式下,可以发现,数据在读使能拉高后的下一个时钟周期出,同时读计数rd_count
和写计数wr_count
同时变化;
在最后一个数据读出的那一个时钟周期,empty信号就拉高,如果下一个时钟周期读使能依然拉高,那么就会触发underflow
;
在FWFT模式下,可以发现,存入数据有效的那个时钟周期data_valid就开始有效,一直等到fifo里的数据全部输出完毕;
其他信号均与之前相同,在此不多赘述;
其他的下次再更~~未完待续,有问题请评论区指正
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。