VHDL中泛型子程序的使用与实践
立即解锁
发布时间: 2025-08-16 01:28:52 阅读量: 2 订阅数: 11 


VHDL设计指南:从入门到精通
### VHDL 中泛型子程序的使用与实践
#### 1. 抽象数据类型(ADT)使用风险
在复杂的行为模型中使用抽象数据类型(ADT)有很多优势,但也存在一定风险。VHDL 无法隐藏 ADT 底层数据结构的具体细节,因为类型声明必须写在包声明中。这意味着 ADT 用户可能会利用这些信息,不通过 ADT 过程和函数来修改数据结构。例如,若一个 ADT 操作只是更新一个记录元素,用户可能会直接更新记录以避免过程调用的开销。然而,现代编译器和计算机使这种“优化”变得不必要,而且用户可能会无意中破坏数据结构。因此,在 VHDL 中使用 ADT 时,用户需要避免这种诱惑,遵守 ADT 接口中的约定,一点自律在建模过程中会带来显著的好处。
#### 2. 子程序中的泛型列表
泛型列表在子程序中允许我们描述可用于各种类型操作数的操作,就像在包中允许描述可复用的容器数据类型一样。
##### 2.1 带泛型列表的子程序语法
- **过程**:
```plaintext
subprogram_body ⇐
procedure identifier
[ generic ( generic_interface_list ) ]
[ [ parameter ] ( parameter_interface_list ) ] is
{ subprogram_declarative_part }
begin
{ sequential_statement }
end [ procedure ] [ identifier ] ;
```
- **函数**:
```plaintext
subprogram_body ⇐
[ pure I impure ]
function identifier
[ generic ( generic_interface_list ) ]
[ [ parameter ] ( parameter_interface_list ) ] return type_mark is
{ subprogram_declarative_item }
begin
{ sequential_statement }
end [ function ] [ identifier ] ;
```
带有泛型列表的子程序称为未实例化的子程序。可选关键字 `parameter` 可用于明确泛型列表和参数列表的分界,对于没有泛型的子程序通常省略该关键字,对于未实例化的子程序是否包含该关键字可根据个人喜好决定。
##### 2.2 子程序声明与实例化
VHDL 允许将子程序声明分为两部分,一部分仅包含头部,另一部分包含头部和主体。在实例化未实例化的子程序时,需要使用子程序实例化声明,例如:
- **过程实例化**:
```plaintext
subprogram_instantiation_declaration ⇐
procedure identifier is new uninstantiated_subprogram_name [ signature ]
[ generic map ( generic_association_list ) ] ;
```
- **函数实例化**:
```plaintext
subprogram_instantiation_declaration ⇐
function identifier is new uninstantiated_subprogram_name [ signature ]
[ generic map ( generic_association_list ) ] ;
```
实例化后,使用实例名称调用实例。如果所有形式泛型都有默认值,可以省略 `generic map` 以使用默认值。
##### 2.3 示例:通用交换过程
```vhdl
procedure swap
generic ( type T )
parameter ( a, b : inout T ) is
variable temp : T;
begin
temp := a; a := b; b := temp;
end procedure swap;
procedure int_swap is new swap
generic map ( T => integer );
procedure vec_swap is new swap
generic map ( T => bit_vector(0 to 7) );
variable a_int, b_int : integer;
variable a_vec, b_vec : bit_vector(0 to 7);
int_swap(a_int, b_int);
vec_swap(a_vec, b_vec);
```
需要注意,不能直接调用未实例化的 `swap` 过程,并且不能用无约束类型作为实际泛型类型来实例化 `swap` 过程,因为过程内部使用该类型声明了一个变量。
##### 2.4 示例:建立时间检查过程
```vhdl
package timing_pkg is
procedure check_setup
generic ( type signal_type;
type clk_type; clk_active_value : clk_type;
T_su : delay_length )
( signal s : signal_type; signal clk : clk_type );
...
end package timing_pkg;
package body timing_pkg is
procedure check_setup
generic ( type signal_type;
type clk_type; clk_active_value : clk_type;
T_su : delay_length )
( signal s : signal_type; signal clk : clk_type ) is
begin
if clk'event and clk = clk_active_value then
assert s'last_event >= T_su
report "Setup time violation" severity error;
end if;
end procedure check_setup;
...
end package body timing_pkg;
use work.timing_pkg.all;
procedure check_normal_setup is new check_setup
generic map ( signal_type => std_ulogic,
clk_type => std_ulogic,
clk_active_value => '1',
T_su => 200ps );
procedure check_normal_setup is new check_setup
generic map ( signal_type => std_ulogic_vector,
clk_type => std_ulogic,
clk_active_value => '1',
T_su => 200ps );
procedure check_long_setup is new check_setup
generic map ( signal_type => std_ulogic_vector,
clk_type => std_ulogic,
clk_active_value => '1',
T_su => 300ps );
signal status : std_ulogic;
signal data_in, result : std_ulogic_vector(23 downto 0);
check_normal_setup(status, clk);
check_normal_setup(result, clk);
check_long_setup(data_in, clk);
```
在这个例子中,时钟信号的活动值和建立时间间隔值被绑定到过程实例的定义中,不需要作为单独的参数提供。
##### 2.5 签名的使用
VHDL 允许重载子程序,并根据调用中参数的类型使用参数和结果类型配置文件来区分它们。当需要在调用之外命名子程序时,可以写一个签名来表明我们指的是哪个重载版本。例如,在别名声明中就可以使用签名。签名列出参数类型,对于函数还列出返回类型,都用方括号括起来。如果一个未实例化的子程序被重载,可以在实例化中包含一个签名来表明我们指的是哪个未实例化版本。例如:
```vhdl
procedure combine
generic ( type T )
parameter ( x : T; value : bit );
procedure combine
generic ( type T )
parameter ( x : T; value : integer );
procedure combine_vec_with_bit is new combine[T, bit]
generic map ( T => bit_vector );
```
##### 2.6 泛型映射子程序
和包一样,我们也可以在子程序中包含泛型映射。这样的子程序称为泛型映射子程序。
- **泛型映射过程语法**:
```plaintext
subprogram_body ⇐
procedure identifier
[ generic ( generic_interface_list )
[ generic map ( generic_association_list ) ] ]
[ [ parameter ] ( parameter_interface_list ) ] is
{ subprogram_declarative_part }
begin
{ sequential_statement }
end [ procedure ] [ identifier ] ;
```
- **泛型映射函数语法**:
```plaintext
subprogram_body ⇐
[ pure I impure ]
function identifier
[ generic ( generic_interface_list )
[ generic map ( generic_association_list ) ] ]
[ [ parameter ] ( parameter_interface_list ) ] return type_mark is
{ subprogram_declarative_item }
begin
{ sequential_statement }
end [ function ] [ identifier ] ;
```
通常我们不会显式编写泛型映射子程序,了解这个特性是为了在分析器产生看似神秘的错误消息时有所帮助。
#### 3. 泛型子程序
VHDL 允许声明泛型子程序,作为一种为单元提供要执行的操作或动作的方式。我们在泛型列表中声明一个形式泛型子程序,代表一个尚未指定的子程序,并在具有泛型列表的单元内包含对该形式泛型子程序的调用。当实例化该单元时,为该实例提供一个实际子程序。对形式泛型子程序的每次调用都代表对实例中实际子程序的调用。
##### 3.1 形式泛型子程序声明语法
- **过程**:
```plaintext
interface_declaration ⇐
procedure identifier
[ [ parameter ] ( parameter_interface_list ) ]
```
- **函数**:
```plaintext
interface_declaration ⇐
procedure identifier
[ [ parameter ] ( parameter_interface_list ) ] return type_mark
```
形式泛型子程序必须是一个简单的子程序,即它本身不能包含泛型列表。
##### 3.2 示例:带增量函数的通用计数器
```vhdl
entity generic_counter is
generic ( type count_type;
constant reset_value : count_type;
function increment ( x : count_type )
return count_type );
port ( clk, reset : in bit;
data : out count_type );
end entity generic_counter;
architecture rtl of generic_counter is
begin
count : process (clk) is
begin
if rising_edge(clk) then
if reset then
data <= reset_value;
else
data <= increment(data);
end if;
end if;
end process count;
end architecture rtl;
use ieee.numeric_std.all;
function add1 ( arg : unsigned ) return unsigned is
begin
return arg + 1;
end function add1;
signal clk, reset : bit;
signal count_val : unsigned(15 downto 0);
counter : entity work.generic_counter(rtl)
generic map ( count_type => unsigned(15 downto 0),
reset_value => (others => '0'),
increment => add1 )
port map ( clk => clk, reset => reset, data => count_val );
```
我们还可以为不同类型实例化这个计数器,例如为 `traffic_light_color` 类型:
```vhdl
type traffic_light_color is (red, yellow, green);
function next_color ( arg : traffic_light_color )
return traffic_light_color is
begin
if arg = traffic_light_color'high then
return traffic_light_color'low;
else
return traffic_light_color'succ(arg);
end if;
end function next_color;
signal east_light : traffic_light_color;
east_counter : work.generic_counter(rtl)
generic map ( count_type => traffic_light_color,
reset_value => red,
increment => next_color )
port map ( clk => clk, reset => reset, data => east_light );
```
##### 3.3 默认形式泛型子程序
当在泛型列表中声明形式泛型子程序时,可以指定一个默认子程序,在没有为实例提供实际泛型子程序时使用。
- **带默认值的形式泛型过程声明**:
```plaintext
interface_declaration ⇐
procedure identifier
[ [ parameter ] ( parameter_interface_list ) ]
is subprogram_name
```
- **带默认值的形式泛型函数声明**:
```plaintext
interface_declaration ⇐
function identifier
[ [ parameter ] ( parameter_interface_list ) ] return type_mark
is subprogram_name
```
所命名的子程序必须在该点可见。
##### 3.4 示例:包中的通用错误报告
```vhdl
package error_utility_pkg is
procedure report_error ( report_string : string;
report_severity : severity_level );
end package error_utility_pkg;
package body error_utility_pkg is
procedure report_error ( report_string : string;
report_severity : severity_level ) is
begin
report report_string severity report_severity;
end procedure report_error;
end package body error_utility_pkg;
package operations is
generic ( procedure error_action
( report_string : string;
report_severity : severity_level )
is work.error_utility_pkg.report_error );
procedure step1 ( ... );
...
end package operations;
package body operations is
procedure step1 ( ... ) is
begin
...
if something_is_wrong then
error_action("Something is wrong in step1", error);
end if;
...
end procedure step1;
...
end package body operations;
package reporting_operations is new work.operations;
use re
```
0
0
复制全文
相关推荐










