VHDL 笔记 1 —— 电路设计
Posted on 2014-09-16 14:56 in IC
Code Structure
- 
一段独立的 VHDL 代码一般至少由 3 部分组成: LIBRARY declarations、ENTITY、ARCHITECTURE
- 
Library 用来设计重用和代码共享,使代码结构更清晰 1 2 LIBRARY library-name; USE library-name.package-name.package-parts;- 
常用的 3 个 Library: ieee、std、work
- 
其中 std 和 work 是默认可见的,不需声明,ieee 需要明确的声明 
 
- 
- 
Entity 描述电路的输入 / 输出引脚 1 2 3 4 5 6 ENTITY entity-name IS PORT ( port-name: signal-mode signal-type; port-name: signal-mode signal-type; ...); END entity-name;- 
singal-mode 可以是 4 种类型: inoutinoutbuffer
- 
OUT模式无法回读到电路内部,Buffer模式可以,但是 buffer 不能连接到其他类型的端口,即不能把该模块作为子模块例化,一般使用中间缓冲信号,解决回读问题。
 
- 
- 
Architecture 描述电路的行为和实现的功能 - 
Architecture 包含两部分:声明部分和代码部分 
- 
声明部分(可选)用来声明信号、常量等 
- 
代码部分(begin ... end)描述电路行为 
 
- 
- 
注释行用 --开始
- 
VHDL 不区分大小写 
Data Types
前面的 Entity 中的端口定义:
| 1 |  | 
还有其它地方声明的信号 signal :
| 1 |  | 
还有 常量 constant 声明:
| 1 |  | 
还有 变量 variable 声明:
| 1 |  | 
这些声明中都包含了数据类型字段。一个信号 / 常量 / 变量的数据类型决定了它能取到什么样的值,还有可以进行什么样的操作。
Pre-defined Data Types
IEEE 1164 标准中包含了一些预先定义的数据类型。
- 
std库中的standard包集 (package) 定义了:bit、boolean、integer、real类型
- 
ieee库中的std-logic-1164包集定义了:std-logic、std-ulogic类型
- 
ieee库中的std-logic-arith包集定义了:signed、unsigned类型,还有一些数据类型转换函数
- 
ieee库中的std-logic-signed和std-logic-unsigned包集:包含一些函数,可以使std-logic-vector类型的数据可以像signed和unsigned一样进行运算
bit & bit-vector
- 用 '0' 和 '1' 赋值1 2 3 4 5 signal x : bit; signal y : bit-vector (3 downto 0); x <= '1'; y <= "0011"
std-logic & std-logic-vector
- 
ieee 1164标准中引入的 8 逻辑值系统
- 
不同于 bit 类型,可以取 8 种不同的值,但只有 0、1、Z是可综合的,其他 5 种用来仿真
std-ulogic & std-ulogic-vector
- 
ieee 1164标准中定义的具有 9 种逻辑值的数据类型
- 
std-logic是std-ulogic的子集
boolean
- 只有两种取值:true、false
integer
- 32 位的整数 (-2 147 483 647 ~ +2 147 483 647)
natural
- 非负整数 (0 ~ +2 147483 647)
real
- 实数,不可综合
physical literal
- 表示物理量,不可综合
character
- 单一 / 一串 ASCII 字符
signed & unsigned
- 
ieee库中的std-logic-arith包中定义的数据类型
- 
和 std-logic-vector类似,但是可以支持与整数类似的算术运算。
User-defined Data Types
| 1 2 3 4 5 6 |  | 
Subtypes
| 1 2 |  | 
Arrays
- 
可以认为 VHDL 预定义的数据类型只有 scalar(single bit) 和vector(one-dimensional array of bits) 两种类型。
- 
这两种类型中只有一下类型是可综合的: - 
scalars: bit, std-logic, std-ulogic, boolean 
- 
vectors: bit-vector, std-logic-vector, std-ulogic-vector, integer, signed, unsigned 
 
- 
- 
syntax: 1TYPE type-name IS ARRAY (specification) OF data-type;
- 
example: 1 2 3 4 --1D array TYPE matrix IS ARRAY (0 TO 3) OF STD-LOGIC-VECTOR (7 DOWNTO 0); --2D array TYPE matrix2D IS ARRAY (0 TO 3, 7 DOWNTO 0) OF STD-LOGIC;
Port Array
有时在定义端口时,需要把端口定义为矢量阵列。但是在 Entity 中不允许使用 type 定义,所以我们必须自己定义包集 (package),然后使用 use 声明使用该用户自定义的包集,最后才能在 Entity 中使用这种新定义的类型。
Signed and Unsigned Data Types
- 
ieee库中的std-logic-arith包中定义了有符号数 (signed) 和无符号数 (unsigned) 两种数据类型。
- 
只有先声明使用这个库下的包,才能在代码中使用 signed/unsigned 1 2 3 4 use ieee.std-logic-arith.all; signal x signed (7 downto 0); signal y unsigned (0 to 3);
- 
使用它们主要是为了进行算术运算,但是它们不支持逻辑运算。( std-logic-vector 不支持算术运算,但是支持逻辑运算 ) 
- 
如果信号的类型只能是 std-logic-vector,那么通过其他方法也是可以进行算术运算的,解决方案就是声明使用 ieee的std-logic-unsigned和std-logic-signed两个包集,声明之后,std-logic-vector 就可以像 signed/unsigned 一样进行算术运算了。1 2 3 4 5 6 7 8 9 use ieee.std-logic-signed.all; -- use ieee.std-logic-unsigned.all; signal a in std-logic-vector (7 downto 0); signal b in std-logic-vector (7 downto 0); signal x out std-logic-vector (7 downto 0); x <= a + b; --legal, arithmetic x <= a and b; --legal, logiccal- 需要注意的是,这两个包不能同时存在于同一份代码中,因为这样会引入二义性。比如上面例子中的 “+” 运算,如果我们同时包含了这两个包集,那么编译器不知道我们定义的运算到底应该重载哪一个,综合时会报错。
 
Data Conversion
- 
在 VHDL 中,不同类型的数据是不能直接进行算术 / 逻辑运算的,所以必要时必须进行类型转换操作。 
- 
有两种方法实现类型转换: - 
使用包中预定义的数据类型转换函数 
- 
手动写一段专门用于数据类型转换的代码 
 
- 
- 
std-logic-arith中包含了很多数据类型转换函数,可以实现不同数据之间的转换。
Operators and Attributes
VHDL 语法虽然枯燥无味,但是只有对数据类型、运算操作符及其属性有了深刻认识,才能写出高质量和高效率的代码。
Opreators
- 
VHDL 提供了 6 种预定义的预算符: - 
赋值 assignment 
- 
逻辑 logical 
- 
算术 arithmetic 
- 
关系 relational 
- 
移位 shift 
- 
并置 concatenation 
 
- 
assignment
一共 3 种:
- 
<=用于给signal对象赋值
- 
:=用于给variable,constant,generic赋值,还可用于赋初值
- 
=>用于给矢量 (vector) 对象的某些位赋值,常和others一起使用
logical
进行逻辑运算,操作数必须是 bit, std-logic, std-ulogic 类型或者它们的扩展,即bit-vector, std-logic-vector, std-ulogic 类型。
- NOT,- AND,- OR,- NAND,- NOR,- XOR
arithmetic
- 
操作数是 signed,unsigned,integer,real,其中real类型是不可综合的
- 
如果声明了 std-logic-signed或者std-logic-unsigned,则std-logic-vector类型也可以进行加减运算。
- +,- -,- *,- /,- **,- MOD,- REM,- ABS
comparison
一共有 6 种:=, /=, >, <, >=, <=
shift
VHDL93 中引入的操作,语法:
| 1 |  | 
- 
left operand 必须是 bit-vector类型
- 
right operand 必须是 integer类型
- 
shift operator 有: sll,srl,sla,sra,rol,ror
concatenation
用于位的拼接。
- 
操作数:任何支持逻辑运算的数据类型 
- 
操作符: &,(, , ,)
Attributes
VHDL 中的属性语句可以获得相关数据 / 对象 的信息,使代码更加灵活。
Pre-defined
内置的预定义属性可以分为两大类:数值类属性 和 信号类属性。
- 
data attributes 
- 
signal attributes 大多数信号类属性都是不可综合的,只有 s'event和s'stable是可综合的。
User-defined
也可以用户自己定义一个新的属性,并描述某个对象的这个属性的值是多少,之后就可以使用这个属性了。
syntax
| 1 2 |  | 
example:
| 1 2 3 4 |  | 
首先定义了一个新的属性,名字叫 number-of-inputs,表示输入端口的个数,然后针对对象 nand3 (3 输入的与非门 ) 这个对象,描述它的这个属性的类型为 signal 类型,取值为 3;最后,使用这个属性,将 nand3 的这个属性的值赋值给 input 对象。
Operator Overloading
用户不仅可以自定义属性,还可以自定义操作符。预定义的操作符的操作数必须是特定的类型,对于某些类型,我们可以自定义操作符对应的操作。
VHDL 中的自定义操作符作用和 C++ 中的操作符重载 方法、目的都很类似。首先构造一个函数,然后调用这个函数即可。
GENERIC
generic 必须在 ENTITY 中声明,它可以指定常规参数,所指定的参数是静态的,全局的。感觉类似于 Verilog 中的 define 吧,但是显然 Verilog 中的 parameter 是更好的设计,因为全局变量 / 常量很不安全。
syntax
| 1 |  | 
example
| 1 2 3 4 |  | 
Concurrent Code
从本质上讲,HDL 是 描述 (Description) 语言,对应的是硬件电路,而硬件电路是时刻工作的,所以,它的代码是并发执行的。只有 process,function,procedure 中的代码是顺序执行的,而且当这些模块作为一个共同的整体时,它们之间也是并行的。
在并发代码中可以使用下列各项:
- 
运算操作符 
- 
when语句(when/else 和 with/select/when)
- 
generate语句
- 
block语句
仔细观察可以发现,其实 when, generate, block 语句和运算语句相比,只是添加了一些条件判断,它们主要的核心还是运算操作符组成的运算,所以,并行代码的核心就是这些并行的运算语句。
when
When/else syntax:
| 1 2 3 |  | 
with/select/when syntax:
| 1 2 3 4 |  | 
generate
功能类似于 Verilog HDL 中的 generate,它常和 for/if 一起使用。 因为描述的对象是电路,最终的电路是固定的,功能也是静态的,所以,对于 generate,它的循环操作的上下界必须是静态的,否则代码是不可综合的。
实际上,引入 generate 的主要目的是为了写出更加通用的代码,达到修改最少代码,实现不同设计的目的,也就是动态编译。而引入 for 循环,只是为了减少代码量。
block
VHDL 中存在两种类型的块 block:简单块 (simple block) 和 卫式块 (guarded block):
simple block
simple block 只是对原有代码进行了区域分割,目的也仅仅是为了增强代码的可读性和可维护性。
syntax:
| 1 2 3 4 5 |  | 
guarded block
guarder block 是一种特殊的 block,它比 simple block 多了一个表达式,叫做 guard expression,只有当这个表达式为 True 时,这个 block 才会执行。
syntax:
| 1 2 3 4 5 |  | 
Sequential Code
VHDL 本质是并发执行的代码,但是在 process, function, procedure 内部的代码是顺序执行的,当它们作为一个整体时,相互之间也是并发执行的。
顺序代码并非只能与时序逻辑 (sequential logic) 对应,同样也可以用它们来实现组合逻辑 (combinational logic)。
顺序代码也称为描述代码 (behavioral code)。
这里主要讨论顺序代码,也就是这 3 个块中的代码,包括 if, wait, case, loop 语句。
process
作用类似于 Verilog HDL 中的 always 语句。
syntax
| 1 2 3 4 5 |  | 
if
syntax
| 1 2 3 4 5 |  | 
wait
如果在 process 中使用了 wait 语句,那么 process 就不能含有敏感信号列表了,所以此时 wait 必须是 process 的第一条语句。
syntax1
| 1 |  | 
syntax2
| 1 |  | 
syntax3
| 1 |  | 
case
case 和 when 的区别在于,case 允许在每个测试条件下执行多个赋值操作,而 when 只能执行一个赋值操作。
syntax
| 1 2 3 4 5 |  | 
loop
syntax1: FOR/LOOP repeat a fix number of times
| 1 2 3 |  | 
syntax: WHILE/LOOP
| 1 2 3 |  | 
syntax3: EXIT
| 1 |  | 
syntax4: NEXT
| 1 |  | 
Signals & Variables
VHDL 提供了 signal 和 variable 两种对象来处理非静态数据;提供了 constant 和 generic 来处理静态数据。
constant 和 signal 是全局的,可以在顺序执行的代码中,也可以在并发执行的代码中;variable 是局部的,只能值顺序代码中,并且它们的值是不能向外传递的 ( 如果想传递出去,必须先把这个变量值传递给一个信号,再由这个信号传递出去 )。
constant
constant 可以定义在 package, entity, architecture 中,对应的作用域也不同。
- 
定义在 package 中的 constant 是真正的全局的,可以被所有调用该 package 的 entity 使用 
- 
定义在 entity 中的 constant 对于该 entity 的所有 architecture 而言是全局的 
- 
定义在 architecture 中的 constant 仅在该 architecture 中是全局的 
syntax
| 1 |  | 
signal
VHDL 中的 signal 代表的是逻辑电路中的 “ 硬 ” 连线,既可以用于电路的输入输出端口,也可以用于 内部单元之间的连接。
syntax
| 1 |  | 
- 
和 Verilog HDL 的 always 中的 reg 类似,VHDL 的 process 中的 signal 也是在进程结束时更新值。 
- 
对同一个信号多次重复赋值,结果取决于编译器。(Xilinx XST 不报错,认为最后一次赋值是有效的 ) 
variable
相比于 signal 是局部的,variable 只能在 process,function,procedure 中使用,而且对它的赋值是立即更新的,新的值可以在下一行代码中立即使用。
syntax
| 1 |  |