[VHDL]

玩转VHDL-001编程规范

[复制链接]
1034|3
手机看帖
扫描二维码
随时随地手机跟帖
ucx|  楼主 | 2017-9-18 10:09 | 显示全部楼层 |阅读模式

优秀的VHDL代码,在完成功能的前提下,应该具备如下特点:

  简短的代码量

  通俗的可读性

  更少的资源消耗

  更高的工作速率

  更强的可重用性

  最后强调但最重要的是鲁棒性

这些要求,有时是相互矛盾的。但是,通过合理的程序书写风格可以使这些要求分别或整体达到最优。其中更高的工作速率是指通过合理分配组合逻辑和时序逻辑,使内部逻辑时延相对于系统工作时钟留有一定余量,从而保证逻辑的可靠性。可重用性,指整个工程功能模块分配合理,在将来其他类似设计中,可以拷贝文件或稍作修改重新使用。

简短的代码量有时等效于最少的程序行数。我们在使用protel等软件画电路时,常希望有更大的计算机显示屏,以便尽可能通览全局,及时准确地把握电路功能。那么,代码量或或行数的减少,类似于屏幕的增大,便于设计者或读者总览全局,保持思路的连续性。这一特点,客观上也提高了程序的可读性。我们知道VHDL是一门语法非常严格的语言,这在很多时候增加了代码量,这也被很多硬件工程师所诟病。那么如何规避这所谓的缺点,减少代码量呢?我们从一个很小的例子说起。

1:假设有一个3位的计数器cn :std_logic_vector(2 downto 0),在一定时钟下按照一定规则计数,需要在计数值为03后输出q : std_logic为高电平、其他值为低电平。那么:

if cn=0 or cn =3 then

         q <= '1';

else

         q <= '0';

end if;

这时,需要5行代码完成。如果强行写成1行:

if cn=0 or cn = 3 then q <= '1'; else q<= '0'; end if;

此时的可读性我们只能以呵呵来评价。这么简单的逻辑,我们期望能像C语言一样写成:

q <= cn=0 orcn = 3;

由于q定义的是std_logic型,编译不能通过。

我们定义一个标准化函数STDZ(STanDardiZation的缩写,可读作stands)

         Function STDZ (b :boolean) return std_logic is

         begin

                   if b then   return '1';

                   else           return '0';

                   end if;

         End;

这是个不消耗任何资源的函数,那么例1可写为:

q <=STDZ(cn=0 or cn = 3);

至此,程序编程了1行代码。

尽管在VHDL中定义了很多数据类型,但在实际编程中使用的不外乎有integerstd_logicstd_logic_vector三种,以及隐含使用的boolean类型。我个人觉得std_logic_vector类型名字有些过长,所以:subtype std_vector is std_logic_vector;  
现在我们新建一个ucx_200pkg.vhd文件,初始内容如下图。为了使读者完全理解此用户自定义包,在后续内容中逐渐增加自定义包的内容。






相关帖子

ucx|  楼主 | 2017-9-18 10:14 | 显示全部楼层
QQ图片.png

使用特权

评论回复
ucx|  楼主 | 2017-9-18 14:33 | 显示全部楼层
002计数器
计数器在时序逻辑中应用极为普遍,用于计数、定时、分频和时序控制。对计数器的常用操作有装载、复位、置位、增1和减1。通常,装载、复位和置位的操作优先级高于增1和减1。装载操作与置位复位操作优先级关系随具体设计而定。增减操作中,有时减计数的优先级更高。

一、Procedure LoadValue()

2:同样假设一个计数器cnt,受到rst, set, inc, dec, ld五个boolean类型信号控制,rst为同步复位(reset)set为同步置位,inc为增1使能(increase)dec为减1使能(decrease)ld为装载条件(load)。那么:

                   if rst then

                            cnt< = (others => '0');

                   elsif inc then

                            cnt< = cnt + 1;

                   elsif dec then

                            cnt< = cnt - 1;

                   end if;

其功能显而易见,不作赘述。硬件语言区别于C语言的一个显著特点是其赋值分为信号赋值和变量赋值。根据信号赋值的特点,上述简短代码等效代码如下:

                   if dec then

                            cnt< = cnt - 1;

                   end if;      

                   if inc then

                            cnt< = cnt + 1;

                   end if;      

                   if rst then

                            cnt< = (others => '0');

                   end if;

同一个进程内对同一个信号赋值,后面的赋值条件优先级高于前面的赋值条件。上述三次赋值,形式相同。形式为

                   if load_condition then

                            q< = d;

                   end if;

的赋值语句极为常用,所以定义:

         Procedure LoadValue(signal q : out std_logic; d : in std_logic; load : in boolean) is

         begin

                   if load then

                            q <= d;

                   end if;

         End;

为了使LoadValue子进程更具一般性,重载如下:

         Procedure LoadValue(signal q : out std_logic; d : in std_logic; load : in std_logic) is

         begin                  

                   LoadValue(q, d, load = '1');

         End;

同理,继续重载LoadValue为:

Procedure LoadValue(signal q : out std_vector;      d : in std_vector;      load : in boolean);   

Procedure LoadValue(signal q : out std_vector;      d : in std_vector;      load : in std_logic);





使用特权

评论回复
ucx|  楼主 | 2017-9-18 14:33 | 显示全部楼层
本帖最后由 ucx 于 2017-9-18 14:38 编辑

二、IncDec()DecInc(),读作ink dickdick ink

2中,提到了增减计数,用子进程描述如下:

Procedure IncDec(signal cnt : inout std_vector; inc : in boolean;   dec : in boolean := false) is

begin

                   if inc then

                            cnt< = cnt + 1;

                   elsif dec then

                            cnt< = cnt - 1;

                   end if;

End;

其中dec用给出默认值false,其目的是适用于仅仅增计数场合IncDec(cnt, inc)只使用两个参数时等效为

                   if inc then

                            cnt< = cnt + 1;

                   end if;      

同理定义减计数优先级高的子进程DecInc

Procedure DecInc(signal cnt : inout std_vector; dec : in boolean; inc : in boolean := false) is

begin

                   if dec then

                            cnt< = cnt - 1;

                   elsif inc then

                            cnt< = cnt + 1;

                   end if;

End;

类似于LoadValue(),重载这两个子进程。

Procedure IncDec(signal cnt : inout std_vector;     inc : in std_logic; dec : in std_logic := '0') is

begin

                   IncDec(cnt, inc = '1', dec = '1');

End;

Procedure DecInc(signal cnt : inout std_vector;     dec : in std_logic;     inc : in std_logic := '0');

三、RstIncDecSetIncDecRstDecIncSetDecInc

2中的增减计数,增加复位或置位条件,又可得四个子进程,并分别重载后增添到ucx_2008pkg包中。文件ucx_2008pkg.vhd头部如下图所示:
计数器.png
其中,Procedure RstIncDec(signal cnt : inout std_vector;  rst,inc : in boolean; dec : in boolean := false);定义如下:
         Procedure RstIncDec(signal cnt : inout std_vector;    rst,inc : in boolean; dec : in boolean := false) is
                   subtype T        is std_vector(1 to cnt'length);
         begin
                   if rst then
                            cnt< = T'(others => '0');
                   elsif inc then
                            cnt< = cnt + 1;
                   elsif dec then
                            cnt< = cnt - 1;
                   end if;
         End;
         Procedure RstIncDec(signal cnt : inout std_vector;   rst,inc : in std_logic;         dec : in std_logic := '0') is
         begin
                   RstIncDec(cnt, rst = '1', inc = '1', dec = '1');
         End;
其余三个子进程类似定义。
四、一个简单实例
写到这里,我们用一个简单的实例来说明如何使用这些子函数(或进程),也算作休息一下。
3:设计一个时钟工作在50M的计数器,其状态转换为01237540,如此7个状态循环,并且,用一个高电平脉冲指示输出值为4
分析:可用一个3位计数器cnt实现上述功能,计数器的复位、置位、加1、减1和装载条件分别是cnt=4cnt=3cnt <4cnt>4cnt=7。程序代码如下图。
dm_cnt.png

ucx_2008pkg.vhd拷贝到Quartus II安装目录\quartus\libraries\ucxLib文件夹下即可在编译成功。
ucxLib库添加到altera_modelsim中,即可仿真成功,结果如下。


dm_cnt_sim.png

modelsimteshbench文件名为top_IO.vht,内容如下:
Library ieee;  USE ieee.std_logic_1164.all, ieee.std_logic_unsigned.all;  
Library ucxLib; Use ucxLib.ucx_2008pkg.all;
entity top IS
end top;
architecture pure_sim of top is
signal clock                                                               : std_logic := '0';
signal cnt                                                                  : std_vector(2 downto 0);
signal q_4v                                                                        : std_logic;
begin
i1: entity work.dm_cnt Port Map(clock=>clock, qCnt=>cnt, q_end=>q_4v);
Process begin
       wait for 20 ns;
       clock <= not clock;

End process;
End pure_sim;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

ucx

28

主题

85

帖子

5

粉丝