VHDL编程语言语法内容很多很杂,包括用于编程的规定、用于仿真的规定以及用于描述的规定。对于刚刚接触VHDL语言的设计者而言,拿到厚厚的VHDL编程语言书籍,往往有无从下手感觉,其实对于使用VHDL很久的人来讲,也不一定会使用VHDL很多编程规范,今天对VHDL常用的编程规范进捋一捋。
VHDL语言中的信号、变量或者常量都必须有一个属于自己的名字,即标识符,然后才能在程序中进行引用。而VHDL语法中的命名规则如下:
1、标识符中只能包含字符、数字和下划线,即”A-Z”,”a-z”,”0-9”,”_”;
2、标识符必须以字符开头,即开头符号只能是”A-Z”,”a-z”。
vhdl编程语言对信号、变量或常量大小不敏感。signal、variable和constant 是vhdl三种经常使用的赋值对象。所有赋值对象都遵循先声明后使用原则。特别说明:常量是不能单独赋值的,仅仅能在声明的同时被初始化赋值。
signal
signal 中文意思为“信号” ,一般对应电路中特定的物理连线或存储单元。当程序中需要用到signal时,我们一般需要在VHDL基本程序框架中的architecture语法的声明与定义部分先声明一个signal,然后才能在architecture的语句部分使用它。 Signal的声明语法如下:
声明1
signal 信号名 :=初值 ; – 有初值
需要对signal 信号赋初值,只能定义一个信号名,eg
signal state : std_logic_vector(3 downto 0):=x"0";
声明2
signal 信号名:信号类型 ; – 无初值
signal state1: std_logic_vector(4 downto 0);
signal cnt: integer range 0 to 31;
声明3
如果有多个同样类型的信号需要声明,也可以用这样的语法:
signal 信号名1,信号名2,信号名3: 信号类型 ; – 多个信号名之间用逗号隔开
signal state11,state12: std_logic_vector(4 downto 0);
signal cnt1,cnt2: integer range 0 to 31;
特殊的声明-端口信号声明
entity语法中的port语法结构中的in、out、buffer或者inout类型的端口,虽然没有用关键字“signal”来声明,但它们其实都是signal类型的,并且同类型的端口也支持逗号分隔单行书写的方法。
entity div_des is
port
(
clk : in std_logic;
rst_n : in std_logic;
numer32 : in std_logic_vector( 31 downto 0);
demoniator32 : in std_logic_vector( 31 downto 0);
quotient32 : out std_logic_vector( 31 downto 0);
remainer32 : out std_logic_vector( 31 downto 0)
);
end div_des;
或
entity div_des is
port
(
clk,rst_n : in std_logic;
numer32 : in std_logic_vector( 31 downto 0);
demoniator32 : in std_logic_vector( 31 downto 0);
quotient32 : buffer std_logic_vector( 31 downto 0);
remainer32 : out std_logic_vector( 31 downto 0)
);
end div_des;
特殊声明-package 中声明signal
在library里的package语法中,也可以声明signal。
package my pkg is
type byte is range 0 to 255; ---定义byte的范围
subtye nibyte is byte range 0 to 15; 定义子类型
constant byte_ff: bcd:=255;
signal added: nibabc;
component byte_adder
port
(
a,b: in byte;
c: out byte;
overflow: out boolean
);
end component;
function my_fun (a: in byte)
return byte;
end my pkg;
Variable
Variable即是“变量”的意思,它不具有特定的物理意义,对应关系也不太直接,通常只代表暂存某些值的载体。** 在之前介绍的VHDL基本程序框架中,可以看到variable出现在process语句中,作为process的局部变量来使用。** 当程序中要用到variable时,只需要在process语法的敏感量列表与begin关键字之间先声明一个variable,然后就能在process的语句体中使用它。Variable的声明语法如下:
特别强调:变量只能在进程、function和procedure 语法结构中进行先声明后使用!
变量定义与信号定义类似,除了关键字和声明的位置不同外,其余定义都相同!
Constant
Constant即是“常量”的意思,它具有特定的物理意义,通常对应数字电路中的电源或者地。Constant能出现在所有signal和variable出现的场合中,它的声明语法如下:
constant : := ;
可以注意到,常量是不能单独赋值的,仅仅能在声明的同时被初始化赋值。
总结一下:信号、变量和常量之间差别:
变量声明使用variable,赋值符号位:=
而信号声明用signal,赋值符号为<=
1、变量只能用在进程函数体,子程序内部
2、信号不能再进程中声明,信号用在结构体、实体、程序包。
在一个进程中,变量及时赋值,信号在进程结束后赋值。
(1)loop语句中,若在一个循环体内需要多次对某一个数据操作,则必须用变量,因为对信号赋值进行多次赋值只在最后一次才会有效。
(2)数组的索引(index)只能用变量。如果使用信号则编译会报错
(1)变量赋值无延时是针对进程运行而言的,只是一个理想值,对于变量的操作往往被综合成为组合逻辑的形式,而硬件上的组合逻辑必然存在输入到输出延时。当进程内关于变量的操作越多,其组合逻辑就会变得越大越复杂。假设在一个进程内,有关于变量的3个 级连操作,其输出延时 分别为5ns,6ns,7ns,则其最快的时钟只能达到18ns。相反,采用信号编程,在时钟控制下,往往综合成触发器的形式,特别是对于FPGA芯片而言,具有丰富的触发器结构,易形成流水作业,其时钟频率只受控于延时最大的那一级,而不会与变量一样层层累积。假设某个设计为3级流水作业,其每一级延时分别为10ns,11ns,12ns,则其最快时钟可达12ns。因此,采用信号反而更能提高设计的速度。
(2)由于变量不具备信息的相关性,只有当前值,因此也无法在仿真时观察其波形和状态改变情况,无法对设计的运行情况有效验证,而测试验证工作量往往会占到整个设计70%~80%的工作量,采用信号则不会存在这类问题。
(3)变量有效范围只能局限在单个进程或子程序中,要想将其值带出与其余进程、子模块之间相互作用,必须借助信号,这在一定程度上会造成代码不够简洁,可读性下降等缺点。