FPGA之道(71)提高设计的综合性能(三)提高设计的重用性与易改性

举报
李锐博恩 发表于 2021/07/15 01:30:23 2021/07/15
【摘要】 文章目录 前言提高设计的重用性构建自己的IP库提高设计的易改性常量参数化模块设计结构参数化模块设计总线参数化规模参数化功能参数化 参数化设计的参数管理与组织参数相关性可传递的模块参数化例化参数文件结构 前言 本文节选自《FPGA之道》。 提高设计的重用性 如果打算长期从事FPGA项目开发的话,那么你总不希望自己之前花了那么多精力编写、仿真...

前言

本文节选自《FPGA之道》。

提高设计的重用性

如果打算长期从事FPGA项目开发的话,那么你总不希望自己之前花了那么多精力编写、仿真、测试通过的HDL代码都是一次性用品吧。通常来说,我们都希望自己之前编写的HDL代码能够继续在今后的FPGA开发过程中发光发热,这样,若碰到和之前类似的问题或情况,便不需要完全从0开始编写、仿真和实测代码。
下面,我们就来介绍一种常用的提高设计重用性的方法:

构建自己的IP库

使用现成的IP核,可以大幅的缩减FPGA项目的开发周期,增强FPGA设计的可靠性。那么,同样的道理,如果已经从事FPGA开发工作有一段时间,那么在今后的FPGA开发中,肯定会碰到需要实现一些在以前设计中已经实现或者功能类似的模块。因此,如果能对自己曾经编写的HDL代码也进行一些分类、修改和保存,就可以在碰到这样的情况时,直接调用以前编写、验证完成的模块,从而节省本次开发的时间。
那么,这样一个代码的分类、修改和保存的过程就是构建属于自己的专有IP库的过程,它能极大的提高设计的重用性,让以前的工作成果在今后的设计中仍能够继续发光发热。

当然了,如果仅仅是分类、保存以前的代码,那么在今后的重用中,由于情况通常都不可能完全相同,因此可能会涉及到较多甚至比较庞杂的修改过程。因此为了更好的提高设计的重用性,在分类、保存HDL代码之前,最好对其进行一些修改,以让其在今后的重用中变得更加方便。
那么,在构建自己的IP库时,具体要怎么修改这些HDL代码呢?请参见接下来的【提高设计的易改性】章节。

提高设计的易改性

写代码,免不了要进行修改,尤其是对于大规模的设计,没有人能够一次性成功,所以,修改代码是每个代码编写者的必做工作!那么,做为一名FPGA开发者,修改HDL代码往往也会在整个FPGA开发过程中占据着比较可观的时间比例,因此,如果能够想办法让自己的HDL代码更易于修改,将会对工作效率有着很大的提升。除此以外,提高设计的易改性还能同时大幅提高设计的重用性,因为重用过程通常都伴随有代码的修改。
对于FPGA开发来说,提高设计的易改性的主要方法就是模块的参数化设计思路,那么,下面就来介绍一些常用的模块参数化设计思路:

常量参数化模块设计

在进行HDL代码编写时,免不了要使用一些常量,例如某些信号的判决门限(也称阀值),某些控制信号电平的高有效或低有效等等。可是,当代码的修改涉及到这些常量时,如果这些常量在整个FPGA设计中仅仅出现一次,那么修改工作相对来说比较容易;但如果这些常量在整个FPGA设计中出现了很多次,那么修改工作就变得艰难且容易出错。
为了解决常数的修改问题,我们可以在HDL代码中定义一些参数,并将这些常数的值赋给这些参数,那么,在接下来的HDL编写中,凡是需要使用常数的地方,都以对应参数代替。这样一来,当需要修改某个常数时,我们只需要找到其对应参数的定义位置并修改其赋值情况即可。相比于非参数化的模块设计,无论有多少处代码使用到该参数,修改工作都只需要做一次即可,并且由于参数定义的位置相对来说比较固定,定位起来也相当容易。
下面就给出一个常量参数化模块设计的示例供大家参考:

-- VHDL example
	library IEEE;
	use IEEE.STD_LOGIC_1164.ALL;
	use IEEE.STD_LOGIC_ARITH.ALL;
	use IEEE.STD_LOGIC_UNSIGNED.ALL;

	entity ParaDesign0 is
	generic (
		RST_LEVEL : std_logic := '0';
		VT : integer := 200
	);
	port (
		clk : in  std_logic;
		rst : in  std_logic;
		data : in std_logic_vector(7 downto 0);
		detect: out std_logic
	);
	end ParaDesign0;

	architecture Behavioral of ParaDesign0 is
	begin
	process(clk)
	begin
		if(clk'event and clk = '1')then if(rst = RST_LEVEL)then detect <= '0'; else if(data > VT)then detect <= '1'; else detect <= '0'; end if; end if;
		end if;
	end process;
	end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
	// Verilog example
	module ParaDesign0
	#(parameter RST_LEVEL = 1'b0, VT = 8)
	(
	input clk,
	input rst,
	input [7:0] data,
	output reg detect
	);
	always@(posedge clk)
	begin
		if(rst == RST_LEVEL)
		begin detect <= 1'b0;
		end
		else
		begin if(data > VT) begin detect <= 1'b1; end else begin detect <= 1'b0; end
		end
	end
	endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在上例中,可以方便的修改复位的有效电平及输入的判决阀值。总的来说,常量参数化主要是利用VHDL的generic语法和Verilog的parameter语法,关于这两个语法的具体使用说明也请参考【本篇->编程语法】->【VHDL基本语法】、【Verilog基本语法】的相关章节。当然了,VHDL中还具有constant语法,也可以用作常量参数化,不过这种常量不具有例化覆盖性。

结构参数化模块设计

相对于常量参数化模块设计的“小修小补”,结构参数化模块设计要更为复杂一些,因为结构参数的变化会对模块的的硬件结构产生根本影响。结构参数化模块设计主要可分为三大类型——总线参数化、规模参数化及功能参数化,分别介绍如下:

总线参数化

总线参数化主要针对解决信号的位宽发生变化时的修改情况。例如,原先设计了一个8bits位宽的信号,现由于精度等问题,需要修改到10bits位宽,如果提前没有进行总线的参数化定义,那么修改起来将会变得非常的麻烦。
下面就给出一个总线参数化模块设计的示例供大家参考:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ParaDesign1 is
generic (
	DATA_WIDTH : integer := 8
);
port (
	clk : in  std_logic;
	dIn : in std_logic_vector(DATA_WIDTH-1  downto 0);
	dOut : out std_logic_vector(DATA_WIDTH-1  downto 0)
);
end ParaDesign1;

architecture Behavioral of ParaDesign1 is
begin
process(clk)
begin
	if(clk'event and clk = '1')then
		dOut <= dIn;
	end if;
end process;
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
// Verilog example
module ParaDesign1
#(parameter DATA_WIDTH = 8)
(
input clk,
input [DATA_WIDTH-1 : 0] dIn,
output reg [DATA_WIDTH-1 : 0] dOut
);
always@(posedge clk)
begin
	dOut <= dIn;
end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上例所编写的模块,在被实例化时,可以根据参数情况得到不同数据位宽的寄存器。可以看出,总线参数化主要也是利用VHDL的generic语法和Verilog的parameter语法,关于这两个语法的具体使用说明也请参考【本篇->编程语法】->【VHDL基本语法】、【Verilog基本语法】的相关章节。

规模参数化

规模参数化主要针对解决模块所对应硬件电路规模发生变化时的修改情况。例如,原先设计好了一个模块,其硬件结构中包含了10个功能处理子模块,现在由于一些原因,需要扩展到20个子模块才够用,这时,如果提前没有进行规模的参数化定义,那么修改起来将会变得非常繁琐。
由于规模参数化通常是针对子模块的数量来说,因此其参数化语法首选HDL中的循环生成语句,下面就给出一个利用循环生成语句实现的规模参数化模块设计的示例供大家参考:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ParaDesign2 is
generic (
	REG_NUM : integer := 10
);
port (
	clk : in  std_logic;
	dIn : in std_logic;
	dOut : out std_logic
);
end ParaDesign2;

architecture Behavioral of ParaDesign2 is
	signal wires : std_logic_vector(REG_NUM downto 0); COMPONENT MyReg
	PORT(
		clk : IN std_logic;
		dIn : IN std_logic; dOut : OUT std_logic
		);
	END COMPONENT;
begin
	wires(0) <= dIn;
	dOut <= wires(REG_NUM);

	shift_reg_gen :
	for i in 0 to REG_NUM-1 generate
	begin
		sonMod : MyReg	PORT MAP(clk => clk, dIn => wires(i), dOut => wires(i+1));
	end generate;
end Behavioral;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity MyReg is
port (
	clk : in std_logic;
	dIn : in std_logic;
	dOut : out std_logic
);
end MyReg;

architecture Behavioral of MyReg is
begin
	process(clk)
	begin
		if(clk'event and clk = '1')then dOut <= dIn;
		end if;
	end process;
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
// Verilog example
module ParaDesign2
#(parameter REG_NUM = 10)
(
input clk,
input dIn,
output dOut
);	
	wire [REG_NUM : 0] wires; assign wires[0] = dIn;
	assign dOut = wires[REG_NUM]; genvar i;
	generate
		for (i=0; i < REG_NUM; i=i+1) begin: shift_reg_gen MyReg sonMod(.clk (clk), .dIn (wires[i]), .dOut (wires[i+1]));
		end
	endgenerate
endmodule

module MyReg(
input clk,
input dIn,
output reg dOut
);
	always@(posedge clk)
	begin
		dOut <= dIn;
	end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

上例是一个关于移位寄存器的参数化示例,可以看出,基于生成语句的规模参数化主要是利用VHDL、Verilog中的循环generate语法,关于这两个语法的具体使用说明也请参考【本篇->编程语法】->【VHDL基本语法】、【Verilog基本语法】的相关章节。
不过,若模块中的功能单元并不是以子模块的形式出现,那么利用常量参数化与普通循环语法相结合的方式,也能够实现模块的规模参数化设计,例如:

-- VHDL example
	library IEEE;
	use IEEE.STD_LOGIC_1164.ALL;
	use IEEE.STD_LOGIC_ARITH.ALL;
	use IEEE.STD_LOGIC_UNSIGNED.ALL;

	entity ParaDesign3 is
	generic (
		REG_NUM : integer := 10
	);
	port (
		clk : in  std_logic;
		dIn : in std_logic;
		dOut : out std_logic
	);
	end ParaDesign3;

	architecture Behavioral of ParaDesign3 is signal regs : std_logic_vector(REG_NUM downto 0); 
	begin
		regs(0) <= dIn; dOut <= regs(REG_NUM); process(clk)
		begin if(clk'event and clk = '1')then for i in 0 to REG_NUM-1 loop regs(i+1) <= regs(i); end loop; end if;
		end process;
	end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
// Verilog example
	module ParaDesign3
	#(parameter REG_NUM = 10)
	(
	input clk,
	input dIn,
	output reg dOut
	); reg [REG_NUM : 0] regs;
	integer i; assign regs[0] = dIn;
	assign dOut = regs[REG_NUM]; always@(posedge clk)
	begin
		for (i = 0; i <= REG_NUM-1; i = i+1) begin regs[i+1] <= regs[i];
		end
	end
	endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

当然了,循环生成语句也可以针对具体的HDL功能代码,那么具体用那种方法来实现规模的参数化,主要取决于你嵌入参数化HDL代码的位置。如果在VHDL的process之外或Verilog的always之外,那么请使用循环生成语法,因为这类语法的语句属于并行语句,不能写于process或always之内;反之,则请使用普通循环语句,因为普通循环语句属于串行语句,不能脱离process或always等并行语句体而单独存在。

功能参数化

功能参数化主要针对解决模块所对应硬件电路功能发生本质变化时的修改情况。例如,原先设计好了一个模块,其内部用到了一个算法A,但现在由于一些原因,需要改用完全不同的算法B,这时,如果提前没有进行功能的参数化定义,那么修改起来也将会变得非常吃力。
功能参数化可以针对子模块,也可以针对功能代码,其参数化语法首选HDL中的条件生成语句,下面就给出一个利用条件生成语句实现的功能参数化模块设计的示例供大家参考:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ParaDesign4 is
generic (
	ARITH_TYPE : integer := 0
);
port (
	a : in  std_logic;
	b : in std_logic;
	c : out std_logic
);
end ParaDesign4;

architecture Behavioral of ParaDesign4 is
begin type0:
	if ARITH_TYPE = 0 generate
	begin c <= a and b;
	end generate; type1:
	if ARITH_TYPE = 1 generate
	begin c <= a or b;
	end generate; type2:
	if ARITH_TYPE = 2 generate
	begin c <= a xor b;
	end generate; type3:
	if ARITH_TYPE = 3 generate
	begin c <= a xnor b;
	end generate;		
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
// Verilog example
module ParaDesign4
#(parameter ARITH_TYPE = 2)
(
input clk,
input a,b,
output c
); generate
		case (ARITH_TYPE) 0: begin assign c = a & b; end 1: begin assign c = a | b; end 2: begin assign c = a ^ b; end 3: begin assign c = a ~^ b; end default: begin assign c = a & b; end
		endcase
	endgenerate
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

上例是一个关于在与、或、异或、同或四种功能中选一的功能参数化示例,可以看出,基于生成语句的功能参数化主要是利用VHDL、Verilog中的条件generate语法,关于这两个语法的具体使用说明也请参考【本篇->编程语法】->【VHDL基本语法】、【Verilog基本语法】的相关章节。
不过,利用常量参数化与普通条件语法相结合的方式,也能够实现模块的功能参数化设计,例如:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ParaDesign5 is
generic (
	ARITH_TYPE : integer := 0
);
port (
	a : in  std_logic;
	b : in std_logic;
	c : out std_logic
);
end ParaDesign5;

architecture Behavioral of ParaDesign5 is
begin process(a,b)
	begin
		case (ARITH_TYPE) is when 0 => c <= a and b;
		when 1 => c <= a or b;
		when 2 => c <= a xor b;
		when 3 => c <= a xnor b;
		when others => c <= a and b;
		end case;
	end process;		
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
// Verilog example
	module ParaDesign5
	#(parameter ARITH_TYPE = 0)
	(
	input a,b,
	output reg c
	); always@(a,b) begin case (ARITH_TYPE) 0: c = a & b; 1: c = a | b; 2: c = a ^ b; 3: c = a ~^ b; default: c = a & b; endcase
		end
	endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

当然了,条件生成语句也可以针对具体的HDL功能代码,那么具体用那种方法来实现规模的参数化,主要取决于你嵌入参数化HDL代码的位置。如果在VHDL的process之外或Verilog的always之外,那么请使用条件生成语法,因为这类语法的语句属于并行语句,不能写于process或always之内;反之,则请使用普通条件语句,因为普通条件语句属于串行语句,不能脱离process或always等并行语句体而单独存在。
最后,功能参数化模块在每次编译时,就确定了其具体的功能,因此,对于上例来说,如果ARITH_TYPE等于0,那么整个参数化代码可以化简如下:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ParaDesign5 is
port (
	a : in  std_logic;
	b : in std_logic;
	c : out std_logic
);
end ParaDesign5;

architecture Behavioral of ParaDesign5 is
begin process(a,b)
	begin
		c <= a and b;
	end process;		
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
// Verilog example
module ParaDesign5(
input a,b,
output reg c
); always@(a,b)	
	begin
		c = a & b;
	end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这样一来,如果想要c等于a与b的或运算,只有修改参数然后重新编译整个FPGA工程了。因此,功能参数化后的模块无法动态的改变自身的功能,但这样的好处就是整个模块在实现的时候非常节省资源,即只需要实现一个功能即可,而不需要所有功能都实现。

参数化设计的参数管理与组织

参数化设计一旦被广泛应用,若能注意一下使用方法,也会带来更好的效果。
首先,随着FPGA设计的渐渐参数化,FPGA设计中的参数个数也会越来越多,那么,其中有些参数之间往往是存在着一定联系的,或者为了代码的易于理解性,我们故意创造了一些存在一定联系的参数。那么这些存在关系的参数之间是否可以通过适当的定义来简化修改操作呢?
其次,当一个FPGA设计变得越来越复杂、越来越庞大时,你会发现,即使一些关键模块都已经单独采用了参数化的方式进行编程,但修改起来仍然不是特别方便。这其中最主要的一个问题就是参数定位问题,例如一个描述MCU的FPGA设计共有2000个HDL文件,其中一些关键文件都做了单独的参数化设计,若现在需要修改运算处理单元中除法器除数的位宽,那么从这2000个文件里找出针对于除法器的那个HDL文件想必也不是一件很容易的事情。尤其是当有多个参数需要修改,而这些参数又分布于不同的HDL文件中时,查找起来就更加麻烦了。
最后,有时候,多个模块中会用到意义完全相同的一些参数,之前的分布式参数定义往往需要为每个模块定义一套同样的参数,这也是种浪费。
因此,在这一小节中,我们将从整个FPGA设计着眼,进步一讨论参数化设计的问题。

参数相关性

先来看一下参数相关性问题。例如,乘法器的输入、输出的位宽就是具有一定关系的,你可能在HDL代码中参数化如下:

-- VHDL example
generic (
	INPUT_A_WIDTH : integer := 8;
	INPUT_B_WIDTH : integer := 8;
	OUTPUT_C_WIDTH : integer := 16;
);

// Verilog example
parameter INPUT_A_WIDTH = 8;
parameter INPUT_B_WIDTH = 8;
parameter OUTPUT_C_WIDTH = 16;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

但是注意,对于乘法器来说,乘积C的位宽显然等于输入A、B的位宽之和,因此在HDL代码中我们其实是可以使用INPUT_A_WIDTH + INPUT_B_WIDTH来代替OUTPUT_C_WIDTH参数的,但是这样做的结果显然会让HDL代码的编写变得十分麻烦,并且还会降低代码的可读性。不过如果定义了OUTPUT_C_WIDTH参数,每当INPUT_A_WIDTH或INPUT_B_WIDTH参数发生变化时,OUTPUT_C_WIDTH参数将会受到牵连,因此也必须同时修改,这样也略显麻烦。那么,是否可以在A、B的位宽参数发生变化时,让C的位宽参数的取值发生自动修改呢?当然可以,你只需简单修改上述参数化代码,新的参数化代码如下所示:

-- VHDL example
	generic (
		INPUT_A_WIDTH : integer := 8;
		INPUT_B_WIDTH : integer := 8;
		OUTPUT_C_WIDTH : integer := INPUT_A_WIDTH + INPUT_B_WIDTH;
	);

	// Verilog example
	parameter INPUT_A_WIDTH = 8;
	parameter INPUT_B_WIDTH = 8;
	parameter OUTPUT_C_WIDTH = INPUT_A_WIDTH + INPUT_B_WIDTH;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这样一来,在代码修改时,我们只需要关注那些最基本的参数值即可,而不需要所有的参数值都要修改一遍。
那么,由于参数的相关性,所以在某些参数的定义中就可以使用含有别的参数的算数表达式,不过有一点需要特别注意,那就是无论这个表达式有多么的复杂,完成其求解的都是编译器软件,而不是我们的FPGA硬件。因此,可以放心的在具有相关性的参数表达式中使用任何HDL语法支持的运算符号,例如乘法、除法、乘方等等,因为这丝毫不会影响FPGA设计的资源和复杂度。

可传递的模块参数化例化

可传递的模块参数化例化方法是将多个文件的修改集中于一个文件进行修改的方法。它主要是利用HDL语言在例化一个模块时,可以用新的参数值覆盖掉模块中原来默认的参数值的这样一个原理(在【本篇->编程语法】->【VHDL基本语法】、【Verilog基本语法】中的基本程序框架小节中都有介绍)。
例如,对于一个参数化的模块A,参数为p0,如果在其父模块B中定义一个参数p1,并在例化A时用p1的值覆盖掉p0的初始值,以此类推,在顶层模块中,便可以通过修改参数pN的值来完成对p0的修改。这样一来,整个FPGA设计的参数修改便都可以在顶层HDL文件中来完成,只要令pN、……、p1、p0的参数名称具有较强相关性,便可以极大简化整个FPGA设计在参数修改时的定位工作。

参数文件结构

可传递的模块参数化例化方法,当FPGA设计的层级较多,而参数化模块又处于较低的层级时,实现起来也是比较费劲的,并且这种方法无法简化多个模块需要相同参数的问题。因此,设计专门的参数文件能够更好的解决参数化设计时的参数管理与组织问题。
那么,相比于之前介绍的一些分布式参数定义方法,通过借鉴C语言等软件编程语言中头文件的思路,可以使用一个或一类固定的文件定义所有的参数,然后在每个单独的参数化模块中调用这个或这类文件中的某个文件,便可以使用其中定义的所有参数。
对于VHDL语言来说,这个或这类专门定义参数的文件就是自定义的库文件(其实是程序包),而对于Verilog语言来说,这个或这类专门定义参数的文件就叫其头文件,利用define指令定义参数,通过include指令便可将其包含进任何一个模块中去。
若对【结构参数化模块设计->功能参数化】小节中的例子加以修改,便可得到具有单独参数文件的示例如下:

-- VHDL example
-- ParaOfVHDL.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.all;

package ParaOfVHDL is
	constant ARITH_TYPE : integer := 0;
end ParaOfVHDL;

package body ParaOfVHDL is 
	-- nothing in there, because no functions or procedures is defined in this case! 
end ParaOfVHDL;

-- ParaDesign6.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

use work.ParaOfVHDL.all;

entity ParaDesign6 is
port (
	a : in  std_logic;
	b : in std_logic;
	c : out std_logic
);
end ParaDesign6;

architecture Behavioral of ParaDesign6 is
begin process(a,b)
	begin
		case (ARITH_TYPE) is when 0 => c <= a and b;
		when 1 => c <= a or b;
		when 2 => c <= a xor b;
		when 3 => c <= a xnor b;
		when others => c <= a and b;
		end case;
	end process;		
end Behavioral;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
// Verilog example
	// ParaOfVerilog.vh
	`define ARITH_TYPE 1

	// ParaDesign6.v
	`include "ParaOfVerilog.vh"
	module ParaDesign6(
	input a,b,
	output reg c
	); always@(a,b) begin case (`ARITH_TYPE) 0: c = a & b; 1: c = a | b; 2: c = a ^ b; 3: c = a ~^ b; default: c = a & b; endcase
		end
	endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/105023775

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。