Vivado 开发套件中,Vivado综合能够综合多种类型的属性,大多数情况下,这些属性的使用语法和行为都一样。当使用综合属性时,假如Vivado能够识别该属性,那么就使用这个属性并创建反映已经使用该属性的逻辑;Vivado也可能无法识别所给的属性,这时Vivado就综合器就会将属性及其值传递给生成的网表文件。
下面介绍Viado开发工具支持的综合属性。
1.async_reg (用在跨异步时钟域场合)
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";
2.BLACK_BOX
存储相关的综合 属性
- RAM_STYLE
指示综合工具如何实现一个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 示例
HDL Coding Example
Distributed RAM Examples
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];
endmodule
Simple 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内容的初始化方式
有一下两种对RAM进行初始化的方式:
(1)在HDL代码中指定RAM的初始值;
(2) 靠外部数据文件来指定RAM的初始值。
(1)HDL中指定
依靠信号的默认值机制直接在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
(2)外部数据文件指定RAM初始值
使用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%60;
这就给出一个范围在-59~59之间的随机数。下面例子通过拼接操作产生一个0~59的数。
reg[23:0] rand;
rand = {$random}%60;