Vivado 开发套件中,Vivado综合能够综合多种类型的属性,大多数情况下,这些属性的使用语法和行为都一样。当使用综合属性时,假如Vivado能够识别该属性,那么就使用这个属性并创建反映已经使用该属性的逻辑;Vivado也可能无法识别所给的属性,这时Vivado就综合器就会将属性及其值传递给生成的网表文件。
下面介绍Viado开发工具支持的综合属性。
FPGA设计中经常会遇到跨时钟域问题,在跨时钟域场合,对于控制信号而言(通常都1bit的,_en,flag,hsync...)一般通过打两拍的方法实现跨时钟域操作(由两级触发器实现的“一位同步器”)。如下图所示。
此时图中标记为1的触发器需要使用综合属性:async_reg。使用该属性有两个目的:
(1)告诉综合器,1号的触发器能够将接收来自异步时钟域的数据(即数据与接本地采样时钟不同步);
(2)同时也说明了2号触发器是同步链路上的触发器。
当遇到此属性的时候,Vivado综合器就会将其视为DONT_TOUCH属性,并在网表中向前推送ASYNC_REG属性。后续的流程中,布局布线的工具也会收到该属性正确处理, 在后面布局的时候就能保证1号和2号触发器被放置到同一个SLICE中,可以减少线延时对时序的影响。
假如没有这个属性,综合器很可能就把它们给优化掉,并且在后续的流程中也无法正确处理了。
这个属性可以用在RTL和XDC文件中。
HDL示例:
//Verilog 用法 (*ASYNC_REG = "TRUE" *) reg [2:0] sync_regs; //VHDL用法 attribute ASYNC_REG: string; attribute ASYNC_REG of synv_regs : siganl is "TRUE";
指示综合工具如何实现一个RAM存储器,可设置为:
block(使用BRAM即块RAM来实现);
distributed(使用LUT搭建分布式RAM);
registers(使用寄存器组来替代RAM)或ultra(使用UltraScale中的URAM)。
默认情况下工具会为了得到最好的设计效果而自动选择。如果该属性在定义RAM的信号处申明,则仅作用于该信号;如果在某一层次结构处申明,将作用于该层次中的所有RAM(但不会影响到该层次的子层次)。可以在RTL或XDC中设置,示例如下:
(* ram_style = “distributed” *) reg [size-1:0] myram [2**addr-1:0]; //Verilog示例 attribute ram_style : string; attribute ram_style of myram : signal is "distributed"; //VHDL示例
RAM_DECOMP
该属性用于指示综合工具如何用块RAM(BRAM)来实现一个较大的RAM。比如需要一个2K*36的RAM,通常会用两个2K18的BRAM组合实现(为了提高设计速度)。
如果将该属性设置为power,则会用两个1K36的BRAM来组合实现,这样在读写过程中,使用地址使只需要一个BRAM处于活跃状态,因此可以降低功耗。
该属性只有一个可配置值即power,虽然可以降低功耗,但是会增加地址解码的时间。可以在RTL或XDC中设置,示例如下:
(* ram_decomp = “power” *) reg [size-1:0] myram [2**addr-1:0]; //Verilog示例 set_property ram_decomp power [get_cells myram] #XDC示例 ROM_STYLE指示综合工具如何推断一个ROM存储器,可设置为block(使用BRAM即块RAM来实现)或distributed(使用LUT搭建分布式ROM),默认情况下工具会为了得到最好的设计效果而自动选择。可以在RTL或XDC中设置,示例如下:
(* rom_style = “distributed” *) reg [data_size-1:0] myrom [2**addr-1:0]; //Verilog示例 attribute rom_style : string; attribute rom_style of myrom : signal is “distributed”;//VHDL 示例Dual-Port RAM with Asynchronous Read Coding Example (Verilog)
// Dual-Port RAM with Asynchronous Read (Distributed RAM) // File: rams_dist.v module rams_dist (clk, we, a, dpra, di, spo, dpo); input clk; input we; input [5:0] a; input [5:0] dpra; input [15:0] di; output [15:0] spo; output [15:0] dpo; reg[15:0] ram [63:0]; always @(posedge clk) begin if (we) ram[a] <= di; end assign spo = ram[a]; assign dpo = ram[dpra]; endmoduleSimple Dual-Port Block RAM with Single Clock (VHDL)
-- Simple Dual-Port Block RAM with One Clock -- Correct Modelization with a Shared Variable -- File:simple_dual_one_clock.vhd library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; entity simple_dual_one_clock is port( clk : in std_logic; ena : in std_logic; enb : in std_logic; wea : in std_logic; addra: in std_logic_vector(9 downto 0); addrb: in std_logic_vector(9 downto 0); dia : in std_logic_vector(15 downto 0); dob : in std_logic_vector(15 downto 0) ); end simple_dual_one_clock; architecture syn of simple_dual_one_clock is type ram_type is array (1023 downto 0) of std_logic_vector(15 downto 0); shared variable RAM : ram_type; begin process(clk) begin if clk'event and clk = '1' then if ena = '1' then if wea = '1' then RAM(conv_integer(addra)) := dia; end if end if end if end process; process(clk) begin if clk'event and clk = '1' then if enb = '1' then dob <= RAM(conv_integer(addrb)); end if end if end process; end syn;Simple Dual-Port Block RAM with Dual Clocks (Verilog)
// Simple Dual-Port Block RAM with Two Clocks // File: simple_dual_two_clocks.v module simple_dual_two_clocks (clka,clkb,ena,enb,wea,addra,addrb,dia,dob); input clka,clkb,ena,enb,wea; input [9:0] addra,addrb; input [15:0] dia; output [15:0] dob; reg[15:0] ram [1023:0]; reg[15:0] dob; always @(posedge clka) begin if(ena == 1'b1) begin if(wea == 1'b1) ram(addra) <= dia; end end always@(posedge clkb) begin if(enb) begin dob <= ram(addrb); end end end module
更多示例参见ug901 chapter3 HDL coding Techniques
有一下两种对RAM进行初始化的方式:
(1)在HDL代码中指定RAM的初始值;
(2) 靠外部数据文件来指定RAM的初始值。
依靠信号的默认值机制直接在HDL源代码中描述初始RAM内容。
VHDL编码
type ram_type is array (0 to 31) of std_logic_vector(19 downto 0); signal RAM : ram_type := ( X”0200A”, X”00300”, X”08101”, X”04000”, X”08601”, X”0233A”, X”00300”, X”08602”, X”02310”, X”0203B”, X”08300”, X”04002”, X”08201”, X”00500”, X”04001”, X”02500”, X”00340”, X”00241”, X”04002”, X”08300”, X”08201”, X”00500”, X”08101”, X”00602”, X”04003”, X”0241E”, X”00301”, X”00102”, X”02122”, X”02021”, X”0030D”, X"08201" );也可将RAM中所有bit位置都初始化为同一个值:
type ram_type is array (0 to 127) of std_logic_vector (15 downto 0); signal RAM : ram_type := (others => (others => '0'));Verilog Coding Examples :
所有可寻址的字都被初始化为相同值:
reg [DATA_WIDTH-1:0] ram [DEPTH-1:0]; integer i; initial for (i=0; i<DEPTH; i=i+1) ram[i] = 0; end使用HDL原码中的文件读写功能来从外部文件中下载数据到RAM中。
•外部数据文件可以是任何名称的ASCII文本文件。 •外部数据文件中的每一行描述RAM中地址位置的初始内容。 •外部数据文件中的行必须与RAM阵列中的行一样多。 标记的行数不足。 •与给定行相关的可寻址位置由建模RAM的信号的主要范围的方向定义。 •您可以用二进制或十六进制表示RAM内容。 你不能混合两者。 •外部数据文件不能包含任何其他内容,例如注释。 •以下外部数据文件使用二进制值初始化8 x 32位RAM:
Verilog Example
使用系统任务$readmemb或$readmemh来分别下载二进制和16进制格式的数据。
使用格式共有6种
$readmemb("<数据文件名>",<存储器名>);$readmemb("<数据文件名>",<存储器名>,<起始地址>);$readmemb("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);$readmemh("<数据文件名>",<存储器名>);$readmemh("<数据文件名>",<存储器名>,<起始地址>);$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>); reg [31:0] ram [0:63]; initial begin $readmemb(“rams_20c.data”, ram, 0, 63); end顺便介绍一下系统任务$random
这个任务提供了一个产生随机数的手段。当函数被调用时会返回一个32bit的随机数,并且是一个有符号的整型数。
一般用法:
$random %b,其中b>0。这样结果就能返回一个范围在(-b+1):(b-1)中的随机数。例如
reg[23:0] rand; rand = $random`;这就给出一个范围在-59~59之间的随机数。下面例子通过拼接操作产生一个0~59的数。
reg[23:0] rand; rand = {$random}`;