VHDL中的包、使用子句与解析信号详解
立即解锁
发布时间: 2025-08-16 01:28:50 阅读量: 6 订阅数: 10 

VHDL设计指南:从入门到精通
### VHDL 中的包、使用子句与解析信号详解
#### 包与使用子句
在 VHDL 编程中,包和使用子句是非常重要的概念。有时候,我们可能会遇到子程序声明冲突的情况。例如,当不同包中存在同名且参数类型相同的子程序时,按常理可能会认为声明冲突,导致这些子程序无法直接可见。但实际上,重载过程调用的消歧规则允许考虑命名关联中使用的参数名。
比如下面两个调用:
```vhdl
increment ( a => count_value, n => -1 );
increment ( c => count_value, n => -1 );
```
第一个调用指向 `int_ops` 包中的 `increment` 过程,第二个调用指向 `counter_ops` 包中的 `increment` 过程。不过,要使这个规则生效,两个子程序名必须直接可见。如果使用位置关联,调用可能会产生歧义。
在 VHDL - 87、 - 93 和 - 2002 版本中,对于一个包中隐式声明的预定义操作与另一个包中显式声明的重载版本之间的冲突,处理方式与 VHDL - 2008 不同。在这些早期版本中,两者都会直接可见,需要使用重载解析规则来消除调用的歧义。而且,有一种广泛但错误的观点认为,从不同包中使用的同名且参数类型相同的子程序版本会冲突,并且不会直接可见。有些工具会按照这种观点实现,而不是像上面 `increment` 过程那样使两个子程序都直接可见。
下面是一些相关的练习题及解答思路:
1. **发动机管理系统包声明**:编写一个用于发动机管理系统模型的包声明,包含物理类型 `engine_speed`(单位为每分钟转数 RPM)、常量 `peak_rpm`(值为 6000 RPM)和枚举类型 `gear`(表示一挡、二挡、三挡、四挡和倒挡)。假设该包已分析并存储在当前工作库中,还需写出包中声明的每个项的选定名称。
- 包声明示例:
```vhdl
package engine_management is
type engine_speed is range 0 to integer'high units
rpm = 1;
end units;
constant peak_rpm : engine_speed := 6000 rpm;
type gear is (first, second, third, fourth, reverse);
end package engine_management;
```
- 选定名称示例:`work.engine_management.engine_speed`、`work.engine_management.peak_rpm`、`work.engine_management.gear`
2. **整数递增过程声明**:编写一个用于递增整数的过程声明,该声明应出现在包声明中。
```vhdl
package integer_ops is
procedure increment (variable num : inout integer);
end package integer_ops;
package body integer_ops is
procedure increment (variable num : inout integer) is
begin
num := num + 1;
end procedure increment;
end package body integer_ops;
```
3. **判断整数是否为奇数的函数声明**:编写一个用于测试整数是否为奇数的函数声明,该声明应出现在包声明中。
```vhdl
package integer_tests is
function is_odd (num : integer) return boolean;
end package integer_tests;
package body integer_tests is
function is_odd (num : integer) return boolean is
begin
return (num mod 2 /= 0);
end function is_odd;
end package body integer_tests;
```
4. **实数常量 `e` 的延迟常量声明**:编写实数常量 `e = 2.71828` 的延迟常量声明。
```vhdl
package math_constants is
constant e : real;
end package math_constants;
package body math_constants is
constant e : real := 2.71828;
end package body math_constants;
```
5. **包体需求判断**:判断练习题 1 中描述的包声明是否需要包体。在练习题 1 的包声明中,没有包含需要在包体中实现的子程序,所以不需要包体。
6. **使用子句编写**:编写一个使用子句,使练习题 1 中描述的 `engine_speed` 类型直接可见。
```vhdl
use work.engine_management.engine_speed;
```
7. **上下文子句编写**:编写一个上下文子句,使库 `DSP_lib` 可访问,并使实体 `systolic_FFT` 和库中包 `DSP_types` 中声明的所有项直接可见。
```vhdl
context work is
library DSP_lib;
use DSP_lib.systolic_FFT;
use DSP_lib.DSP_types.all;
end context work;
```
8. **处理时间值的包声明和包体开发**:开发一个包声明和包体,用于处理时间值。包将时间值定义为一个记录,包含自午夜以来的小时、分钟和秒,并提供表示午夜和中午的延迟常量。包提供的操作包括比较(`<`、`>`、`<=` 和 `>=`)、时间值与秒数的加法以得到时间结果,以及两个时间值的减法以得到秒数结果。
```vhdl
package time_operations is
type time_of_day is record
hours : integer range 0 to 23;
minutes : integer range 0 to 59;
seconds : integer range 0 to 59;
end record;
constant midnight : time_of_day := (0, 0, 0);
constant midday : time_of_day := (12, 0, 0);
function "<" (t1, t2 : time_of_day) return boolean;
function ">" (t1, t2 : time_of_day) return boolean;
function "<=" (t1, t2 : time_of_day) return boolean;
function ">=" (t1, t2 : time_of_day) return boolean;
function "+" (t : time_of_day, s : integer) return time_of_day;
function "-" (t1, t2 : time_of_day) return integer;
end package time_operations;
package body time_operations is
-- 实现比较函数
function "<" (t1, t2 : time_of_day) return boolean is
begin
if t1.hours < t2.hours then
return true;
elsif t1.hours = t2.hours then
if t1.minutes < t2.minutes then
return true;
elsif t1.minutes = t2.minutes then
return t1.seconds < t2.seconds;
end if;
end if;
return false;
end function "<";
-- 其他比较函数和加法、减法函数实现类似,此处省略
end package body time_operations;
```
9. **处理标识符字符串的包声明和包体开发**:开发一个包声明和包体,用于处理表示标识符的字符串。包声明大纲如下:
```vhdl
package identifier_pkg is
subtype identifier is string(1 to 15);
constant max_table_size : integer := 50;
subtype table_index is integer range 1 to max_table_size;
type table is array (table_index) of identifier;
procedure to_lowercase (str : inout identifier);
procedure search (id : in identifier; tbl : in table; found : out boolean; pos : out table_index);
end package identifier_pkg;
package body identifier_pkg is
procedure to_lowercase (str : inout identifier) is
begin
for i in str'range loop
if str(i) >= 'A' and str(i) <= 'Z' then
str(i) := character'val(character'pos(str(i)) + 32);
end if;
end loop;
end procedure to_lowercase;
procedure search (id : in identifier; tbl : in table; found : out boolean; pos : out table_index) is
begin
found := false;
for i in tbl'range loop
if tbl(i) = id then
found := true;
pos := i;
exit;
end if;
end loop;
end procedure search;
end package body identifier_pkg;
```
#### 解析信号
在之前的章节中,我们一直避免考虑多个输出端口连接到一个信号的情况。当多个源驱动一个信号时,问题就在于确定该信号的最终值。VHDL 提供了解析信号机制来处理这种情况。
##### 基本解析信号
在实际的数字系统中,如果有两个输出驱动一个信号,我们可以根据一些模拟电路理论来确定结果值。信号会被驱动到某个中间状态,这取决于冲突驱动源的驱动能力。这个中间状态可能是也可能不是有效的逻辑状态。通常,在设计中我们只会在最多只有一个输出处于活动状态,其余输出处于高阻抗状态时才连接输出。在这种情况下,结果值应该是单个活动输出的驱动值。此外,我们还会包含某种“上拉”机制,以确定所有输出都不活动时信号的值。
虽然这种简单的方法适用于某些模型,但在其他情况下,我们需要更进一步。模拟设计模型的一个原因是检测错误,例如多个同时活动的连接输出。在这种情况下,我们需要扩展简单的方法来检测此类错误。当我们在更高的抽象级别进行建模并使用更复杂的类型时,另一个问题就出现了。我们需要明确将枚举类型的多个输出连接在一起意味着什么。
VHDL 采用了一种非常通用的方法:该语言要求设计者精确指定连接多个输出所产生的值。这是通过解析信号实现的,解析信号是我们在前面章节中使用的基本信号的扩展。解析信号在其定义中包含一个函数,称为解析函数,用于根据其所有源的值计算信号的最终值。
下面通过一个例子来说明其工作原理。我们可以使用对预定义类型 `bit` 的简单扩展来模拟三态输出驱动的值,例如:
```vhdl
type tri_state_logic is ('0', '1', 'Z');
```
额外的值 `'Z'` 用于表示输出处于高阻抗状态。接下来,我们需要编写一个函数,该函数接受这种类型的值的集合(表示多个输出驱动的值),并返回要应用到连接信号的结果值。在这个例子中,我们假设最多只有一个驱动源处于活动状态(`'0'` 或 `'1'`),其余驱动源都驱动 `'Z'`。编写这个函数的难点在于我们不应将其限制为固定数量的输入值。我们可以通过给它一个单一参数来避免这个问题,该参数是一个无约束的 `tri_state_logic` 值数组,定义如下:
```vhdl
type tri_state_logic_array is
array (integer range <>) of tri_state_logic;
```
解析函数的声明如下:
```vhdl
function resolve_tri_state_logic
( values : in tri_state_logic_array )
return tri_state_logic is
variable result : tri_state_logic := 'Z';
begin
for index in values'range loop
if values(index) /= 'Z' then
result := values(index);
end if;
end loop;
return result;
end function resolve_tri_state_logic;
```
创建解析信号的最后一步是声明信号,如下所示:
```vhdl
signal s1 : resolve_tri_state_logic tri_state_logic;
```
这个声明几乎与普通信号声明相同,但在信号类型之前添加了解析函数名。信号仍然采用 `tri_state_logic` 类型的值,但包含函数名表示该信号是一个解析信号,命名的函数作为解析函数。`s1` 是解析信号意味着在设计中我们可以有多个源驱动它(源包括进程内的驱动源和与信号关联的组件的输出端口)。当为信号安排一个事务时,值不会直接应用到信号上。相反,连接到信号的所有源的值,包括事务中的新值,会形成一个数组并传递给解析函数。函数返回的结果然后作为信号的新值应用到信号上。
下面是描述上述示例中 VHDL 机制的语法规则:
```plaintext
subtype_indication ⇐
```
0
0
复制全文


