FPGA之道(66)代码中的约束信息(三)存储器以及寄存器的相关约束

前言

这是这个话题的第三篇,最重要的前言是本文节选自:《FPGA之道》。

存储器的相关约束

与乘法器类似,当我们需要使用RAM或ROM作数据存储时,通常的做法也是调用IP核来进行配置。因此,当涉及到大量不同存储模块调用或修改的时候,我们同样面临着令人头疼的工作。所以,掌握一些基本的存储器相关实现约束是非常必要的。

ram_extract

ram_extract是针对RAM的一项综合约束,它的意思是是否从HDL代码中提取RAM结构。即,一旦使能该约束,编译器便会对我们的HDL代码进行分析,看内部是不是有跟RAM功能吻合的描述。如果存在这种描述,那么编译器将认为这是一个RAM,并采取相应的方式来实现RAM,否则很可能就按照寄存器形式来实现这部分存储代码。由此可见,要想通过HDL自己编写RAM,必须首先使能ram_extract约束。
下面列举出在HDL中嵌入ram_extract约束的语法:
– VHDL syntax
– 第一步,先要声明ram_extract约束;
attribute ram_extract : string;
– 第二步,为ram_extract语法指定约束线网名称和属性取值;
attribute ram_extract of <{signal_name|entity_name}> : {signal|entity} is “{yes|no}”;

// Verilog syntax
(* ram_extract = “{yes|no}” *)
// 上述约束会作用于紧随其后调用的实体、模块或信号

ram_style

ram_style是针对RAM的另一项综合约束,它表明了RAM的实现风格。
下面列举出在HDL中嵌入ram_style约束的语法:
– VHDL syntax
– 第一步,先要声明ram_style约束;
attribute ram_style : string;
– 第二步,为ram_style语法指定约束线网名称和属性取值;
attribute ram_style of <{signal_name|entity_name}> : {signal|entity} is
“{auto|block|distributed|pipe_distributed}”;

// Verilog syntax
(* ram_style = “{auto|block|distributed|pipe_distributed}” *)
// 上述约束会作用于紧随其后调用的实体、模块或信号
从上述语法可以看出,ram_style约束具有四个属性值,分别为:auto——由编译器自动选择实现风格;block——使用BLOCK RAM资源;distributed——使用离散RAM资源(即LUT);pipe_distributed——通过流水线的思路来提高使用离散RAM资源时RAM的性能(即会消耗大量寄存器资源)。
结合ram_extract和ram_style,我们可以自己编写RAM模块,并制定使用何种资源来实现。下述即为一个使用BRAM实现RAM存储的示例:

-- VHDL example
type ram_type is array (1023 downto 0) of std_logic_vector (15 downto 0);
signal RAM : ram_type;
attribute ram_extract : string;
attribute ram_extract of RAM : signal is "yes";
attribute ram_style : string;
attribute ram_style of RAM : signal is "block"; 
	……
process (clk)
begin
		if (clk'event and clk = '1') then
			if (wr = '1') then
				RAM(conv_integer(wrAddr)) <= D;
			end if;		
			Q <= RAM(conv_integer(rdAddr));
		end if;
end process;
// Verilog example
(* ram_extract = "yes" *)
(* ram_style = "block" *)
reg [15:0] RAM[1023:0];
……
always@(posedge clk)
begin
	if(wr == 1’b1)
	begin
		RAM(conv_integer(wrAddr)) <= D;
		end
		Q <= RAM(conv_integer(rdAddr));
end

注意,编译器是否能达到约束中使用BRAM资源的预期,很大程度上取决于对RAM读取时的描述。这里请大家记住一点,那就是无论对RAM的读取逻辑有多么复杂,都切记读取到Q的赋值语句只能出现一次。例如,你想在复位时改变RAM的输出,那么可以在Q前面再加一级寄存器Qs,然后用复位去控制Qs即可,否则编译器很可能不能用BRAM来实现你的RAM。正确的示例如下:

- VHDL example 
type ram_type is array (1023 downto 0) of std_logic_vector (15 downto 0);
signal RAM : ram_type;
attribute ram_extract : string;
attribute ram_extract of RAM : signal is "yes";
attribute ram_style : string;
attribute ram_style of RAM : signal is "block"; 
	……
process (rst, clk)
begin
	if(rst = '1')then
		<some initial but not on Q>;
	else
			if (clk'event and clk = '1') then
				if (wr = '1') then
					RAM(conv_integer(wrAddr)) <= D;
				end if;		
				Q <= RAM(conv_integer(rdAddr));
			end if;
		end if;
end process;
// Verilog example
(* ram_extract = "yes" *)
(* ram_style = "block" *)
reg [15:0] RAM[1023:0];
……
always@(posedge rst, posedge clk)
begin
	if(rst == 1'b1)
	begin
		<some initial but not on Q>;
	end
	else
	begin
		if(wr == 1’b1)
		begin
			RAM(conv_integer(wrAddr)) <= D;
			end
			Q <= RAM(conv_integer(rdAddr));
		end
end

rom_extract

rom_extract约束是关于ROM的一个综合约束,它和ram_extract约束的意义几乎一样,只不过它是针对ROM来说的。
下面列举出在HDL中嵌入rom_extract约束的语法:
– VHDL syntax
– 第一步,先要声明rom_extract约束;
attribute rom_extract : string;
– 第二步,为rom_extract语法指定约束线网名称和属性取值;
attribute rom_extract of <{signal_name|entity_name}> : {signal|entity} is “{yes|no}”;

// Verilog syntax
(*rom_extract = “{yes|no}” *)
// 上述约束会作用于紧随其后调用的实体、模块或信号

rom_style

rom_style是针对ROM的另一项综合约束,它和ram_style约束的意义几乎一样,只不过它表明了ROM的实现风格。
下面列举出在HDL中嵌入rom_style约束的语法:
– VHDL syntax
– 第一步,先要声明rom_style约束;
attribute rom_style : string;
– 第二步,为rom_style语法指定约束线网名称和属性取值;
attribute rom_style of <{signal_name|entity_name}> : {signal|entity} is
“{auto|block|distributed }”;

// Verilog syntax
(* rom_style = “{auto|block|distributed }” *)
// 上述约束会作用于紧随其后调用的实体、模块或信号
对比ram_style的语法可以发现,rom_style约束的属性取值中少了一项pipe_distributed,这大概是由于只读存储实现起来比读写存储本身就要简洁的原因。

寄存器的相关约束

作为一名FPGA开发者,寄存器是我们再熟悉不过的东西了,不过可能很少会有人关心自己项目中所使用的寄存器到底是属于哪一类资源的。经过【知己知彼篇->FPGA内部资源介绍】章节的学习,我们了解到,FPGA芯片中的绝大多数寄存器资源都位于逻辑资源块内部,因此在进行HDL程序设计时,我们所描述的具有寄存器功能的代码,几乎都会被编译器最终映射到逻辑资源块中去。但是有些时候,这样的默认操作很可能会导致FPGA设计的失败或者性能降低。因为我们发现,其实在FPGA芯片的其他资源内部也是有寄存器分布的,例如接口资源,想必这样的安排一定是有其原因的。最显而易见的原因就是距离问题,以接口资源为例,这部分资源和FPGA芯片的pin(或pad)脚距离最近,外部信号必须要经过接口资源才能传递到内部的逻辑资源块,而距离决定了线延迟,因此,当处理高速输入接口时,为了保证设计的性能,有时候必须要使用位于接口资源内部的寄存器。
要使用接口内部的寄存器资源,约束语法如下:
– VHDL syntax
– 第一步,先要声明iob约束;
attribute iob: string;
– 第二步,为iob语法指定约束线网名称和属性取值;
attribute iob of {component_name|entity_name|label_name}:
<{component|entity|label}> is “{TRUE|FALSE|AUTO}”;

// Verilog syntax
(* IOB = “{TRUE|FALSE|AUTO}” *)
// 上述约束会作用于紧随其后调用的实体、模块或信号
例如,下面就是一种使用接口资源内部寄存器进行高速输入信号采集的示例,不过请注意,由于接口资源内部的寄存器通常不具有复位端口,因此在用HDL描述时,切记不要对想放入接口资源的寄存器进行复位操作。

-- VHDL example
signal IlogicData : std_logic; 
	attribute IOB : string;
	attribute IOB of IlogicData : signal is "TRUE";
	process(clk)
	begin	
		if (clk'event and clk = '1') then

			IlogicData <= inputPin;
		end if;	
	end process;
// Verilog example
(* IOB = "TRUE" *)
	reg IlogicData; 
	always@(posedge clk)
	begin
		IlogicData <= inputPin;
	end

需要说明的一点是,上例仅仅是示范了iob约束的使用方法,真要实现高性能的输入接口,时钟信号的来源也很重要,具体的方法已在【本篇->编程思路->关于外界接口的编程思路】章节中进行过介绍,这里就不再赘述。

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

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/105020754
今日推荐