一 、XPM简介
XPM全称是Xilinx Parameterized Macros,是XILINX提供的一种IP参数化方法。传统的IP调用,我们需要进入到IP Catolog里选择自己需要的IP,这种调用方式的优点是比较直观,参数的设置图形化;但是它的弊端也很明显,就是每次修改IP参数都需要进入到图形化界面来修改然后保存。对于一些同质化比较高的项目,比如一个1K的视频图像处理项目,要移植成一个2K的版本;图像的分辨率发生了变化,工程里使用到的参数也会全部发生变化,我们需要把工程里面所有的IP全部重新设置一遍来进行更新,这样效率无疑是非常低的。Xilinx的工程师们考虑到用户的参数化设计需求,为一些常用的基础IP提供参数化模板。
注意这里是说的常用基础IP,那么到底包括哪些IP呢?
我们打开一个工程,点击工具导航栏的Tools --> 选择 Language Templates,这是一个宝库,里面有Xilinx提供的各种设计模板与约束模板,其中就包括XPM.
从上图可以看到XPM主要包括3大类: XPM_CDC(跨时钟处理)、XPM_FIFO、和XPM_MEMORY。这里涉及的子类有点多,关于每一项的具体使用方法,我不一一赘述;大家有需要可以参考官方文档UG953第二章,这里简单的以一个Simple daul port ram举例XPM的使用方法。
二、XPM的使用
这里以XPM_MEMORY中的simple daul port ram为例说明XPM如何使用。
2.1 XPM模板
首先附上language temple提供的语法模板:
// xpm_memory_sdpram: Simple Dual Port RAM
// Xilinx Parameterized Macro, version 2019.2
xpm_memory_sdpram #(
.ADDR_WIDTH_A(6), // DECIMAL
.ADDR_WIDTH_B(6), // DECIMAL
.AUTO_SLEEP_TIME(0), // DECIMAL
.BYTE_WRITE_WIDTH_A(32), // DECIMAL
.CASCADE_HEIGHT(0), // DECIMAL
.CLOCKING_MODE("common_clock"), // String
.ECC_MODE("no_ecc"), // String
.MEMORY_INIT_FILE("none"), // String
.MEMORY_INIT_PARAM("0"), // String
.MEMORY_OPTIMIZATION("true"), // String
.MEMORY_PRIMITIVE("auto"), // String
.MEMORY_SIZE(2048), // DECIMAL
.MESSAGE_CONTROL(0), // DECIMAL
.READ_DATA_WIDTH_B(32), // DECIMAL
.READ_LATENCY_B(2), // DECIMAL
.READ_RESET_VALUE_B("0"), // String
.RST_MODE_A("SYNC"), // String
.RST_MODE_B("SYNC"), // String
.SIM_ASSERT_CHK(0), // DECIMAL;
//0=disable simulation messages, 1=enable simulation messages
.USE_EMBEDDED_CONSTRAINT(0), // DECIMAL
.USE_MEM_INIT(1), // DECIMAL
.WAKEUP_TIME("disable_sleep"), // String
.WRITE_DATA_WIDTH_A(32), // DECIMAL
.WRITE_MODE_B("no_change") // String
)
xpm_memory_sdpram_inst (
.dbiterrb(dbiterrb), // 1-bit output:
// Status signal to indicate double bit error occurrence
// on the data output of port B.
.doutb(doutb), // READ_DATA_WIDTH_B-bit output:
// Data output for port B read operations.
.sbiterrb(sbiterrb), // 1-bit output:
// Status signal to indicate single bit error occurrence
// on the data output of port B.
.addra(addra), // ADDR_WIDTH_A-bit input:
// Address for port A write operations.
.addrb(addrb), // ADDR_WIDTH_B-bit input:
// Address for port B read operations.
.clka(clka), // 1-bit input: Clock signal for port A.
// Also clocks port B when
// parameter CLOCKING_MODE is "common_clock".
.clkb(clkb), // 1-bit input:
// Clock signal for port B when parameter CLOCKING_MODE is
// "independent_clock".
// Unused when parameter CLOCKING_MODE is
// "common_clock".
.dina(dina), // WRITE_DATA_WIDTH_A-bit input:
// Data input for port A write operations.
.ena(ena), // 1-bit input: Memory enable signal for port A.
// Must be high on clock
// cycles when write operations are initiated.
// Pipelined internally.
.enb(enb), // 1-bit input: Memory enable signal for port B.
// Must be high on clock cycles when read operations
// are initiated. Pipelined internally.
.injectdbiterra(injectdbiterra), // 1-bit input: Controls double bit error
// injection on input data when ECC enabled
// (Error injection capability is not available in
// "decode_only" mode).
.injectsbiterra(injectsbiterra), // 1-bit input: Controls single bit error
// injection on input data when ECC enabled
// (Error injection capability is not available in
// "decode_only" mode).
.regceb(regceb), // 1-bit input: Clock Enable for
// the last register stage on the output data path.
.rstb(rstb), // 1-bit input: Reset signal for the final port B
// output register stage.Synchronously resets output
// port doutb to the value specified by parameter ESET_VALUE_B.
.sleep(sleep), // 1-bit input: sleep signal to enable the dynamic power
// saving feature.
.wea(wea) // WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: write enable
// vector for port A input data port dina.
// 1 bit wide when word-wide writes are used.
// In byte-wide write configurations, each bit controls the
// writing one byte of dina to address addra. For example, to
// synchronously write only bits [15-8] of dina when
// WRITE_DATA_WIDTH_A is 32, wea would be 4'b0010.
);
// End of xpm_memory_sdpram_inst instantiation
这个模板主要包括两个部分:参数列表(parameter list) 和 端口列表(port list)。
端口列表的大部分端口信号和我们传统调用IP所生成的端口信号差不多,重点是参数列表,每一个参数代表什么意义,弄懂了XPM的参数列表,我们才能正常使用这一XPM模板。
2.2 parameter list说明
首先来看一下在GUI 界面我们也能看得到的常见参数:
1:地址位宽:ADDR_WIDTH_A/B
这个参数就是表示A/B端口的寻址地址的宽度,比如设置SDP_RAM的深度1024,则地址的取值范围至少是0~1023,那么地址的位宽须 ≥10;
如果AB端口的数据位宽不一致,则AB端口的地址位宽也会不一致,这一点需要注意;
地址位宽的计算建议是通过function函数输入深度直接计算,这样能避免出错。
function integer clogb2 (input integer depth);
begin
for(clogb2=0; depth>0; clogb2=clogb2+1)
depth = depth >> 1;
end
endfunction
localparam C_DPRAM_ADDR_WIDTH = clogb2(C_DPRAM_DEPTH - 1) ;
2、数据位宽WRITE_DATA_WIDTH_A 和 READ_DATA_WIDTH_B
顾名思义这个就是A/B端口的数据位宽,当AB端口需要设置不同位宽时,需要给A/B分别设置不同的值;需要注意的是当ECC功能开启,对位宽的设置有限制,具体可以查阅UG953;
BYTE_WRITE_WIDTH_A:必须是能被WRITE_DATA_WIDTH_A 整除,例如WRITE_DATA_WIDTH_A =32;BYTE_WRITE_WIDTH_A=8;那么写使能wea的位宽就会有4bit,可以通过使能wea的相应bit决定让哪一个byte生效。
3、RAM的大小 MEMORY_SIZE
需要注意的是这个参数的单位是bits,可根据端口的位宽和深度计算;
例如:我们需要调用一个2Kx32bits大小的RAM,则需要设置MEMORY_SIZE:65536。
4、读延时设置:READ_LATENCY_B
下发读地址后,delay多少个clkb输出数据到doutb。例如设置Lantency = 2,其时序如下:
5、读优先 or 写优先 :WRITE_MODE_B
有三种选项:"no _change", "read _first", "write _first";
6、时钟模式
CLOCKING_MODE:"common_clock", "independent_clock" 设置AB端口的时钟是共用时钟还是独立时钟,如果选择“common_clock”,即使B端口接入了自己的时钟,综合工具也会默认使用clka 的时钟。
7、MEMORY类型:MEMORY _PRIMITIVE
有"auto", "block", "distributed", "ultra" 4种选项;
这里需要注意的是:当使能了ECC 或者A/B侧非对称时,这个参数会被强制置为“auto”,想设置“block” 或 “ultra”都无法生效。这里所谓的A/B侧非对称,是指数据位宽和地址位宽不一致。
NOTE: There
may be a behavior mismatch if Block RAM or Ultra RAM specific features, like ECC or Asymmetry, are selected with MEMORY_PRIMITIVE set to "auto".
8、自动优化使能: MEMORY_OPTIMIZATION
“true” : 使能优化,综合工具会根据接入的信号位宽、地址位宽自动优化没有使用的存储单元; “false”: 关闭优化;
9、初始化相关:
USE_MEM_INIT : 当需要初始化时,需要将参数USE_MEM_INIT置“1”,否则初始化不会生效。
MEMORY_INIT_FILE :初始化文件、mem格式、ASCII编码、文件内容只包含十六进制数;传参时只用文件名,不用路径,如果需要使用文件初始化,则将mem文件随设计文件一起加入工程的文件列表。
Specify "none" (including quotes) for no memory nitialization, or specify the name of a memory initialization file- Enter only the name of the file with .mem extension, including quotes but without path (e.g. "my_file.mem"). File format must be ASCII and consist of only hexadecimal values organized into the specified depth by narrowest data width generic value of the memory. See the Memory File (MEM) section for more information on the syntax. Initialization of memory happens through the file name specified only when parameter MEMORY_INIT_PARAM value is equal to "". | When using XPM_MEMORY in a
project, add the specified file to the Vivado project as a design source.
MEMORY_INIT _PARAM:参数初始化,是初始化的另外一种方式,相当于直接把初始化内容写在参数列表,比较笨,一般不这么用,仅限于RAM很小,或者只需要初始化前几个地址时可以考虑使用;
For example, if the narrowest data width is 8, and the depth of memory is 8 locations, then the parameter value should be passed as shown below.
parameter MEMORY_INIT_PARAM = "AB,CD,EF,1,2,34,56,78"
Where "AB" is the 0th location and "78" is the 7thlocation.
其他诸如ECC、和sleep相关设置可以使用默认设置,也可以参考官方文档自己研究研究。
2.3 端口说明
除了sleep 和ECC相关的port ,基本都是常规的data、addr、clk 、rst 和en信号;
如果对于ECC(错误校验)和sleep(睡眠)有需要的童鞋可以参考UG953,我一般都不使能这两项。
三、XPM的封装
首先说说,我们为什么要对它进行封装?
从上面的模板我们可以看到整个XPM的例化代码非常冗长,而且里面很多的参数和端口在用户侧都不需要,如果直接用这个模板进行例化,会导致我们的代码可读性非常差;另一方面,如果涉及到平台切换,比如从6系切换到7系,或者切换到Ultrascale,甚至是切换到其他品牌的FPGA,各个系列/品牌的XPM模板可能是不一样的,如果我们不做封装,那么每一个调用XPM的地方我们都需要回去修改,如果我们将原厂的模板封装成自己的模板,然后引出要用的参数,我们只用修改几个种类的模板即可,可以极大提高移植的效率。
下面附上我自己的一个模板为例:
module xpm_sdpram_wrapper#
(
parameter C_ADDR_WIDTH_A = 'd8 ,
parameter C_DATA_WIDTH_A = 'd8 ,
parameter C_ADDR_WIDTH_B = 'd8 ,
parameter C_DATA_WIDTH_B = 'd8 ,
parameter C_RD_LATENCY = 'd1
)
(
I_clk_a ,
I_ena ,
I_addr_a ,
I_data_a ,
I_clk_b ,
I_rst_b ,
I_enb ,
I_addr_b ,
O_data_b
);
localparam C_DEPTH = 2**C_ADDR_WIDTH_A ;
localparam C_SIZE = C_DEPTH * C_DATA_WIDTH_A ;
input I_clk_a ;
input I_ena ;
input [C_ADDR_WIDTH_A-1:0] I_addr_a ;
input [C_DATA_WIDTH_A-1:0] I_data_a ;
input I_clk_b ;
input I_rst_b ;
input I_enb ;
input [C_ADDR_WIDTH_B-1:0] I_addr_b ;
output[C_DATA_WIDTH_B-1:0] O_data_b ;
xpm_memory_sdpram #(
// A-port-parameter
.RST_MODE_A ("SYNC" ), // "SYNC" or "ASYNC"
.ADDR_WIDTH_A (C_ADDR_WIDTH_A ),
.WRITE_DATA_WIDTH_A (C_DATA_WIDTH_A ),
.BYTE_WRITE_WIDTH_A (C_DATA_WIDTH_A ),
//B-PORT
.RST_MODE_B ("SYNC" ), // "SYNC" or "ASYNC"
.ADDR_WIDTH_B (C_ADDR_WIDTH_B ),
.READ_DATA_WIDTH_B (C_DATA_WIDTH_B ),
.READ_LATENCY_B (C_RD_LATENCY ),
.READ_RESET_VALUE_B ("0" ),
// COMMON
.MEMORY_SIZE (C_SIZE ),
.MESSAGE_CONTROL (0 ),
.SIM_ASSERT_CHK (0 ),
.CASCADE_HEIGHT (0 ),
.CLOCKING_MODE ("common_clock" ),
.ECC_MODE ("no_ecc" ),
.MEMORY_INIT_FILE ("none" ),
.MEMORY_INIT_PARAM ("0" ),
.MEMORY_OPTIMIZATION ("true" ),
.MEMORY_PRIMITIVE ("block" ),
.USE_EMBEDDED_CONSTRAINT(0 ),
.AUTO_SLEEP_TIME (0 ),
.USE_MEM_INIT (1 ),
.WAKEUP_TIME ("disable_sleep" ),
.WRITE_MODE_B ("no_change" )
) U_gen_NHP (
// a-port-io
.clka (I_clk_a ), //
.ena (I_ena ), //
.addra (I_addr_a ), //
.dina (I_data_a ), //
.wea (1'b1 ), //
//b-port-io
.clkb (I_clk_b ), //
.rstb (I_rst_b ), //
.enb (I_enb ), //
.addrb (I_addr_b ), //
.doutb (O_data_b ), //
.dbiterrb ( ), //
.sbiterrb ( ), //
.injectdbiterra (1'b0 ), //
.injectsbiterra (1'b0 ), //
.regceb (1'b0 ), //
.sleep (1'b0 ) //
);
endmodule
参考文档:
UG953(7series)文章来源:https://www.toymoban.com/news/detail-694993.html
UG974(Ultrascale)文章来源地址https://www.toymoban.com/news/detail-694993.html
到了这里,关于XILINX-VIVADO IP参数化方法(XPM)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!