Xilinx RTL编码指南一
- 控制信号与控制集
- Reset
-
- 使用复位的时间和位置
- 同步复位与异步复位
- 复位编码实例 1:乘法器与异步复位
-
- 异步复位
-
- RTL
- synthesis
- 同步复位
-
- RTL
- synthesis
- 尝试消除HDL代码复位时出现问题
- 时钟使能
-
- 创建时钟使能
- 复位和时钟使能的先后
- 使用综合属性控制使能/重置提取
-
- DIRECT_ENABLE
- DIRECT_RESET
- 将逻辑从控制引脚推到数据引脚
- 信号控制技巧
该一系列为学习ug949-vivado-design-methodology.pdf的记录,该文档包含了对Xilinx器件最佳实践的方法学。
控制信号与控制集 ? 控制集指用于驱动任何给定 SRL、 LUTRAM 或寄存器的控制信号组 (置位/复位、时钟使能和时钟)。对任意控制信号的独特组合,都能构成独立控制集。背后的原因是一个重要的概念,即在 7 系列 slice 中的寄存器都共享相同的控制信号,因此只有使用相同控制集的寄存器才能打包到同一个 slice 中。不同slice可以有不同的控制信号。例如,如果具有给定控制集的寄存器仅具有一个寄存器作为加载,则被占据的片中的其他七个寄存器将不可用。(在wp 405-7 series FPGAS The Logical Advantage里面说明了确实是这样)
? 同时拥有多个独立控制集的设计会造成大量资源浪费和布局选项数量减少,导致功耗上升,性能下降。从布局的角度而言,拥有较少数量控制集的设计能提供更多选项和更高灵活性,一般也能产生更加理想的结果。在 UltraScale? 器件中,在 CLB 内部能灵活地实现控制集的映射。未驱动的复位不形成控制集的一部分,因为是在片内局部生成的。然而,限制独立控制集数目是为一组逻辑布局提供最大灵活性的一个好方法。
Reset 复位是需要在设计中考虑和设限的更常见也更重要的控制信号之一。复位会给用户设计的性能、占位面积和功耗产生显著影响。
经引用得到的同步代码会产生下列资源:
- LUT
- 寄存器
- 移位寄存器 LUT(SRL)
- 块存储器或 LUT 存储器
- DSP48 寄存器
使用复位的时间和位置 赛灵思器件提供专用的全局置位/复位信号 (GSR)。在器件配置结束时,该信号将器件配置中所有顺序单元的初始值。如果未指定初始状态,则为顺序原语分配默认值。在大多数情况下,默认值为零。 FDSE 和 FDPE 原语是例外,默认为逻辑 1。每个寄存器在配置结束时将处于已知状态。因此没有必要单独为初始化加电器件编写全局复位代码。赛灵思强烈建议用户仔细判断设计何时需要复位,何时不需要复位。大多数情况下,在控制路径逻辑上可能需要复位以确保正常运行。然而在数据路径逻辑上通常不需要复位。限制复位使用的方法如下:
- 限制复位网络的总体扇出。
- 减少复位路由所需的互联数量。
- 简化复位路径的时序。
- 从而在许多情况下能够从整体上提升性能、缩小占位面积和降低功耗。
- 要求的功能
- 性能要求
- 可用器件资源
- 功耗
- 同步复位可以直接映射到 FPGA 器件架构中的更多资源元件。
- 异步复位还会影响通用逻辑结构的性能。由于所有赛灵思 FPGA 通用寄存器都能置位/复位为异步或同步,通常认为使用异步复位不会造成时间延迟。这种假设往往是不对的。使用全局异步复位时,虽然控制集的数量不会增多,但由于需要把这个复位信号路由到所有的寄存器元件,时序复杂性会增大。
- 在使用异步复位时,务必记住同步异步复位的取消断言。
- 在需要较大密度或精细化布局的情况下,同步复位能够更加灵活地实现控制集的重新映射。如果在布局更加理想的 slice 中发现有不兼容的复位,可以把同步复位重新映射到该寄存器的数据路径中。这样可以在需要时缩小走线宽度,提升密度,从而实现良好的适配和更加优异的性能。
- 异步复位可能需要多周期激活,以确保电路正确复位且稳定。如果正确地被时序约束了,同步复位则不包含此要求。
- 在复位激活过程中,若异步复位有更高概率发生块 RAM、 LUTRAM、以及 SRL 存储内容翻转时,可以使用同步复位。
- DSP48 和块 RAM 等部分资源只为块中的寄存器元件提供同步复位。当需要在这些元件相关的寄存器元件上使用异步复位时,不能将这些寄存器直接推断到这些块中,否则会造成功能异常
module test(
input clk,
input rst,
input [15:0] a,
input [15:0] b,
output[31:0]dout
);
reg [15:0] a1,a2,b1,b2;
reg [31:0] dout_reg;
always @(posedge clk,posedge rst) begin
if(rst)begin
a1<=0;
a2<=0;
b1<=0;
b2<=0;
dout_reg <= 0;
end else begin
a1<=a;
a2<=a1;
b1<=b;
b2<=b1;
dout_reg <= a2*b2;
end
end
assign dout = dout_reg;
endmodule
RTL
文章图片
synthesis 只截取了一部分图。乘法部分使用了DSP48的资源
文章图片
同步复位
module test(
input clk,
input rst,
input [15:0] a,
input [15:0] b,
output[31:0]dout
);
reg [15:0] a1,a2,b1,b2;
reg [31:0] dout_reg;
always @(posedge clk) begin
if(rst)begin
a1<=0;
a2<=0;
b1<=0;
b2<=0;
dout_reg <= 0;
end else begin
a1<=a;
a2<=a1;
b1<=b;
b2<=b1;
dout_reg <= a2*b2;
end
end
assign dout = dout_reg;
endmodule
RTL
文章图片
这两份代码的RTL的图似乎没有区别,只是前面一个使用了的异步复位,复位端rst接到了寄存器的CLR端,后一个使用了同步复位,复位端rst接到了寄存器的RST端。这说明RTL中CLR是异步清零端,RST是同步复位端,再来看综合后的图:
synthesis
文章图片
对比异步复位综合后的图,可以发现同步复位少了一个触发器和32个LUT2查找表。由于DSP无异步复位,综合器选择用一个触发器和 32 个 LUT2来模拟 DSP 输出上的异步复位 。
尝试消除HDL代码复位时出现问题
module test(
input clk,
input rst,
input [15:0] din,
output[15:0]dout
);
reg [15:0] d1,d2;
reg [15:0] dout_reg;
always @(posedge clk,posedge rst) begin
if(rst)begin
d1<=0;
d2<=0;
dout_reg <= 0;
end else begin
d1<=din;
d2<=d1;
dout_reg <= d2;
end
end
assign dout = dout_reg;
endmodule
上面一段代码将生成下图的RTL:
文章图片
若用户想取消d1,d2的异步复位,选择如下修改:
//d1<=0;
// d2<=0;
仅仅在复位条件内注释掉d1,d2,则会产生下面的RTL:
文章图片
综合器会推断出d1和d2隐含保持的含义,因此增加了2个2选择器,并将选择器输出直连到d1,d2的使能端。
去除复位的最佳方法是创建单独的顺序逻辑过程,一个用于复位条件,另一个用于非复位条件。
module test(
input clk,
input rst,
input [15:0] din,
output[15:0]dout
);
reg [15:0] d1,d2;
reg [15:0] dout_reg;
always @(posedge clk) begin
d1<=din;
d2<=d1;
end
always @(posedge clk) begin
if(rst)begin
dout_reg <= 0;
end else begin
dout_reg <= d2;
end
end
assign dout = dout_reg;
endmodule
这样生成的RTL才符合用户的原始想法。
文章图片
时钟使能 ? 如果正确使用,时钟使能能够显著地降低系统功耗,同时对面积或性能的影响极小。但是如果不正确地使用时钟使能,
可能会造成下列后果:
- 面积增大
- 密度减小
- 功耗上升
- 性能下降
创建时钟使能 ? 如果在同步块中编写不完整条件语句,就能创建时钟使能。推断时钟使能的目的是当前提条件无法满足时,保留最后一个值。如果这是需要的功能,用这种方式编码就是有效的。
module test(
input clk,
input en,
input din,
output dout
);
reg dout_reg;
always @(posedge clk) begin
if(en)begin
dout_reg <= din;
end
end
assign dout = dout_reg;
endmodule
文章图片
? 但是在有些情况下,虽然前提条件值未得到满足,但输出无所谓。此时赛灵思建议用设定的常数 (即为信号赋值 1 或 0)关闭该条件 (即使用 else 子句)。
module test(
input clk,
input en,
input din,
output dout
);
reg dout_reg;
always @(posedge clk) begin
if(en)begin
dout_reg <= din;
end
else begin
dout_reg <= 16'b0;
endend
assign dout = dout_reg;
endmodule
文章图片
此时的en信号并未直接连接到寄存器的使能端了。
? 在大多数实现方案中,这不会造成额外的逻辑,同时可避免使用时钟使能。不过有个情况例外,即对大型总线而言,如果推断时钟使能信号,保持上述的最后一个值,有助于降低功耗。基本前提是如果推断的寄存器数量较少,时钟使能会存在较大弊端,因为它会增加控制集的数量。但是对较大型的群组而言,其利大于弊,所以建议使用。
复位和时钟使能的先后 ? 在赛灵思 FPGA 器件中,所有寄存器的置位/复位功能的优先级均高于时钟使能,不论是异步置位/复位还是同步置位/复位都是如此。为取得最佳结果,赛灵思建议在同步块中的 if/else 结构中,应一直在时钟使能 (如有必要使用)之前对置位/复位进行编码。优先对时钟使能进行编码会强制复位进入数据路径,并导致产生更多逻辑。
使用综合属性控制使能/重置提取 您可以通过DIRECT_RESET,DIRECT_ENABLE ,EXTRACT_RESET
EXTRACT_ENABLE 属性来强制控制器集映射,以处理给定结构的控制器集的映射。当设计包括同步复位/使能时,当负载等于或高于由 “-control_set_opt_threshold” 综合开关设置的阈值时,通过CE/R/S 引脚映射的综合创建逻辑椎,或如果低于阈值,通过 D 引脚映射创建逻辑椎。默认阈值为:
- 7 系列器件:4
- UltraScale 器件:2
要使用控制器集映射,你可以将属性应用于连接启用/复位信号的网络,但这将强制综合使用 CE/R 引脚。在下图中,使能信号 (en) 只连接到一个触发器上。因此,综合引擎将 en 信号连接到逻辑的 FDRE/D 引脚椎。注意,CE 引脚连接到逻辑 1。
文章图片
要覆盖此默认行为,可以使用 DIRECT_ENABLE 属性。例如,下图显示了如何通过将 DIRECT_ENABLE 属性添加到端口/信号来将使能信号 (en) 连接到寄存器的 CE 引脚。
文章图片
但在vivado2019中,未加“direct_enable = true”就已经是综合成下面的情况了,直接使用了en使能端。
文章图片
DIRECT_RESET
下图显示了RTL 代码,其中 global_rst 或 int_rst 可以复位寄存器。默认情况下。
文章图片
仔细看,可以发现dout_reg的R复位端没有接,而是将global_rst和int_rst融入了数据路径中。
您可以使用 DIRECT_RESET 属性来指定连接到寄存器复位引脚的复位信号。例如,下图显示了如何使用 DIRECT_RESET 属性仅将 global_rst 信号连接到寄存器 FDRE/R 引脚,并将 int_rst 信号连接到逻辑的 FDRE/D 椎形。
文章图片
但同样在vivado2019中,未加“direct_reset = true”就已经是综合成下面的情况了,直接使用RST端。
文章图片
将逻辑从控制引脚推到数据引脚 ? 在分析关键路径时,您可能会发现以控制引脚结束的多个路径。您必须分析这些路径,以确定是否有一种方法将逻辑推入数据路径,而不会产生损失,例如额外的逻辑层。由于存在最后一个 LUT 的输出到 FF 的 D 输入的直接连接,在给定相同逻辑层的情况下,到 D 引脚路径的延迟比到 CE/R/S 引脚少。以下编码示例说明如何将逻辑从控制引脚推送到寄存器的数据引脚。
在以下示例中,dout_reg_reg 的CE使能引脚具有 2 个逻辑层,数据引脚D具有1个逻辑层。
module test(
input clk,
input [1:0] en,
input din,
output dout
);
reg [1:0] en_reg;
reg dout_reg,din_reg;
wire en_temp = &en_reg;
always @(posedge clk) begin
en_reg <= en;
din_reg <= din;
if(en_temp)
begin
dout_reg <= din_reg;
end
end
assign dout = dout_reg;
endmodule
文章图片
? 在这种情况下,通过将 RTL 文件中 dout_reg 寄存器定义中的 EXTRACT_ENABLE 属性设置为“no”,可以将使能逻辑移至 D 引脚,从而提高时序。
module test(
input clk,
input [1:0] en,
input din,
output dout
);
reg [1:0] en_reg;
reg dout_reg,din_reg;
(*KEEP= "true" *) reg dout_next;
wire en_temp = &en_reg;
always @(*) begin
dout_next = dout_reg;
if(en_temp)
dout_next = din_reg;
end
always @(posedge clk) begin
en_reg <= en;
din_reg <= din;
dout_reg <= dout_next;
end
assign dout = dout_reg;
endmodule
文章图片
此时dout_reg_reg的CE使能端直接接高电平。保持的实现通过dout_reg_reg的Q输出端->LUT4 I1->dout_reg_reg的D输入端实现。
信号控制技巧
- 检查是否真正需要全局复位。
- 避免异步控制信号。
- 保持时钟、使能和复位信号极性一致。
- 勿将置位和复位编码到同一寄存器元件中。
- 如果确实需要异步复位,应务必与异步复位的解除保持同步。
推荐阅读
- FPGA|基于SDSoC的软硬件协同设计
- Verilog|(127)Verilog HDL(设计一个优先编码器之Always case2)
- Verilog|(128)Verilog HDL(设计一个优先编码器之Always casez)
- Verilog|【Verilog HDL学习之路】第一章 Verilog HDL 数字设计总论
- 视频处理|FPGA verilog 临近插值任意比例视频缩小代码
- 视频处理|Syetem Verilog 将视频流输出写入 BMP 图片文件 testbench 激励代码
- 视频处理|Syetem Verilog 用BMP图片文件产生视频流 testbench 激励代码
- 视频处理|FPGA 处理视频SDRAM带宽计算
- fpga|【入门学习四】基于 FPGA 使用 Verilog 实现串口回传通信代码及原理讲解