VHDL中共享变量、受保护类型与属性的深入解析
立即解锁
发布时间: 2025-08-16 01:28:55 阅读量: 7 订阅数: 11 


VHDL设计指南:从入门到精通
# VHDL中共享变量、受保护类型与属性的深入解析
## 1. VHDL - 93中的共享变量
在VHDL - 93里,共享变量不必是受保护类型,它们类似于普通的非共享变量。共享变量可以声明为除文件类型之外的任何类型,并且声明时可以包含初始值表达式。共享变量的值能在表达式中使用,也可以通过普通的变量赋值语句进行更新。不过,在给定的仿真周期内,不同进程对共享变量的访问没有互斥机制,这意味着不同进程的读写操作可能会相互干扰。所以,在VHDL - 93中使用共享变量时,必须格外小心,确保每个仿真周期内只有一个进程能访问每个共享变量。如果一个模型依赖于在任何仿真周期内被多个进程访问的共享变量的值,那么该模型会被视为错误。
## 2. 受保护类型中的未实例化方法
### 2.1 未实例化子程序与受保护类型的关系
存在两种情况需要考虑:
- **将未实例化子程序的实例声明为受保护类型的方法**:若在受保护类型外部声明了一个未实例化子程序,并在受保护类型声明内声明该子程序的一个实例,那么这个实例就成为了受保护类型的一个方法。示例代码如下:
```vhdl
procedure uninstantiated_name
generic ( ... )
parameter ( ... );
type PT is protected
...
procedure instance_name is new uninstantiated_name
generic map ( ... );
...
end protected PT;
shared variable SV : PT;
...
SV.instance_name ( ... );
```
乍一看,这种方案似乎没有什么意义,因为未实例化子程序在受保护类型外部,无法引用受保护类型封装的项。但我们可以通过将受保护类型的一个方法作为实际的通用子程序提供给实例,从而实现对封装项的受控访问。改进后的方案如下:
```vhdl
procedure uninstantiated_name
generic ( ...; formal_generic_subprogram; ... )
parameter ( ... );
type PT is protected
method_declaration;
procedure instance_name is new uninstantiated_name
generic map ( ..., method_name, ... );
...
end protected PT;
```
在这个方案中,该方法可以访问受保护类型内的封装项。当实例调用实际的通用子程序时,就会调用该方法。
### 2.2 示例:带跟踪功能的测试向量集
假设有一个未实例化子程序,它能获取与指定时间对应的测试向量值,并将该向量值写入标准输出文件。该过程有一个形式通用子程序,代表获取测试向量的操作。代码如下:
```vhdl
procedure trace_test_vector is
generic ( impure function get_test_vector
( vector_time : time )
return test_vector )
parameter ( vector_time : time ) is
variable vector : test_vector;
use std.textio.all;
variable L : line;
begin
write(L, now);
write(L, string'(": "));
vector := get_test_vector(vector_time);
... -- write test vector
writeline(output, L);
end procedure trace;
```
我们可以声明一个受保护类型,代表要在不同时间应用的一组测试向量。该受保护类型有一个方法,用于获取特定时间的测试向量。我们将`trace_test_vector`过程的一个实例作为方法,用于跟踪受保护类型的共享变量所代表的特定测试向量集。受保护类型声明如下:
```vhdl
type test_set is protected
...
impure function get_vector_for_time ( vector_time : time )
return test_vector;
procedure trace_for_time is new trace_test_vector
generic map ( get_test_vector => get_vector_for_time );
end protected test_set;
```
我们可以声明该受保护类型的两个共享变量,代表两组不同的测试向量:
```vhdl
shared variable main_test_set, extra_test_set : test_set;
```
如果调用其中一个共享变量的`trace_for_time`方法:
```vhdl
main_test_set.trace_for_time(100 ns);
```
`trace_test_vector`过程的实例会调用为受保护类型实例提供的实际子程序,即调用与共享变量`main_test_set`关联的`get_vector_for_time`方法。同理,调用另一个共享变量的`trace_for_time`方法时,也会调用相应的`get_vector_for_time`方法。这表明受保护类型的每个共享变量将其`get_vector_for_time`方法(该方法可以访问共享变量的状态)绑定为`trace_test_vector`过程实例中的实际通用过程,因此该实例可以间接访问共享变量的状态。
### 2.3 在受保护类型内声明未实例化子程序
受保护类型内声明的未实例化过程本身不是一个方法,因为它不能被调用。但是,它可以在受保护类型内实例化以提供一个方法。而且,受保护类型的每个共享变量在逻辑上都包含该未实例化子程序的单独声明。这个子程序可以被实例化,从而得到一个可以访问共享变量中封装项的子程序。
#### 示例:带访问者遍历的激励列表
对于需要有符号激励值的设计,我们可以声明一个用于将有符号值显示到标准输出文件的过程:
```vhdl
procedure output_signed ( value : in signed ) is
use std.textio.all;
variable L : line;
begin
write(L, value);
writeline(output, L);
end procedure output_signed;
```
我们还声明一个用于有符号激励值列表的受保护类型:
```vhdl
type signed_stimulus_list is protected
...
procedure traverse_with_in_parameter
generic ( procedure visit ( param : in signed ) );
procedure output_all is new traverse_with_in_parameter
generic map ( visit => output_signed );
end protected signed_stimulus_list;
```
该受保护类型包含一个未实例化过程`traverse_with_in_parameter`,用于将一个访问者过程应用于有符号值列表中的每个元素。它实例化该遍历过程以提供一个显示每个元素的方法。我们可以使用这个受保护类型声明一个共享变量,然后调用该方法来显示其元素值:
```vhdl
shared variable list1 : signed_stimulus_list;
...
list1.output_all;
```
如果我们想使用遍历过程来累积列表中元素的总和,以便计算平均值,可以提供另一个操作过程,并在遍历过程的进一步实例化中使用它:
```vhdl
variable sum, average : signed(31 downto 0);
variable count : natural := 0;
procedure accumulate_signed ( value : in signed ) is
begin
sum := sum + value;
count := count + 1;
end procedure accumulate_signed;
procedure accumulate_all_list1 is
new list1.traverse_with_in_parameter
generic map ( visit => accumulate_signed );
...
accumulate_all_list1;
average := sum / count;
```
在这种情况下,实例是在受保护类型外部声明的一个过程。但由于它是在共享变量`list1`内定义的子程序的一个实例,所以该实例可以访问`list1`中封装的项。实例`accumulate_all_list1`会将`accumulate_signed`访问者过程应用于`list1`中的每个元素。
如果我们想计算任何元素列表的平均值,需要将这些声明封装在一个以共享变量为参数的过程中,包括在外部过程内声明遍历过程的实例。完整的过程如下:
```vhdl
procedure calculate_average
( variable list : inout signed_stimulus_list
variable average : out signed ) is
variable sum : signed(average'range);
variable count : natural := 0;
procedure accumulate_signed ( value : in signed ) is
begin
sum := sum + value;
count := count + 1;
end procedure accumulate_signed;
procedure accumulate_all is
new list.traverse_with_in_parameter
generic map ( visit => accumulate_signed );
begin
accumulate_all;
average := sum / count;
end procedure calculate_average;
```
### 2.4 练习
以下是一些相关练习:
1. 假设在VHDL - 93中,在一个包`instrumentation`中声明了一个共享变量:
```vhdl
shared variable multiply_counter : natural := 0;
```
有一个行为乘法器模型的两个实例`m1`和`m2`,其中包含语句:
```vhdl
instrumentation.multiply_counter
:= instrumentation.multiply_counter + 1;
```
请说明在VHDL - 93中,如果允许这两个实例在同一仿真周期内访问该变量,变量可能会被错误更新的情况。
2. 编写一个受保护类型声明和主体,为一个整数共享变量提供互斥机制。该受保护类型应包含一个名为`set`的方法来更新变量值,以及一个名为`get`的方法来检索变量值。
3. 第12.3.2节中的有界缓冲区抽象数据类型(ADT)可用于将字节流从一个进程发送到另一个进程。但是,如果发送者和接收者同时调用写和读过程,可能会干扰并导致缓冲区状态损坏。开发一个包,定义一个受保护的有界缓冲区的受保护类型。受保护类型主体应封装第12.3.2节中有界缓冲区ADT的一个变量。受保护类型应包含测试缓冲区是否已满、是否为空以及读写字节的方法。在一个架构主体中使用生产者和消费者进程,通过该受保护有界缓冲区类型的共享变量进行通信,测试你的包。
4. 我们可以使用受保护类型来封装抽象数据类型的私有数据,从而对类型的用户隐藏这些数据。例如,我们可以声明一个随机数流类型:
```vhdl
type random_generator is protected
impure function next_random return real;
impure function next_random ( min, max : real )
return real;
impure function next_random ( min, max : integer )
return integer;
...
end protected random_generator;
```
受保护类型主体包括用于存储随机种子的变量声明。这些方法结合`math_real.uniform`过程使用种子来生成连续的随机数,每个方法会对结果进行缩放和类型转换。开发一个包,完成该受保护类型的定义,包括返回`bit_vector`和`std_ulogic_vector`类型值的方法。在一个模型中使用你的包,为一个32位加法器生成随机激励值进行测试。
## 3. VHDL中的预定义属性
### 3.1 属性概述
VHDL提供了全面的机制来表达设计的行为和结构,同时还提供了属性机制,用于为模型添加额外的信息。以下是对预定义属性的总结:
### 3.2 不同类型的预定义属性
#### 3.2.1 标量类型的属性
这些属性提供有关标量类型中值的信息,具体如下表所示:
| 属性 | T的类型 | 结果类型 | 结果 |
| ---- | ---- | ---- | ---- |
| T'left | 任何标量类型或子类型 | 与T相同 | T中的最左值 |
| T'right | 任何标量类型或子类型 | 与T相同 | T中的最右值 |
| T'low | 任何标量类型或子类型 | 与T相同 | T中的最小值 |
| T'high | 任何标量类型或子类型 | 与T相同 | T中的最大值 |
| T'ascending | 任何标量类型或子类型 | boolean | 如果T是升序范围,则为true,否则为false |
| T'image(x) | 任何标量类型或子类型 | string | 类型T的值x的文本表示 |
| T'value(s) | 任何标量类型或子类型 | T的基类型 | 由字符串s表示的T中的值 |
| T'pos(s) | 任何离散或物理类型或子类型 | 通用整数 | x在T中的位置编号
0
0
复制全文
相关推荐









