引言
本文记录一些用于 Vivado 综合约束的实用命令,欢迎补充~
本文会适当结合一些特定设计进行解释,并结合相关工程进行具体的综合实现分析,不只是理论知识还有实际操作。
演示使用的Vivado 版本:2018.3
FPGA芯片型号:xc7a35tfgg484-2
本篇博文,建议在电脑端网页/pad上查看~
综合阶段
综合设置
综合设置的打开方式:
注意:凡是出现在综合设置区的设置均为 全局设置 ,即对设计工程中的所有模块都有效。
-flatten_hierarchy
解释说明
对于此设置项,Vivado给出 3 个可选项:full、none、rebuilt(默认)
那么此设置选项是什么意思呢?
flatten:打平、压平的意思
hierarchy:层次化的意思
连起来,此设置项表示将工程中的设计模块之间的层次打平(主要在LUT映射期间),是不同模块之间的层次化边界变得模糊,这有利于工具对我们写的RTL代码进行最大程度的优化。
对于此设置项的不同取值,在这里做简单解释:
取值 | 含义 |
full | 将原始设计打平,只保留顶层设计,执行边界优化 |
none | 完全保留原始设计的层次,不执行边界优化 |
rebuilt | 将原始设计打平,执行边界优化,将网表文件按照原始设计层次显示,故与原始设计层次相同 |
如果希望某个模块的层次优化策略与 -flatten_hierarchy中选择的值不同,可以使用综合属性中的 KEEP_HIERARCHY ,其仅可在RTL代码中使用,优先级高于 -flatten_hierarchy 中的设置值。KEEP_HIERARCHY 指保留层级结构,不被优化。
KEEP_HIERARCHY 的 使用方法(Verilog HDL ,示例):
(* KEEP_HIERARCHY = "yes" *) module UART_TX_MDL( );
🔶对于-flatten_hierarchy ,通常保持默认值 rebuilt 即可。使用 rebuilt 的益处在于,使用内嵌的逻辑分析仪(ILA时),可以快速地根据层级找到待观测信号。
🔷KEEP_HIERARCHY 的优先级高于 -flatten_hierarchy ,可根据需要对某些层次设置此属性。
设计实践
此处我想用一个运算模块来试验-flatten_hierarchy 在不同设置特性下综合的结果,同时也验证 KEEP_HIERARCHY 的设置功能。
顶层模块实现:Y = (A + B) * C - D;其中操作数 A B C D 均为有符号 16位数据;
设计源码
加法模块
// | ============================== 有符号加法运算模块 ==============================
// | 作者:Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 时间:2023-01-05
// | 功能:实现同位宽的两个有符号数相加
// | ================================================================================
`timescale 1ns / 1ps
module SIGNED_ADD_MDL #(
// 模块参数声明
parameter P_OPR_DATA_WIDTH = 32'd16
)(
// 输入输出端口声明
input I_OPR_CLK,
input I_OPR_RSTN,
input [P_OPR_DATA_WIDTH-1:0] I_OPR_DATA_A,
input [P_OPR_DATA_WIDTH-1:0] I_OPR_DATA_B,
input I_OPR_DATA_VAL,
output reg [P_OPR_DATA_WIDTH:0] O_ADD_RES,
output reg O_ADD_RES_VAL
);
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
O_ADD_RES <= {(P_OPR_DATA_WIDTH+1){1'b0}};
O_ADD_RES_VAL <= 1'b0;
end
else
begin
if(I_OPR_DATA_VAL)
begin
O_ADD_RES <= {I_OPR_DATA_A[P_OPR_DATA_WIDTH-1],I_OPR_DATA_A} + {I_OPR_DATA_B[P_OPR_DATA_WIDTH-1],I_OPR_DATA_B};
O_ADD_RES_VAL <= I_OPR_DATA_VAL;
end
else
begin
O_ADD_RES <= {(P_OPR_DATA_WIDTH+1){1'b0}};
O_ADD_RES_VAL <= 1'b0;
end
end
end
endmodule
减法模块
// | ============================== 有符号减法运算模块 ==============================
// | 作者:Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 时间:2023-01-05
// | 功能:实现同位宽的两个有符号数相减
// | ================================================================================
`timescale 1ns / 1ps
module SIGNED_SUB_MDL #(
// 模块参数声明
parameter P_OPR_DATA_WIDTH = 32'd16
)(
// 输入输出端口声明
input I_OPR_CLK,
input I_OPR_RSTN,
input [P_OPR_DATA_WIDTH-1:0] I_OPR_DATA_A,
input [P_OPR_DATA_WIDTH-1:0] I_OPR_DATA_B,
input I_OPR_DATA_VAL,
output reg [P_OPR_DATA_WIDTH:0] O_SUB_RES,
output reg O_SUB_RES_VAL
);
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
O_SUB_RES <= {(P_OPR_DATA_WIDTH+1){1'b0}};
O_SUB_RES_VAL <= 1'b0;
end
else
begin
if(I_OPR_DATA_VAL)
begin
O_SUB_RES <= {I_OPR_DATA_A[P_OPR_DATA_WIDTH-1],I_OPR_DATA_A} - {I_OPR_DATA_B[P_OPR_DATA_WIDTH-1],I_OPR_DATA_B};
O_SUB_RES_VAL <= I_OPR_DATA_VAL;
end
else
begin
O_SUB_RES <= {(P_OPR_DATA_WIDTH+1){1'b0}};
O_SUB_RES_VAL <= 1'b0;
end
end
end
endmodule
乘法模块
// | ============================== 有符号乘法运算模块 ==============================
// | 作者:Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 时间:2023-01-05
// | 功能:实现不同位宽的两个有符号数相乘
// | ================================================================================
`timescale 1ns / 1ps
module SIGNED_MULT_MDL #(
// 模块参数声明
parameter P_OPR_DATA_WIDTH_A = 32'd16,
parameter P_OPR_DATA_WIDTH_B = 32'd16
)(
// 输入输出端口声明
input I_OPR_CLK,
input I_OPR_RSTN,
input [P_OPR_DATA_WIDTH_A-1:0] I_OPR_DATA_A,
input [P_OPR_DATA_WIDTH_B-1:0] I_OPR_DATA_B,
input I_OPR_DATA_VAL,
output reg [P_OPR_DATA_WIDTH_A+P_OPR_DATA_WIDTH_B-1:0] O_MULT_RES,
output reg O_MULT_RES_VAL
);
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
O_MULT_RES <= {(P_OPR_DATA_WIDTH_A+P_OPR_DATA_WIDTH_B){1'b0}};
O_MULT_RES_VAL <= 1'b0;
end
else
begin
if(I_OPR_DATA_VAL)
begin
O_MULT_RES <= {{(P_OPR_DATA_WIDTH_B){I_OPR_DATA_A[P_OPR_DATA_WIDTH_A-1]}},I_OPR_DATA_A} * {{(P_OPR_DATA_WIDTH_A){I_OPR_DATA_B[P_OPR_DATA_WIDTH_B-1]}},I_OPR_DATA_B};
O_MULT_RES_VAL <= I_OPR_DATA_VAL;
end
else
begin
O_MULT_RES <= {(P_OPR_DATA_WIDTH_A+P_OPR_DATA_WIDTH_B){1'b0}};
O_MULT_RES_VAL <= 1'b0;
end
end
end
endmodule
顶层模块
// | ============================== 有符号运算测试顶层模块 ==============================
// | 作者:Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 时间:2023-01-05
// | 功能:实现有符号数混合运算 (A+B)*C-D
// | ====================================================================================
`timescale 1ns / 1ps
module SIGNED_CAL_TOP_MDL(
// 输入输出端口声明
input I_OPR_CLK,
input I_OPR_RSTN,
input [15:0] I_OPR_DATA_A,
input I_OPR_VAL_A,
input [15:0] I_OPR_DATA_B,
input I_OPR_VAL_B,
input [15:0] I_OPR_DATA_C,
input I_OPR_VAL_C,
input [15:0] I_OPR_DATA_D,
input I_OPR_VAL_D,
output [33:0] O_OPR_RES,
output O_OPR_RES_VAL
);
wire [16:0] W_ADD_RES;
wire W_ADD_RES_VAL;
reg [15:0] R_I_OPR_DATA_C;
reg R_I_OPR_VAL_C;
wire [32:0] W_MULT_RES;
wire W_MULT_RES_VAL;
reg [15:0] R_I_OPR_DATA_D[1:0];
reg [1:0] R_I_OPR_VAL_D;
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
R_I_OPR_DATA_C <= 16'd0;
R_I_OPR_VAL_C <= 1'b0;
R_I_OPR_DATA_D[0] <= 16'd0;
R_I_OPR_DATA_D[1] <= 16'd0;
R_I_OPR_VAL_D <= 2'b0;
end
else
begin
R_I_OPR_DATA_C <= I_OPR_DATA_C;
R_I_OPR_VAL_C <= I_OPR_VAL_C;
R_I_OPR_DATA_D[0] <= I_OPR_DATA_D;
R_I_OPR_DATA_D[1] <= R_I_OPR_DATA_D[0];
R_I_OPR_VAL_D <= {R_I_OPR_VAL_D[0],I_OPR_VAL_D};
end
end
SIGNED_ADD_MDL #(
.P_OPR_DATA_WIDTH(32'd16)
) INST_SIGNED_ADD_MDL (
.I_OPR_CLK (I_OPR_CLK),
.I_OPR_RSTN (I_OPR_RSTN),
.I_OPR_DATA_A (I_OPR_DATA_A),
.I_OPR_DATA_B (I_OPR_DATA_B),
.I_OPR_DATA_VAL (I_OPR_VAL_A & I_OPR_VAL_B),
.O_ADD_RES (W_ADD_RES),
.O_ADD_RES_VAL (W_ADD_RES_VAL)
);
SIGNED_SUB_MDL #(
.P_OPR_DATA_WIDTH(32'd33)
) INST_SIGNED_SUB_MDL (
.I_OPR_CLK (I_OPR_CLK),
.I_OPR_RSTN (I_OPR_RSTN),
.I_OPR_DATA_A (W_MULT_RES),
.I_OPR_DATA_B ({{17{R_I_OPR_DATA_D[1][15]}},R_I_OPR_DATA_D[1]}),
.I_OPR_DATA_VAL (R_I_OPR_VAL_D[1] & W_MULT_RES_VAL),
.O_SUB_RES (O_OPR_RES),
.O_SUB_RES_VAL (O_OPR_RES_VAL)
);
SIGNED_MULT_MDL #(
.P_OPR_DATA_WIDTH_A(32'd17),
.P_OPR_DATA_WIDTH_B(32'd16)
) INST_SIGNED_MULT_MDL (
.I_OPR_CLK (I_OPR_CLK),
.I_OPR_RSTN (I_OPR_RSTN),
.I_OPR_DATA_A (W_ADD_RES),
.I_OPR_DATA_B (R_I_OPR_DATA_C),
.I_OPR_DATA_VAL (W_ADD_RES_VAL & R_I_OPR_VAL_C),
.O_MULT_RES (W_MULT_RES),
.O_MULT_RES_VAL (W_MULT_RES_VAL)
);
endmodule
仿真文件
编写测试文件:
`timescale 1ns / 1ps
module TB_SIGNED_CAL_TOP_MDL();
// 输入输出端口声明
reg I_OPR_CLK;
reg I_OPR_RSTN;
reg [15:0] I_OPR_DATA_A;
reg I_OPR_VAL_A;
reg [15:0] I_OPR_DATA_B;
reg I_OPR_VAL_B;
reg [15:0] I_OPR_DATA_C;
reg I_OPR_VAL_C;
reg [15:0] I_OPR_DATA_D;
reg I_OPR_VAL_D;
wire [33:0] O_OPR_RES;
wire O_OPR_RES_VAL;
initial I_OPR_CLK = 1'b0;
always #10 I_OPR_CLK = ~I_OPR_CLK;
initial
begin
I_OPR_RSTN = 1'b0;
I_OPR_DATA_A = 16'd0;
I_OPR_VAL_A = 1'b0;
I_OPR_DATA_B = 16'd0;
I_OPR_VAL_B = 1'b0;
I_OPR_DATA_C = 16'd0;
I_OPR_VAL_C = 1'b0;
I_OPR_DATA_D = 16'd0;
I_OPR_VAL_D = 1'b0;
#204;
I_OPR_RSTN = 1;
#100;
TASK_SIM(16'd100,-16'd120,-16'd200,16'd390);
TASK_SIM(16'd10,-16'd1200,-16'd200,16'd390);
TASK_SIM(16'd50,-16'd120,-16'd200,16'd3900);
TASK_SIM(16'd1000,-16'd12,-16'd200,16'd390);
TASK_SIM(16'd120,-16'd120,-16'd210,16'd390);
TASK_SIM(16'd180,-16'd120,-16'd250,16'd390);
$finish;
end
SIGNED_CAL_TOP_MDL INST_SIGNED_CAL_TOP_MDL
(
.I_OPR_CLK (I_OPR_CLK),
.I_OPR_RSTN (I_OPR_RSTN),
.I_OPR_DATA_A (I_OPR_DATA_A),
.I_OPR_VAL_A (I_OPR_VAL_A),
.I_OPR_DATA_B (I_OPR_DATA_B),
.I_OPR_VAL_B (I_OPR_VAL_B),
.I_OPR_DATA_C (I_OPR_DATA_C),
.I_OPR_VAL_C (I_OPR_VAL_C),
.I_OPR_DATA_D (I_OPR_DATA_D),
.I_OPR_VAL_D (I_OPR_VAL_D),
.O_OPR_RES (O_OPR_RES),
.O_OPR_RES_VAL (O_OPR_RES_VAL)
);
task TASK_SIM;
input [15:0] I_A;
input [15:0] I_B;
input [15:0] I_C;
input [15:0] I_D;
begin
@(posedge I_OPR_CLK)
I_OPR_DATA_A <= I_A;
I_OPR_VAL_A <= 1'b1;
I_OPR_DATA_B <= I_B;
I_OPR_VAL_B <= 1'b1;
I_OPR_DATA_C <= I_C;
I_OPR_VAL_C <= 1'b1;
I_OPR_DATA_D <= I_D;
I_OPR_VAL_D <= 1'b1;
@(posedge I_OPR_CLK)
I_OPR_DATA_A <= 16'd0;
I_OPR_VAL_A <= 1'b0;
I_OPR_DATA_B <= 16'd0;
I_OPR_VAL_B <= 1'b0;
I_OPR_DATA_C <= 16'd0;
I_OPR_VAL_C <= 1'b0;
I_OPR_DATA_D <= 16'd0;
I_OPR_VAL_D <= 1'b0;
#120;
end
endtask
endmodule
仿真结果
仿真波形:
综合分析
仿真发现功能准确后,对RTL代码进行综合:
对于-flatten_hierarchy执行不同的综合策略选项:
如何切换不同策略?
点击 Apply 以后会跳出如下对话框:
我的建议是选择 No,否则每切换一次综合策略就会生成一个新的综合文件夹,导致垃圾文件堆积。
rebuilt
none
full
通过上面对同一RTL源代码,进行不同策略的综合分析,当 -flatten_hierarchy的综合策略为full时,会将原本模块与模块之间的界限打破,从综合的结果来看,仅剩一个DSP48E1。而其余两个均在网表中保留了原本RTL设计中的模块层次界限。
(*KEEP_HIERARCHY = "yes"*)
在 -flatten_hierarchy的综合策略为full的情况下,使用综合属性 (*KEEP_HIERARCHY = "yes"*) 对加法运算模块进行约束,综合后的网表结果:
可以看到加法模块的层级仍然保留,其他模块的层级不见了,乘法和减法用DSP48E1代替了。
总结
🔶对于-flatten_hierarchy ,通常保持默认值 rebuilt 即可。使用 rebuilt 的益处在于,使用内嵌的逻辑分析仪(ILA时),可以快速地根据层级找到待观测信号。
🔷KEEP_HIERARCHY 的优先级高于 -flatten_hierarchy ,可根据需要对某些层次设置此属性。对于此属性在 Verilog HDL 中的使用再补充两点:
(*KEEP_HIERARCHY = "yes"*) 可以约束在 设计文件 中,类似下图这种,当约束在设计文件中时,该模块在任何一个地方例化,都会保持原本的层级结构。
(*KEEP_HIERARCHY = "yes"*) 也可以约束在 模块例化 时,类似下图这种,当约束在模块例化处时,仅对约束的例化模块有效。
上述代码的综合结果:
-control_set_opt_threshold
解释说明
对于此设置项,Vivado给出18个可选项:0~16,auto(默认)
官方的说法是:
Threshold for synchronous control set optimization to lower number of control sets .The higher the number, the more control set optimization will be performed and fewer control sets will result.To disable control set optimization completely, set to 0.
百度翻译的结果:
用于同步控制集优化的阈值,以减少控制集的数量。数量越高,将执行的控制集优化越多,产生的控制集越少。若要完全禁用控制集优化,请设置为0。
设计实践
总结
-no_lc
解释说明
设计实践
总结
-keep_equivalent_registers
解释说明
设计实践
总结
-resource_sharing
解释说明
设计实践
总结
-gated_clock_conversion
解释说明
设计实践
总结
-fanout_limit
解释说明
设计实践
总结
-shreg_min_size、-no_srlextract
解释说明
设计实践
总结
-fsm_extraction
解释说明
设计实践
总结
综合属性
ASYNC_REG
解释说明
设计实践
总结
MAX_FANOUT
解释说明
设计实践
总结
SRL_STYLE、SHREG_EXTRACT
解释说明
设计实践
总结
USE_DSP
解释说明
设计实践
总结
RAM_STYLE、ROM_STYLE
解释说明
设计实践
总结
EXTRACT_ENABLE、EXTRACT_RESET
解释说明
设计实践
总结
MARK_DEBUG
解释说明
设计实践
总结
实现阶段
设计分析相关
查看逻辑级数 tcl 脚本命令:
report_design_analysis -logic_level_distribution -logic_level_dist_paths 5000 -name design_analysis_prePlace
参考声明
【1】高亚军,Vivado 从此开始(进阶篇).北京:电子工业出版社. 2020.1