VHDL基础:词法元素与语法规则详解
立即解锁
发布时间: 2025-08-16 01:28:28 阅读量: 2 订阅数: 11 


VHDL设计指南:从入门到精通
### VHDL基础:词法元素与语法规则详解
#### 1. 学习新语言的通用方法
学习一门新的自然语言,如希腊语、中文或英语,通常从学习字母开始,然后将字母组合成单词,接着学习如何将单词组合成句子并理解其含义。当我们能够用正确的句子轻松表达想法时,就达到了语言流利的程度。
学习像VHDL这样的专用语言也是如此。我们需要学习以下几个方面:
- **字母表**:VHDL的字母表由ISO 8859 Latin - 1 8位字符集中的所有字符组成,包括大小写字母(含带变音符号的字母)、数字0 - 9、标点符号和其他特殊字符。不过,VHDL - 87使用ASCII字符集,仅包括前128个字符,不含带变音符号的字母。
- **词法元素**:包括注释、标识符、保留字、特殊符号、数字、字符、字符串和位串。
- **语法**:决定哪些词法元素组合构成合法的VHDL描述的规则。
- **语义**:VHDL描述的含义,它使符号集合能够描述数字设计。
- **自描述开发**:学会用VHDL描述自己正在处理的设计,这是建模的创造性部分,熟练掌握这部分将大大提高设计技能。
#### 2. VHDL的词法元素
##### 2.1 注释
在VHDL中编写硬件模型时,添加注释非常重要,它有助于读者理解模型的结构和逻辑。
- **单行注释**:在一行中添加两个连字符“--”,后面跟上注释文本,注释从两个连字符延伸到行尾。例如:
```vhdl
... a line of VHDL code ...
-- a descriptive comment
```
也可以在连续的行上写长注释,每行以两个连字符开头:
```vhdl
-- The following code models
-- the control section of the system
... some VHDL code ...
```
- **多行注释**:以“/*”开头,以“*/”结尾,开头和结尾字符可以在不同行,也可以在同一行。例如:
```vhdl
/* This is a comment header that describes
the purpose of the design unit. It contains
all you ever wanted to know, plus more.
*/
entity thingumy is
port ( clk : in bit; -- keeps it going
reset : in bit /* start over */
/* other ports to be added later */ );
end entity thingumy;
```
需要注意的是,注释不能嵌套,VHDL - 87、 - 93和 - 2002版本只允许单行注释。
##### 2.2 标识符
标识符用于为VHDL模型中的项目命名。
- **基本标识符**:
- 只能包含字母(‘A’ - ‘Z’和‘a’ - ‘z’)、十进制数字(‘0’ - ‘9’)和下划线字符(‘_’)。
- 必须以字母开头。
- 不能以下划线结尾。
- 不能包含两个连续的下划线。
例如,有效的基本标识符有:A、X0、counter、Next_Value、generate_read_cycle;无效的基本标识符有:last@value(包含非法字符)、5bit_counter(以非字母开头)、_A0(以下划线开头)、A0_(以下划线结尾)、clock__pulse(两个连续下划线)。字母大小写在基本标识符中不区分,但下划线是有意义的。
- **扩展标识符**:可以包含任意字符序列,用‘\’字符将标识符字符括起来。例如:\data bus\、\global.clock\、\923\、\d#1\、\start__\。如果需要在扩展标识符中包含‘\’字符,需将其加倍,如\A:\\name\。字母大小写在扩展标识符中是有意义的,且所有扩展标识符与基本标识符不同。VHDL - 87只允许基本标识符。
##### 2.3 保留字
保留字或关键字在VHDL中有特殊用途,不能用作自定义项目的标识符。不同版本的VHDL保留字略有不同:
| VHDL版本 | 非保留字情况 |
| ---- | ---- |
| VHDL - 2002 | assert、fairness、restrict_guarantee等标识符不是保留字,但不建议用作其他用途,以免移植到VHDL - 2008时出现问题。 |
| VHDL - 93 | 除VHDL - 2002的非保留字外,protected也不是保留字。 |
| VHDL - 87 | 除VHDL - 2002和VHDL - 93的非保留字外,group、protected、ror等更多标识符不是保留字。 |
完整的VHDL保留字列表如下:
| 保留字 | 保留字 | 保留字 | 保留字 | 保留字 |
| ---- | ---- | ---- | ---- | ---- |
| abs | access | after | alias | all |
| and | architecture | array | assert | assume |
| assume_guarantee | attribute | begin | block | body |
| buffer | bus | case | component | configuration |
| constant | context | cover | default | disconnect |
| downto | else | elsif | end | entity |
| exit | fairness | file | for | force |
| function | generate | generic | group | guarded |
| if | impure | in | inertial | inout |
| is | label | library | linkage | literal |
| loop | map | mod | nand | new |
| next | nor | not | null | of |
| on | open | or | others | out |
| package | parameter | port | postponed | procedure |
| process | property | protected | pure | range |
| record | register | reject | release | rem |
| report | restrict | restrict_guarantee | return | rol |
| ror | select | sequence | severity | shared |
| signal | sla | sll | sra | srl |
| strong | subtype | then | to | transport |
| type | unaffected | units | until | use |
| variable | vmode | vprop | vunit | wait |
| when | while | with | xnor | xor |
##### 2.4 特殊符号
VHDL使用一些特殊符号来表示运算符、分隔语言结构部分和作为标点。
- **单字符符号**:"、#、&、'、(、)、*、+、-、,、.、/、:、;、<、=、>、?、@、[、]、`、|
- **双字符符号**:=>、**、:=、/=、>=、<=、<>、??、?=、?/=、?>、?<、?>=、?<=、<<、>>
##### 2.5 数字
VHDL中有两种数字形式:
- **整数文字**:表示整数,由无小数点的数字组成。例如:23、0、146。注意,像 - 10这样的数实际上是负号运算符和整数文字10的组合。
- **实数文字**:表示小数,必须包含小数点,小数点前后至少有一位数字。例如:23.1、0.0、3.14159。
整数和实数文字都可以使用指数表示法,用字母‘E’或‘e’后跟指数值。整数文字的指数不能为负,实数文字的指数可以为正或负。例如:
- **整数文字指数表示**:46E5、1E + 12、19e00
- **实数文字指数表示**:1.234E09、98.6E + 21、34.0e - 08
数字还可以用2 - 16进制表示,用‘#’字符将数字括起来,前面加上基数。例如,253可以表示为:2#11111101#、16#FD#、16#0fd#、8#0375#;0.5可以表示为:2#0.100#、8#0.4#、12#0.6#。
基于基数的文字也可以使用指数表示法,指数在结束‘#’字符后附加。为了提高长数字的可读性,可以使用下划线作为数字分隔符,但下划线不能出现在数字开头或结尾,也不能连续出现。例如:123_456、3.141_592_6、2#1111_1100_0000_0000#
##### 2.6 字符
字符文字用单引号括起来,标准字符集中的任何可打印字符(包括空格)都可以这样表示。例如:'A'、'z'、','、'''、' '
##### 2.7 字符串
字符串文字用双引号括起来,表示字符序列,可以包含任意数量的字符(包括零个),但必须在一行内。例如:"A string"、"A string can include any printing characters (e.g., &%@^*)."、"00001111ZZZZ"、""
如果需要在字符串中包含双引号,需将两个双引号连写。例如:"A string in a string: ""A string"". " 如果字符串太长无法放在一行,可以使用连接运算符“&”将子字符串连接起来。
##### 2.8 位串
位串文字表示位值的字符串,用双引号括起来的数字字符串,前面加上表示基数的字符:
- **二进制(B)**:例如,B"0100011"、B"10"、b"1111_0010_0001"、B""
- **八进制(O)**:每个数字代表三位,例如,O"372" 等价于 B"011_111_010"、o"00" 等价于 B"000_000"
- **十六进制(X)**:每个数字代表四位,例如,X"FA" 等价于 B"1111_1010"、x"0d" 等价于 B"0000_1101"
- **十进制(D)**:数字被解释为十进制数并转换为等效二进制值,例如,D"23" 等价于 B"10111"、D"64" 等价于 B"1000000"、D"0003" 等价于 B"11"
位串文字中可以包含非数字字符,在八进制中,非八进制数字字符扩展为三个该字符,十六进制中扩展为四个,二进制中直接表示。例如:O"3XZ4" 等价于 B"011XXXZZZ100"、X"A3--" 等价于 B"10100011--------"、X"0#?F" 等价于 B"0000####????1111"、B"00UU" 等价于 B"00UU"。但十进制位串文字中不允许非数字字符。
我们可以指定位串的精确长度,写在基数说明符之前。如果最终长度比数字隐含的长度长,在左边用‘0’填充;如果更短,且最左边的元素都是‘0’,则截断。
位串文字还可以指定是无符号还是有符号数:
- **无符号数**:使用UB、UO或UX作为基数说明符,与普通的B、O、X相同。扩展时用‘0’填充,截断时必须全为‘0’。十进制文字总是被解释为无符号数,只有D作为基数说明符。
- **有符号数**:使用SB、SO或SX作为基数说明符,扩展和截断规则基于二进制补码数的符号扩展和截断规则。例如:
```vhdl
10SX"71" -- equivalent to B"0001110001"
10SX"88" -- equivalent to B"1110001000"
10SX"W0" -- equivalent to B"WWWWWW0000"
6SX"16" -- equivalent to B"010110"
6SX"E8" -- equivalent to B"101000"
6SX"H3" -- equivalent to B"HH0011"
```
VHDL - 87、 - 93和 - 2002版本只允许B、O和X作为基数说明符,不允许无符号和有符号说明符、十进制说明符D,也不允许指定长度,且除了用于提高可读性的下划线外,不允许非数字字符。
#### 3. 语法描述
在后续内容中,我们将使用基于扩展巴科斯 - 诺尔范式(EBNF)的符号来描述VHDL的语法规则。EBNF将语言划分为语法类别,为每个类别编写规则,描述如何通过组合词法元素和其他类别的子句来构建该类别的VHDL子句。
EBNF规则使用“⇐”符号,左边是定义的语法类别,右边是模式。例如:
```
variable_assignment ⇐ target := expression ;
```
该规则表示“variable_assignment”类别中的VHDL子句由“target”类别子句、“:=”符号、“expression”类别子句和“;”符号组成。
EBNF还使用以下符号:
- **方括号“[ ]”**:表示可选部分,例如:
```
function_call ⇐ name [ ( association_list ) ]
```
表示函数调用由名称和可选的括号内关联列表组成。
- **花括号“{ }”和点号“…”**:表示子句可选且可重复多次。例如:
```
process_statement ⇐
process is
{ process_declarative_item }
begin
{ sequential_statement }
end process ;
```
表示进程可以包含零个或多个进程声明项和零个或多个顺序语句。
```
case_statement ⇐
case expression is
case_statement_alternative
{ … }
end case ;
```
表示case语句必须包含至少一个case语句替代项,且可以根据需要包含任意数量的额外替代项。
```
identifier_list ⇐ identifier { , … }
```
表示标识符列表由一个或多个标识符组成,如果有多个,用逗号分隔。
- **竖线“|”**:表示多个可选替代项,例如:
```
mode ⇐ in | out | inout
```
表示“mode”类别可以由in、out或inout中的一个保留字组成。
- **括号“( )”**:用于分组模式部分,避免歧义,例如:
```
term ⇐ factor { ( * | / | mod | rem ) factor }
```
明确表示一个因子后面可以跟一个运算符符号和另一个因子。
EBNF符号足以描述VHDL的完整语法,但VHDL描述通常还有与词法元素含义相关的其他约束。
### VHDL基础:词法元素与语法规则详解
#### 4. EBNF规则示例分析
为了更好地理解EBNF规则在VHDL语法描述中的应用,下面通过几个具体的例子进行详细分析。
##### 4.1 变量赋值语句规则
```
variable_assignment ⇐ target := expression ;
```
这个规则描述了变量赋值语句的结构。它表明一个有效的变量赋值语句,需要一个目标(`target`),接着是赋值符号 `:=`,然后是一个表达式(`expression`),最后以分号 `;` 结束。
例如,对于语句 `d0 := 25 + 6;`,我们可以根据规则来分析其合法性。首先,`d0` 作为目标,`25 + 6` 作为表达式,整个语句符合 `variable_assignment` 规则中规定的模式,所以它是一个合法的变量赋值语句。而像 `25 fred := x if := .` 这样的语句,由于不符合规则中规定的模式,无法对应到目标、赋值符号、表达式和分号的顺序,因此它不是一个合法的变量赋值语句。
##### 4.2 函数调用规则
```
function_call ⇐ name [ ( association_list ) ]
```
此规则说明函数调用由一个名称(`name`)和一个可选的括号内关联列表(`association_list`)组成。也就是说,一个函数调用可以只是一个简单的函数名,也可以是函数名后面跟着括号内的关联列表。
例如,对于函数 `func1`,`func1` 和 `func1(arg1 => value1, arg2 => value2)` 都可能是合法的函数调用形式。前者没有关联列表,后者包含了关联列表,都符合函数调用规则。
##### 4.3 进程语句规则
```
process_statement ⇐
process is
{ process_declarative_item }
begin
{ sequential_statement }
end process ;
```
进程语句规则规定了进程的结构。进程以 `process is` 开始,接着可以有零个或多个进程声明项(`process_declarative_item`),然后是 `begin` 关键字,之后可以有零个或多个顺序语句(`sequential_statement`),最后以 `end process ;` 结束。
例如:
```vhdl
process is
variable temp : integer; -- 进程声明项
begin
temp := 10; -- 顺序语句
-- 其他顺序语句
end process;
```
这个进程语句包含了一个进程声明项和一个顺序语句,符合进程语句的规则。
##### 4.4 case语句规则
```
case_statement ⇐
case expression is
case_statement_alternative
{ … }
end case ;
```
case语句规则要求case语句必须包含至少一个case语句替代项(`case_statement_alternative`),并且可以根据需要包含任意数量的额外替代项。
例如:
```vhdl
case select_signal is
when value1 =>
-- 执行语句1
when value2 =>
-- 执行语句2
when others =>
-- 执行其他情况的语句
end case;
```
这个case语句包含了多个case语句替代项,符合case语句规则。
#### 5. 语法规则的实际应用及注意事项
在实际使用VHDL进行数字系统设计时,掌握语法规则至关重要。以下是一些实际应用中的注意事项:
##### 5.1 错误检查
VHDL分析器期望输入的是有效的VHDL描述。如果我们不了解语法规则,分析器产生的错误消息可能会显得晦涩难懂。因此,在编写代码时,要时刻对照语法规则进行检查,确保代码的合法性。
例如,在编写变量赋值语句时,要注意目标、赋值符号、表达式和分号的顺序;在编写函数调用时,要注意关联列表的使用是否符合规则。
##### 5.2 规则的灵活运用
虽然语法规则是固定的,但在实际应用中可以根据需要灵活组合。例如,在编写复杂的进程语句时,可以根据设计需求添加多个进程声明项和顺序语句;在编写case语句时,可以根据不同的情况添加多个case语句替代项。
##### 5.3 避免常见错误
在使用EBNF规则描述的语法时,要注意避免一些常见的错误,如括号使用不当导致的歧义、可选部分的遗漏等。
例如,在使用 `term ⇐ factor { ( * | / | mod | rem ) factor }` 规则时,要正确使用括号,避免出现 `term ⇐ factor { * | / | mod | rem factor }` 这样的错误解释。
#### 6. 总结
本文详细介绍了VHDL的词法元素和语法规则。词法元素包括注释、标识符、保留字、特殊符号、数字、字符、字符串和位串等,不同的词法元素有各自的定义和使用规则,并且在不同版本的VHDL中可能存在差异。语法规则使用基于扩展巴科斯 - 诺尔范式(EBNF)的符号进行描述,通过各种符号和模式来规定如何组合词法元素和子句,形成合法的VHDL描述。
在实际应用中,我们需要熟练掌握这些词法元素和语法规则,以便能够准确、高效地使用VHDL进行数字系统设计。同时,要注意不同版本VHDL的差异,避免因版本不兼容而导致的问题。通过不断练习和实践,我们可以更好地运用VHDL,提高数字系统设计的能力。
以下是一个简单的mermaid流程图,展示了编写VHDL代码的基本流程:
```mermaid
graph LR
A[开始] --> B[了解设计需求]
B --> C[选择合适的词法元素]
C --> D[依据语法规则编写代码]
D --> E[检查代码合法性]
E --> F{代码是否合法?}
F -- 是 --> G[完成设计]
F -- 否 --> D
```
通过这个流程图,我们可以清晰地看到编写VHDL代码的步骤,从了解需求开始,选择合适的词法元素,依据语法规则编写代码,然后检查代码的合法性,直到代码合法为止,最终完成设计。
希望本文能够帮助读者更好地理解和掌握VHDL的词法元素和语法规则,为数字系统设计打下坚实的基础。
0
0
复制全文
相关推荐










