VHDL语法简单总结

一个VHDL程序代码包含实体(entity)、结构体(architecture)、配置(configuration)、程序包(package)、库(library)等。

一、数据类型

1.用户自定义数据类型
使用关键字TYPE,例如:
TYPE my_integer IS RANGE -32 TO 32;
–用户自定义的整数类型的子集
TYPE student_grade IS RANGE 0 TO 100;
–用户自定义的自然数类型的子集
TYPE state IS (idle, forward, backward, stop);
–枚举数据类型,常用于有限状态机的状态定义
一般来说,枚举类型的数据自动按顺序依次编码。

2.子类型
在原有已定义数据类型上加一些约束条件,可以定义该数据类型的子类型。VHDL不允许不同类型的数据直接进行操作运算,而某个数据类型的子类型则可以和原有类型数据直接进行操作运算。
子类型定义使用SUBTYPE关键字。

3.数组(ARRAY)
ARRAY是将相同数据类型的数据集合在一起形成的一种新的数据类型。
TYPE type_name IS ARRAY (specification) OF data_type;
–定义新的数组类型语法结构
SIGNAL signal_name: type_name [:= initial_value];
–使用新的数组类型对SIGNAL,CONSTANT, VARIABLE进行声明

例如:
TYPE delay_lines IS ARRAY (L-2 DOWNTO 0) OF SIGNED (W_IN-1DOWNTO 0);
–滤波器输入延迟链类型定义
TYPE coeffs IS ARRAY (L-1 DOWNTO 0) OF SIGNED (W_COEF-1 DOWNTO0);
–滤波器系数类型定义
SIGNAL delay_regs: delay_lines; –信号延迟寄存器声明
CONSTANT coef: coeffs :=( );–常量系数声明并赋初值

4.端口数组
在定义电路的输入/输出端口时,有时需把端口定义为矢量阵列,而在ENTITY中不允许使用TYPE进行类型定义,所以必须在包集(PACKAGE)中根据端口的具体信号特征建立用户自定义的数据类型,该数据类型可以供包括ENTITY在内的整个设计使用。

—————————————PACKAGE———————————-
library ieee;
use ieee.std_logic_1164.all;
——————————————
PACKAGE my_data_types IS
TYPE vector_array IS ARRAY (natural range<>) OF STD_LOGIC_VECTOR(7 DOWNTO 0);–声明8位的数组
END my_data_types;
———————————–Main Code—————————————
library ieee;
use ieee.std_logic_1164.all;
use work.my_data_types.all; –用户自定义包集
——————————————————————
ENTITY mux IS
PORT (inp: IN vector_array(0 to 3);
END mux;
——————————————————————————-

5.有符号数和无符号数
要使用SIGNED和UNSIGNED类型数据,必须在代码开始部分声明ieee库中的包集std_logic_arith。它们支持算术运算但不支持逻辑运算。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
……
SIGNAL a: IN SIGNED (7 DOWNTO 0);
SIGNAL b: IN SIGNED (7 DOWNTO 0);
SIGNAL x: IN SIGNED (7 DOWNTO 0);
……
v <= a + b;
w <= a AND b; –非法(不支持逻辑运算)
——————————————————————————-
STD_LOGIC_VECTOR类型的数据不能直接进行算术运算,只有声明了std_logic_signed和std_logic_unsigned两个包集后才可以像SIGNED和UNSIGNED类型的数据一样进行算术运算。

6.数据类型转换
在ieee库的std_logic_arith包集中提供了许多数据类型转换函数:
1. conv_integer(p):将数据类型为INTEGER,UNSIGNED,SIGNED,STD_ULOGIC或STD_LOGIC的操作数p转换成INTEGER类型。不包含STD_LOGIC_VECTOR。
2.conv_unsigned(p,b):将数据类型为INTEGER,UNSIGNED,SIGNED或STD_ULOGIC的操作数p转换成位宽为b的UNSIGNED类型数据。
3. conv_signed(p,b):将数据类型为INTEGER, UNSIGNED,SIGNED或STD_ULOGIC的操作数p转换成位宽为b的SIGNED类型的数据。
4. conv_std_logic_vector(p, b):将数据类型为INTEGER, UNSIGNED,SIGNED或STD_LOGIC的操作数p转换成位宽为b的STD_LOGIC_VECTOR类型的数据。

二、运算操作符和属性

1. 运算操作符

赋值运算符
赋值运算符用来给信号、变量和常数赋值。
<= 用于对SIGNAL类型赋值;
:= 用于对VARIABLE,CONSTANT和GENERIC赋值,也可用于赋初始值;
=> 用于对矢量中的某些位赋值,或对某些位之外的其他位赋值(常用OTHERS表示)。

例:
SIGNAL x: STD_LOGIC;
VARIABLE y: STD_LOGIC_VECTOR(3 DOWNTO 0); –最左边的位是MSB
SIGNAL w: STD_LOGIC_VECTOR(0 TO 7); –最右边的位是MSB
x <= ‘1’;
y := “0000”;
w <= “1000_0000”; –LSB位为1,其余位为0
w <= (0 => ‘1’, OTHERS=> ‘0’); – LSB位是1, 其他位是0

逻辑运算符
操作数必须是BIT, STD_LOGIC或STD_ULOGIC类型的数据或者是这些数据类型的扩展,即BIT_VECTOR,STD_LOGIC_VECTOR,STD_ULOGIC_VECTOR。
VHDL的逻辑运算符有以下几种:(优先级递减)
Ÿ NOT —— 取反
Ÿ AND —— 与
Ÿ OR —— 或
Ÿ NAND —— 与非
Ÿ NOR —— 或非
Ÿ XOR —— 异或

算术运算符
操作数可以是INTEGER, SIGNED, UNSIGNED,如果声明了std_logic_signed或std_logic_unsigned,可对STD_LOGIC_VECTOR类型的数据进行加法或减法运算。
+ —— 加
- —— 减
* —— 乘
/ —— 除
** —— 指数运算
MOD —— 取模
REM —— 取余
ABS —— 取绝对值
加,减,乘是可以综合成逻辑电路的;除法运算只在除数为2的n次幂时才能综合,此时相当于对被除数右移n位;对于指数运算,只有当底数和指数都是静态数值(常量或GENERIC参数)时才是可综合的;对于MOD运算,结果的符号同第二个参数的符号相同,对于REM运算,结果的符号同第一个参数符号相同。

关系运算符
=, /=, <, >, <=,>=
左右两边操作数的类型必须相同。

移位操作符
<左操作数><移位操作符><右操作数>
其中左操作数必须是BIT_VECTOR类型的,右操作数必须是INTEGER类型的(可以为正数或负数)。
VHDL中移位操作符有以下几种:
u sll 逻辑左移 – 数据左移,右端补0;
u srl 逻辑右移 – 数据右移,左端补0;
u sla 算术左移 – 数据左移,同时复制最右端的位,填充在右端空出的位置;
u sra 算术右移 – 数据右移,同时复制最左端的位,填充在左端空出的位置;
u rol 循环逻辑左移 —数据左移,从左端移出的位填充到右端空出的位置上;
u ror 循环逻辑右移 –数据右移,从右端移出的位填充到左端空出的位置上。

例:x <= “01001”,那么:
y <= x sll 2; –逻辑左移2位,y<=”00100”
y <= x sla 2; –算术左移2位,y<=”00111”
y <= x srl 3; –逻辑右移3位,y<=”00001”
y <= x sra 3; –算术右移3位,y<=”00001”
y <= x rol 2; –循环左移2位,y<=”00101”
y <= x srl -2; –相当于逻辑左移2位
l 并置运算符
用于位的拼接,操作数可以是支持逻辑运算的任何数据类型。有以下两种:
² &
² (, , , )
与Verilog中{}的功能一样。

2. 属性(ATTRIBUTE)

数值类属性
数值类属性用来得到数组、块或一般数据的相关信息,例如可用来获取数组的长度和数值范围等。
以下是VHDL中预定义的可综合的数值类属性:
d’LOW –返回数组索引的下限值
d’HIGH –返回数组索引的上限值
d’LEFT –返回数组索引的左边界值
d’RIGHT –返回数组索引的右边界值
d’LENGTH –返回矢量的长度值
d’RANGE –返回矢量的位宽范围
d’REVERSE_RANGE –按相反的次序返回矢量的位宽范围
例:定义信号 SIGNAL d: STD_LOGIC_VECTOR(7 DOWNTO 0);
则有:d’LOW = 0, d’HIGH = 7, d’LEFT = 7, d’RIGHT = 0, d’LENGTH = 8,d’RANGE = (7 DOWNTO 0), d’REVERSE_RANGE = (0 TO7).

信号类属性
对于信号s,有以下预定义的属性(可综合的):
s’EVENT 若s的值发生变化,则返回布尔量TRUE,否则返回FALSE
s’STABLE 若s保持稳定,则返回TRUE,否则返回FALSE
例:clk的上升沿判断
IF (clk’EVENT AND clk = ‘1’)
IF (NOT clk’STABLE AND clk = ‘1’)
WAIT UNTIL (clk’EVENT AND clk = ‘1’)

3. 通用属性语句
GENERIC语句提供了一种指定常规参数的方法,所指定的参数是静态的,增加了代码的可重用性,类似于Verilog中的parameter与defparam。GENERIC语句必须在ENTITY中进行声明,由GENERIC语句指定的参数是全局的,不仅可在ENTITY内部使用,也可在后面的整个设计中使用。语法结构如下:
GENERIC (parameter_name: parameter_type := parameter_value);
用GENERIC语句指定多个参数:
GENERIC (n: INTEGER := 8; vector: BIT_VECTOR :=“0000_1111”);

三、并发代码
VHDL中并发描述语句有WHEN和GENERATE。除此之外,仅包含AND, NOT, +,*和sll等逻辑、算术运算操作符的赋值语句也是并发执行的。在BLOCK中的代码也是并发执行的。

从本质上讲,VHDL代码是并行执行的。只有PROCESS, FUNCTION,PROCEDURE内部的代码才是顺序执行的。但是当它们作为一个整体时,与其他模块之间又是并行执行的。并发代码称为“数据流”代码。

通常我们只能用并发描述语句来实现组合逻辑电路,为了实现时序逻辑电路,必须使用顺序描述语句。事实上,使用顺序描述语句可以同时实现组合逻辑电路和时序逻辑电路。

在并发代码中可以使用以下各项:

  • 运算操作符
  • WHEN语句(WHEN/ELSE或WITH/SELECT/WHEN)
  • GENERATE语句
  • BLOCK语句
  • 使用运算操作符

    运算类型

    运算操作符

    操作数类型

    逻辑运算

    NOT, AND, NAND,OR

    NOR, XOR, XNOR

    BIT, BIT_VECTOR, STD_LOGIC, STD_LOGIC_VECTOR

    STD_ULOGIC, STD_ULOGIC_VECTOR

    算术运算符

    +, —, *, /, **

    INTEGER, SIGNED, UNSIGNED

    比较运算符

    =, /=, <, >, <=,>=

    任意数据类型

    移位运算符

    sll, srl, sla, sra, rol, ror

    BIT_VECTOR

    并置运算符

    &,(, , ,)

    STD_LOGIC, STD_LOGIC_VECTOR, STD_ULOGIC

    STD_ULOGIC_VECTOR, SIGNED, UNSIGNED

    WHEN语句
    WHEN语句是一种基本的并发描述语句,有两种形式:WHEN/ELSE和WITH/SELECT/WHEN。
    WHEN/ELSE语法结构:
    assignment WHEN condition ELSE
    assignment WHEN condition ELSE
    …;
    WITH/SELECT/WHEN语法结构
    WITH identifier SELECT
    assignment WHEN value,
    assignemnt WHEN value,
    …;
    当使用WITH/SELECT/WHEN时,必须对所有可能出现的条件给予考虑,使用关键字OTHERS,如果在某些条件出现时不需要进行任何操作,那应该使用UNAFFECTED。

    例:

    ————————————-withWHEN/ELSE——————————————-
    Output <= “000” WHEN (inp = ‘0’OR reset = ‘1’) ELSE
    “001” WHEN ctl = ‘1’ ELSE
    “010”;
    ———————————–withWITH/SELECT/WHEN——————————–
    WITH control SELECT
    Output <= “000” WHEN reset,
    “111” WHEN set,
    UNAFFECTED WHEN OTHERS;

    对于WHEN语句,WHEN value的描述方式有以下几种:
    WHENvalue –针对单个值进行判断
    WHEN value1 tovalue2 –针对取值范围进行判断
    WHEN value1 | value2 |… –针对多个值进行判断

    GENERATE语句
    GENERATE语句和顺序描述语句中的LOOP语句一样用于循环执行某项操作,通常与FOR一起使用。语法结构如下:
    label: FOR identifier IN rangeGENERATE
    (concurrent assignments)

    END GENERATE
    GENERATE语句还有另一种形式:IF/GENERATE,此处不允许使用ELSE。IF/GENERATE可以嵌套在FOR/GENERATE内部使用。反之亦然。
    Label1: FOR identifier IN rangeGENERATE
    ……
    Label2: IF condition GENERATE
    (concurrent assignments)
    END GENERATE;
    ……
    END GENERATE;

    例:
    SIGNAL x: BIT_VECTOR(7 DOWNTO 0);
    SIGNAL y: BIT_VECTOR(15 DOWNTO 0);
    SIGNAL z: BIT_VECTOR(7 DOWNTO 0);
    ……
    G1: FOR i IN x’RANGE GENERATE
    z(i) <= x(i) ANDy(i+8);
    END GENERATE;
    GENERATE中循环操作的上界和下界必须是静态的,在使用过程中还要注意多值驱动问题。

    例:
    OK: FOR i IN 0 TO 7 GENERATE
    Output(i) <= ‘1’ WHEN (a(i) AND b(i)) = ‘1’ ELSE‘0’;
    END GENERATE;
    —————————————————————————
    NotOK: FOR i IN 0 TO 7 GENERATE
    accum <=“1111_1111” WHEN (a(i)AND b(i)) = ‘1’ ELSE “0000_0000”;
    END GENERATE;
    —————————————————————-
    NotOK: FOR i IN 0 TO 7 GENERATE
    Accum <= accum + 1 WHEN x(i) = ‘1’;
    END GENERATE;
    —————————————————————-
    块语句(BLOCK)
    VHDL中有两种BLOCK:simple BLOCK和guarded BLOCK。
    n Simple BLOCK
    SimpleBLOCK仅仅是对原有代码进行区域分割,增强整个代码的可读性和可维护性。语法结构如下:
    label:BLOCK
    [ declarative part]
    BEGIN
    (concurrent statement)
    END BLOCK label;
    —————————————————————————————————-
    ARCHITETURE example…
    BEGIN

    block1: BLOCK
    BEGIN

    END BLOCK block1;

    block2: BLOCK
    BEGIN

    END BLOCK block2;

    END example;
    —————————————————————————————–

    例:
    b1: BLOCK
    SIGNAL a: STD_LOGIC;
    BEGIN
    a <= input_sig WHEN ena = ‘1’ ELSE ‘z’;
    END BLOCK b1;
    ———————————————————————————————————————-
    无论是simple BLOCK还是guardedBLOCK,其内部都可以嵌套其他的BLOCK语句,相应的语法结构如下:
    label1: BLOCK
    [顶层BLOCK声明部分]
    BEGIN
    [顶层BLOCK并发描述部分]
    label2: BLOCK
    [嵌套BLOCK声明部分]
    BEGIN
    [嵌套BLOCK并发描述部分]
    END BLOCK label2;
    [顶层BLOCK其他并发描述语句]
    END BLOCK label1;
    ———————————————————————————————————
    n Guarded BLOCK
    多了一个卫式表达式,只有当卫式表达式为真时才能执行。语法结构如下:
    Label: BLOCK(卫式表达式)
    [声明部分]
    BEGIN
    (卫式语句和其他并发描述语句)
    END BLOCK label;

    四、 顺序代码
    在PROCESS, FUNCTION,PROCEDURE内部的代码都是顺序执行的,这样的语句包括IF,WAIT,CASE和LOOP。变量只能在顺序代码中使用,相对于信号而言,变量是局部的,所以它的值不能传递到PROCESS,FUNCTION和PROCEDURE的外部。

    1. 进程(PROCESS)
    进程内部经常使用IF,WAIT,CASE或LOOP语句。PROCESS具有敏感信号列表(sensitivitylist),或者使用WAIT语句进行执行条件的判断。PROCESS必须包含在主代码段中,当敏感信号列表中的某个信号发生变化时(或者当WAIT语句的条件得到满足时),PROCESS内部的代码就顺序执行一次。语法结构如下:
    [label: ] PROCESS (sensitivity list)
    [VARIABLE name type [range] [ := initial_value; ]]
    BEGIN
    (顺序执行的代码)
    END PROCESS [label];
    如果要在PROCESS内部使用变量,则必须在关键字BEGIN之前的变量声明部分对其进行定义。变量的初始值是不可综合的,只用于仿真。在设计同步电路时,要对某些信号边沿的跳变进行监视(时钟的上升沿或下降沿)。通常使用EVENT属性来监视一个信号是否发生了变化。

    2. 信号和变量
    信号可在PACKAGE,ENTITY和ARCHITECTURE中声明,而变量只能在一段顺序描述代码的内部声明。因此,信号通常是全局的,变量通常是局部的。赋予变量的值是立刻生效的,在后续的代码中,此变量将使用新的变量值,而信号的值通常只有在整个PROCESS执行完毕后才开始生效。

    3. IF语句
    IF/ELSE语句在综合时可能会产生不必要的优先级解码电路。IF语句语法结构如下:
    IF conditions THEN assignments;
    ELSIF conditions THEN assignments;
    ELSE assignments;
    END IF;
    ————————————————————————————————
    例:
    IF (x < y) temp :=“1111_1111”;
    ELSIF (x = y AND w = ‘0’) THEN temp :=“1111_0000”;
    ELSE temp := (OTHERS =>‘0’);

    4. WAIT语句
    如果在process中使用了WAIT语句,就不能使用敏感信号列表了。WAIT语句使用以下3种形式的语法结构:
    WAIT UNTIL signal_condition;
    WAIT ON signal1 [, signal2, ...];
    WAIT FOR time;
    WAIT UNTIL后面只有一个信号条件表达式,更适合于实现同步电路(将时钟的上升沿或下降沿作为条件),由于没有敏感信号列表,所以它必须是process的第一条语句。当WAITUNTIL语句的条件满足是,process内部的代码就执行一遍。
    –带有同步复位的8bit寄存器
    process –没有敏感信号列表
    begin
    wait until (clk’event and clk = ‘1′);
    if (rst = ‘1′) then
    output <= (others => ‘0′);
    elsif (clk’event and clk = ‘1′) then
    output <= input;
    end if;
    end process;
    WAIT ON 语句中可以出现多个信号,只要信号列表中的任何一个发生变化,process内的代码就开始执行。
    –带异步复位的8bit寄存器
    process
    begin
    wait on clk, rst;
    if (rst = ‘1′) then
    output <= (others => ‘0′);
    elsif (clk’event and clk = ‘1′) then
    output <= input;
    end if;
    end process;
    WAIT FOR 语句只能用于仿真。

    5. CASE 语句
    CASE语句的语法结构如下:
    CASE 表达式 IS
    WHEN 条件表达式 =>顺序执行语句;
    WHEN 条件表达式 =>顺序执行语句;
    ……
    END CASE

    例:
    case control is
    when“00″ => x<= a; y <= b;
    when“01″ => x<= b; y <= c;
    when others => x<= “0000″; y <= “zzzz”;
    end case;
    关键词OTHERS代表了所有未列出的可能情况,与Verilog中default相当。关键词NULL表示没有操作发生,如WHENOTHERS => NULL.
    CASE语句允许在每个测试条件下执行多个赋值操作,WHEN语句只允许执行一个赋值操作。

    6. LOOP语句
    LOOP语句用在需要多次重复执行时。语法结构有以下几种:
    FOR/LOOP: 循环固定次数
    [label: ] FOR 循环变量 IN 范围LOOP
    (顺序描述语句)
    END LOOP [label];
    WHILE/LOOP: 循环执行直到某个条件不再满足
    [label: ] WHILE condition LOOP
    (顺序描述语句)
    END LOOP [label];
    EXIT: 结束整个循环操作
    [label: ] EXIT [label] [WHEN condition];
    NEXT: 跳出本次循环
    [label: ] NEXT [loop_label] [WHEN condition];

    Example: FOR/LOOP
    for i in 0 to 5 loop
    x(i) <= enable and w(i+2);
    y(0, i) <= w(i);
    end loop
    Example: WHILE/LOOP
    while (i < 10) loop — 0~9
    wait until clk’event and clk = ‘1′;
    (其他语句)
    end loop;

    for i in 0 to data’range loop
    case data(i) is
    when ‘0′ => count := count +1;
    when others => null;
    end case;
    end loop;

    7. CASE语句和IF语句的比较
    IF语句和CASE语句编写的代码在综合、优化后最终生成的电路结构是一样的。
    例:下面两段代码综合后可以得到结构相同的多路复用器
    ————with IF————–
    if (sel =“00″) then x <= a;
    elsif (sel = “01″) then x<= b;
    elsif (sel = “10″) then x<= c;
    else x <= d;
    end if;
    ————-with case———–
    case sel is
    when “00″=> x <= a;
    when “01″=> x <= b;
    when “10″=> x <= c;
    when others => x <=d;
    end case;

    8. CASE语句和WHEN语句的比较
    case语句和when语句的不同之处在于,when语句是并发执行的,case语句是顺序实行的。
    –下面两段代码的功能等效
    ——-with when——————
    with sel select
    x <= a when “000″,
    b when “001″,
    c when “101″,
    unaffected when others;
    ——-with case——————
    case sel is
    when “000″ => x <= a;
    when “001″ => x <= b;
    when “101″ => x <= c;
    when others => null;
    end case;

    9. 使用顺序代码设计组合逻辑电路
    原则1:确保在process中用到的所有输入信号都出现在敏感信号列表中;
    原则2:电路的真值表必须在代码中完整的反映出来。(否则会生成锁存器)

    五、信号和变量
    常量和信号是全局的,既可以用在顺序执行的代码中,也可用在并发执行的代码中。变量是局部的,只能用在顺序代码中,并且它们的值是不能直接向外传递的。

    1. 常量
    CONSTANT name: type := value;

    2. 信号-signal
    VHDL中的signal代表的是逻辑电路中的“硬”连线,既可用于电路的输入/输出端口,也可用于电路内部各单元之间的连接。Entity的所有端口默认为signal。格式如下:
    SIGNAL name: type [range] [:= initial value];
    当信号用在顺序描述语句中时,其值不是立刻更新的,信号值是在相应的进程、函数或过程完成之后才进行更新的。对信号赋初值的操作时不可综合的。

    3. 变量
    变量仅用于局部电路的描述,只能在顺序执行的代码中使用,而且对它的赋值是立即生效的,所以新的值可在下一行代码中立即使用。格式:
    VARIABLE name: type [range] [:= initial value];
    对变量的赋初值操作也是不可综合的。

    4. 寄存器的数量
    当一个信号的赋值是以另一个信号的跳变为条件时,或者说当发生同步赋值时,该信号经过编译后就会生成寄存器。如果一个变量是在一个信号跳变时被赋值的,并且该值最终又被赋给了另外的信号,则综合后就会生成寄存器。如果一个信号在还没有进行赋值操作时已被使用,那么也会在综合时生成寄存器。
    process (clk)
    begin
    if (clk’event and clk = ‘1′) then
    output1 <=temp; – output1被寄存
    output2 <=a; – output2被寄存
    end if;
    end process;

    process (clk)
    begin
    if (clk’event and clk = ‘1′) then
    output1 <=temp; – output1被寄存
    end if;
    output2 <=a; – output2未被寄存
    end process;

    process (clk)
    variabletemp: bit;
    begin
    if (clk’event and clk = ‘1′) then
    temp <= a;
    end if;
    x <= temp; –temp促使x被寄存
    end process;

    六、包集元件

    1. 包集
    经常使用的代码通常以component,function或procedure的形式编写。这些代码被添加到package中,并在最后编译到目标library中。Package中还可以包含TYPE和CONSTANT的定义。语法格式如下:
    package package_name is
    (declaration)
    end package_name;
    package body package_name is
    (function and procedure description)
    end package_name;]
    Example6.1 简单的程序包
    library ieee;
    use ieee.std_logic_1164.all;
    ———————————————————————-
    package my_package is
    type state is (st1, st2, st3, st4);
    type color is (red, green, blue);
    constantvec: std_logic_vector(7 downto0) := “1111_1111″;
    end my_package;
    Example6.2 内部包含函数的package
    library ieee;
    use ieee.std_logic_1164.all;
    ———————————————————————-
    package my_package is
    type state is (st1, st2, st3, st4);
    type color is (red, green, blue);
    contant vec: std_logic_vector(7 downto 0) := “1111_1111″;
    function positive_edge(signal s: std_logic) returnboolean;
    end my_package;
    ———————————————————————-
    package body my_package is
    function positive_edge(signal s: std_logic) returnboolean is
    begin
    return(s’event and s = ‘1′);
    end positive_edge;
    end my_package;
    为了在QUARTUSII中使用这些package,要在当前project目录下新建一个文件夹,不妨起名为user_lib,把要编译的package放进此文件夹中,然后在AssignmentsàSettingàLibrary中设置相应的目录即可。在VHDL代码中要使用这些package,要在主程序中加入如下代码:
    use work.package_name.all;

    2. 元件component
    一个元件是一段结构完整的常用代码,包括声明,实体和结构体,使用component可以使代码具有层次化的结构。
    元件声明:
    component comp_name is
    port (
    port_name1: signal_mode signal_type;
    port_name2: signal_mode signal_type;

    );
    end component;
    元件实例化:
    label: comp_name port map (port_list);
    元件的声明可以放在主代码中,即调用该元件的代码;或者将元件的声明放到package中,使用时在主代码中增加一条USE语句即可,这样避免了主代码中每实例化一个元件就要声明一次的麻烦。

    3. 端口映射
    在元件实例化过程中,有两种方法实现元件端口的映射:位置映射和名称映射。
    component inverter is
    port ( a: in std_logic;
    b: out std_logic
    );
    end component;

    U1: inverter port map(x, y);
    此处采用的是位置映射法,x对应a,y对应b。
    U1: inverter port map(a => x,b=> y);
    此处采用的是名称映射法。对于不需要使用的端口可以断开,只需使用关键字open即可,但是输入端口不能指定为空连接。比如:
    U2: my_circuit port map(x => a, y=> b, w => open, z =>b);

    4. GENERIC参数映射
    元件实例化时如果要通过GENERIC传递参数,则需进行GENERIC参数的映射。元件实例化的格式如下:
    label: comp_name generic map(param_list) portmap(port_list);

    七、 函数和过程
    Function和procedure统称为子程序,内部包含的都是顺序描述的VHDL语言.

    八、 有限状态机
    状态机的设计包含两个主要过程:状态机建模和状态的编码。

    1.有限状态机的建模
    有限状态机通常使用CASE语句来建模,一般的模型由两个进程组成,一个进程用来实现时序逻辑电路,另一个进程用来实现组合逻辑电路。
    模型的构建:
    (1) 分析设计目标,确定有限状态机所需的状态,并绘制状态图;
    (2) 建立VHDL实体,定义枚举类型的数据类型;
    (3) 定义状态变量,其数据类型为前面所定义的枚举数据类型;例:
    TYPE STATE IS (STATE0, STATE1, STATE2, …);
    SIGNAL CR_STATE, NEXT_STATE: STATE;
    (4) 建立时序逻辑电路的实现进程;例:
    PROCESS (CLK,RESET)
    BEGIN
    IF RESET=’1’ THEN
    CR_STATE <= STATE0;
    ELSIF CLK’EVENT AND CLK=’1’ THEN
    CR_STATE <= NEXT_STATE;
    END IF;
    END PROCESS;
    (5) 使用CASE语句建立组合逻辑电路的实现进程。例:
    PROCESS(CR_STATE,INPUT)
    BEGIN
    CASE CR_STATE IS
    WHEN STATE0 =>
    IF INPUT = … THEN
    NEXT_STATE <= STATE1;
    END IF;
    WHEN STATE1 =>

    WHEN OHTERS => NEXT_STATE <=STATE0;
    END CASE;
    END PROCESS;

    2.状态编码
    状态编码包括二进制编码、枚举类型的编码和一位有效编码。利用一位有效编码(One-hotencoding)可以创建更有效地在FPGA结构中实现的有限状态机。每个状态可以使用一个触发器来创建状态机,并且可以降低组合逻辑的宽度。

    有限状态机的可能状态由枚举类型所定义,即:
    TYPE type_name IS(枚举元素1, 枚举元素2, …., 枚举元素n);
    这个定义是通用的格式,时必须的。在该枚举类型定义语句之后,就可以声明信号为所定义的枚举类型:
    TYPE STATE_TYPE IS(S1, S2, S3, S4, S5, S6, S7);
    SIGNAL CS,NS: STATE_TYPE;

    为了选择有限状态机的状态编码方式,需要指定状态矢量。也可以通过综合工具指定编码方式。当在程序中指定编码方式时,可以在枚举类型定义语句后指定状态矢量,例如,

    定义二进制编码的状态矢量的语句是:
    ATTRIBUTE ENUM_ENCODING: STRING;
    ATTRIBUTE ENUM_ENCODING OF STATE_TYPE:TYPE IS “001 010 011 100101 110 111”;

    定义一位有效编码的状态矢量的语句为:
    ATTRIBUTE ENUM_ENCODING:STRING;
    ATTRIBUTE ENUM_ENCODING OF STATE_TYPE:TYPE IS ”00000010000010 0000100 0001000 0010000 0100000 1000000”;

    附:
    1.buffer和out都是用来表示模块的输出的,如果模块内部会load这个输出,就要声明成buffer,否则声名成 out就可以了。一般情况下的buffer可以代替out,但out不能代替buffer。
    2.std_logic_vector 是标准逻辑矢量,定义的是长度大于1的变量,需要确定赋值方向 (n downto 0) or (0 downto n)。std_logic 是长度为1的逻辑 与bit 相似,只是 bit 只能是'0 ’和'1‘ 而 std_logic有以下九种状态:U'——初始值,'X'——不定,'0'——0,'1'——1,'Z'——高阻,'W'——弱信号不定,'L'——弱信号0,'H'——弱信号1,'-'——不可能的情况