FPGA之道(64)代码中的约束信息(一)保持约束

前言

这一节的内容很有意思也很有用,对于我们主动地操作我们的Verilog代码很有帮助。众所周知,通过设置工具的综合策略,实现策略等也可以实现对综合以及实现的操作,但这是对于整体地、宏观地操作,如果我们需要对其中的某些模块,某些功能进行操作,使其按照我们的想法来综合、实现等等,就需要我们用到本节的内容,在代码中添加约束信息。我曾在Vivado系列的博客也写过这方面的内容,确实这部分内容是几乎每一个系列都要写的,可见其重要性。
最重要:本系列以及本文节选自《FPGA之道》,让我们一起站在巨人的肩膀上来学习下代码中的相关的约束信息。

HDL中的常用约束示例

HDL中可以加入的约束信息有很多种,本章节就以Xilinx公司的FPGA芯片的集成开发环境为例,介绍一些常用的嵌入到HDL中的约束示例。而其它厂商的FPGA芯片的代码内约束添加方法均是雷同的。
需要说明的是,嵌入在HDL代码当中的这些约束信息也可以集中写在一个约束文件中,这样更加方便管理,只不过此时语法略有不同,且需要明确指定每条约束所约束对象的绝对路径。

保持约束

keep

keep是一个高级的映射和综合约束。
当编译器在对FPGA设计进行映射时,一些线网将会被吸纳进逻辑块中,而这一操作将使得这些线网无法在项目的物理设计数据库中显示出来。例如,如果某个线网连接的几个元件都被映射到了同一个逻辑块中,那么该线网通常也会被被该逻辑块吸收。这样一来,由于在设计的物理设计数据库找不到该线网,因此后续的一些针对于该线网的约束信息也就无法约束成功。
不过keep的出现改变了这种状况,当为某一线网信号添加了keep类型的约束后,编译器会将其理解为“不允许合并”的意思,因此该线网将不会被吸收进逻辑块,进而也就能在物理设计数据库中被找到。
下面列举出在HDL中嵌入keep约束的语法:
– VHDL syntax
– 第一步,先要声明keep约束;
attribute keep : string;
– 第二步,为keep语法指定约束线网名称和属性取值;
attribute keep of <signal_name>: signal is “{TRUE|FALSE}”;
– 注意,VHDL的约束信息通常都需要放在architecture的声明和定义部分

// Verilog syntax
(* KEEP = “{TRUE|FALSE}” *)
// 上述约束会作用于紧随其后定义的线网
在为FPGA内部产生的时钟信号添加时序约束时,keep约束经常会被用到。因为这些内部时钟线网大多由逻辑生成,因此容易出现被逻辑块吸收的情况。这样一来,由于在物理设计数据库中找不到与该时钟信号名称相关的物理线网(其实该时钟网络可能有了一个完全不同的、新的名称),进而也就难以成功添加针对该时钟信号的相关时序约束(详细的时序约束介绍将会在【时序分析篇->常用时序约束】章节中进行介绍),最终很可能导致设计失败。而使用keep约束则可以避免这种情况的出现,例如:
– VHDL example
signal clk50MHz : std_logic; – 内部生成的时钟信号

attribute keep : string; -- keep约束声明
	attribute keep of clk50MHz: signal is "TRUE"; -- keep约束加至clk50MHz线网上

// Verilog example
(* KEEP = “TRUE” *) // keep约束
wire clk50MHz; // 内部生成的时钟信号
通过在HDL代码中嵌入上述约束信息后,就可以非常方便的在后续的用户约束文件(ucf文件)中成功添加如下约束信息:

NET "clk50MHz" TNM_NET = UserGClk1;
TIMESPEC TS_UserGClk1 = PERIOD "UserGClk1" 20 ns;

以上便是keep约束的一个应用示例。除此以外,根据keep属性的说明,我们可以发现它还可以被用来进行一些故意的逻辑冗余设计,详见【时序分析篇->根据时序报告修改设计->常见问题及修改->使用综合约束】小节。

keep_hierarchy

keep_hierarchy是一个综合和实现方面的约束。
当Xilinx的综合工具XST更倾向于平化HDL代码的层级结构,即将一级级的模块调用机制转换为一个没有子模块的超大模块,这样做的好处是能够进行更好的设计优化工作,因为平化操作去除了原有实体或模块之间的边界限制。
不过有些时候,XST的这种平化工作是我们所不希望的,这时keep_hierarchy语法便派上了用场。使用keep_hierarchy约束能够使综合工具保留指定部分的层级关系,这样一来,在后续的实现阶段也将保留这种层级关系。与此同时,也可以生成用于仿真的具有层级关系的网表。下图就很形象的说明了使用keep_hierarchy约束和不使用keep_hierarchy约束在综合及实现后的区别:
在这里插入图片描述
下面列举出在HDL中嵌入keep_hierarchy约束的语法:
– VHDL syntax
– 第一步,先要声明keep_hierarchy约束;
attribute keep_hierarchy : string;
– 第二步,为keep_hierarchy语法指定约束线网名称和属性取值;
attribute keep_hierarchy of <architecture_name>: architecture is “{TRUE|FALSE|SOFT}”;

// Verilog syntax
(*KEEP_HIERARCHY = “{TRUE|FALSE|SOFT}” *)
// 上述约束会作用于紧随其后调用的模块
从上述语法可以看出,keep_hierarchy约束具有三种属性值,其中false自然是不保留层级关系,而true和soft的区别就是,true会将这种约束传递给后续的实现环节,而soft则仅仅是在综合环节保留层级关系。
下面给出一个利用keep_hierarchy实现三模冗余的示例(注,三模冗余是航空、航天系统中经常用到的一种技术,简要来说,就是由于空间中的射线等原因,会一定几率引起电子产品尤其是基于SRAM的FPGA发生一种称作“单粒子翻转”的效应。“单粒子翻转”即1变成0、0变成1,这样将会导致系统运行出错。因此为了提高系统的稳定性和可靠性,人们想了很多种方法来应对空间中较为恶劣的工作环境,其中一种就是三模冗余,即通过3倍的冗余,结合3中取2的少数服从多数仲裁机制,来提高FPGA设计的抗干扰能力)。

-- VHDL example
	-- file lock.vhd
	library IEEE;
	use IEEE.STD_LOGIC_1164.ALL;
	use IEEE.STD_LOGIC_ARITH.ALL;
	use IEEE.STD_LOGIC_UNSIGNED.ALL;
	entity lock is
	port (
   clk : in	std_logic;
   rst : in std_logic;
   din : in std_logic;
   dout : out std_logic
	);
	end lock;
	architecture Behavioral of lock is
	begin
	process(clk)
	begin
		if(clk'event and clk = '1')then
			if(rst = '1')then
				dout <= '0';
			else
				dout <= din;
			end if;
		end if;
	end process;
	end Behavioral;
	// file mode3.vhd
	library IEEE;
	use IEEE.STD_LOGIC_1164.ALL;
	use IEEE.STD_LOGIC_ARITH.ALL;
	use IEEE.STD_LOGIC_UNSIGNED.ALL;
	entity mode3 is
	port (
   clk : in	std_logic;
   rst : in std_logic;
   din : in std_logic;
   dout : out std_logic
	);
	end mode3;
	architecture Behavioral of mode3 is
		signal a, b, c : std_logic;
		COMPONENT lock
		PORT(
			clk : in std_logic;
   		rst : in std_logic;
   		din : in std_logic;
   			dout : out std_logic
		);
		END COMPONENT;
	attribute keep_hierarchy : string; 
	attribute keep_hierarchy of m0: architecture is “TRUE”; 
	attribute keep_hierarchy of m1: architecture is “TRUE”; 
	attribute keep_hierarchy of m2: architecture is “TRUE”;
	begin
	m0: lock 
	PORT MAP(
		clk => clk,
		rst => rst, 
		din => din,
		dout => a
	); 
	m1: lock 
	PORT MAP(
		clk => clk,
		rst => rst, 
		din => din,
		dout => b
	); 
	m2: lock 
	PORT MAP(
		clk => clk,
		rst => rst, 
		din => din,
		dout => c
	);
	process(clk)
	begin
		if(clk'event and clk = '1')then
			if(rst = '1')then
				dout <= '0';
			else
				if(b /= c)then
					dout <= a;
				else
					dout <= b;
				end if;
			end if;
		end if;
	end process;
	end Behavioral;
// Verilog example
// file lock.v
	module lock(
	input clk,
	input rst,	
	input din,	
	output reg dout
	);	 
	always@(posedge clk)
	begin
		if(rst)
		begin
			dout <= 1'b0;
		end
		else
		begin
			dout <= din;
		end
	end	
	endmodule
// file mode3.v
	module mode3 (
	input clk,
	input rst,	
	input din,	
	output reg dout
	);	 
	wire a, b, c;	
	(* KEEP_HIERARCHY = "TRUE" *) 
	lock m0 (
	    .clk(clk), 
	    .rst(rst), 
	    .din(din), 
	    .dout(a)
	    );	
	(* KEEP_HIERARCHY = "TRUE" *)  
	lock m1 (
	    .clk(clk), 
	    .rst(rst), 
	    .din(din), 
	    .dout(b)
	    );		 
	(* KEEP_HIERARCHY = "TRUE" *)  
	lock m2 (
	    .clk(clk), 
	    .rst(rst), 
	    .din(din), 
	    .dout(c)
	    );	
	always@(posedge clk)
	begin
		if(rst)
		begin		
			dout <= 1'b0;
		end
		else
		begin
			if(b != c)
			begin
				dout <= a;
			end
			else 
			begin
				dout <= b;
			end
		end
	end	
	endmodule

采用上例这种keep_hierarchy的约束添加方式,就可以成功确保在FPGA中实现了三个功能一模一样的模块。否则,由于这三个模块在功能上面完全等价,编译器便会优化掉其中的两个,而最终只保留一个模块,但是这样就无法实现“三模冗余”的保护功能了。

发布了806 篇原创文章 · 获赞 1541 · 访问量 151万+

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/105020540