VHDL语言的一些东西
话说有快一个月没有写点什么了…
这段时间里也发生了各种事…
其中一个是EDA课程的大作业(用VHDL设计洗衣机芯片)…deadline是在7月9日(周五)…
而期末考试是在7月5日,7月7日,7月9日…EDA大作业心血来潮赶着在7月7日的时候就做好交了…不过也庆幸没有往后拖…
因为后来得了胃炎…7月9日的考试时真是煎熬啊…胃很难受,又饿着肚子又反胃…
EDA这门课结束了…VHDL语言的东西也不知道以后还会不会接触…写点什么纪念下吧…
VHDL是一种硬件描述语言…比如用来编写各种芯片…
课上用的环境是maxplus II…不过貌似落伍了?…
编写硬件真是和编写软件差很多…
基本上,VHDL中每条语句之间都是并发的,软件则是顺序执行的(当然多核多线程下可能有真正意义上的并发)…
因为软件每条指令背后都有个cpu时钟…指令之间有着先后顺序…
而VHDL则是用最基本的门电路构建一个从输入到输出的逻辑关系…不计信息传播速度的话,输出只在输入改变的时候改变…
那么…先说下VHDL的几个够用的语法吧…
首先,和C++中#include语句类似的是library
LIBRARY IEEE;
话说到现在也没太搞明白library后面跟的东西是从什么文件夹里找的…
vhdl中use是类似C++中的using的东西
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL;
就像using namespace std;一样平常…
恩…对应C++的namespace的话应该是package了吧…
不过…我没有用过namespace,也没有用过package…不用管这些也挺够用的…
然后是元件(实体)的定义…一段vhdl程序就是用来做成一个元件的…就像C++中一个函数(感觉比起类来说更接近函数)
这个实体的定义是放在entity语句中的,其中主要是port语句定义端口名字和类型,就像C++中定义函数参数一样
ENTITY counter IS PORT(clk,en,reset:IN STD_LOGIC; target:IN STD_LOGIC_VECTOR(4 DOWNTO 0); q:OUT STD_LOGIC); --counter:clk来一个上升沿则内部计数器加一,直到内部计数器与target值相同时q端从0变成1 --en:1-enable,0-disable --reset:0-nothing 1-reset to 0 END ENTITY counter;
这样定义了一个叫做counter的元件(实体)
其中clk,en,reset是输入端,并且都是1bit大小(STD_LOGIC是1个bit),也就是这三个端口分别占用一个引脚…
target也是输入端,但是有5bit大小,也就是它用了5个引脚,要指定是哪个引脚可以用下标,就像C++的数组一样
q是输出端,1bit大小
下面是注释…在vhdl中用–表示注释…
到这里,就像是C++中声明了一个函数,但是函数内容还没有
下面要用architecture来写这个元件内部结构了,相当于C++的函数体
ARCHITECTURE behav OF counter IS --这里可以放内部信号的定义 --引用外部元件的定义也写在这里 BEGIN --这里放代码(赋值语句) END ARCHITECTURE behav;
内部信号类似于C++中函数体中的局部变量,作用域是整个元件(实体)内部
然后代码部分…是的…从根本上来说只有赋值语句…
连接电路的时候除了连连线之外还能做什么呢…
vhdl只是做这种连线的工作…
赋值语句是用<=这个符号的,右边是由引脚,信号量,常量等的逻辑运算组合
当然,声明为IN的引脚只能作为右值,声明为OUT的引脚只能作为左值
声明为BUFF的引脚既可以作为右值也能作为左值,类似内部反馈一样的东西...有点像C++中的引用...
ARCHITECTURE behav OF counter IS SIGNAL cnt:STD_LOGIC_VECTOR(4 DOWNTO 0); SIGNAL m_clk:STD_LOGIC; SIGNAL m_reset:STD_LOGIC; BEGIN m_clk <=en AND clk; m_reset<=en AND reset; PROCESS(m_clk,m_reset) IS BEGIN IF(m_reset='1')THEN cnt<="00001"; q<='0'; ELSIF(m_clk'EVENT AND m_clk='1')THEN IF(cnt=target)THEN q<='1'; cnt<="00001"; ELSE q<='0'; cnt<=cnt+1; END IF; END IF; END PROCESS; END ARCHITECTURE behav;
在定义了几个信号量,写了2句普通的赋值语句之后出现了process这个东西…
在一个architecture中可以写多个process,它们都是并发的…
process这东西…基本上是专门为触发器准备的…
虽然在process中可以不用触发器…但那基本上只剩下”让程序结构清晰”这一条特点了…
一开始的时候我也很奇怪,居然会有if语句…
但是试着把各种东西放进if条件中去后就会发现…这其实只是假象…
就像51单片机的汇编代码…指令加上后面的参数一起都只是助记符而已…别以为有个mov A,@Ri的指令,就会有mov Rn,@Ri的指令…
vhdl中的if也一样…看到判断边沿可以放在if里面就以为一个process里面可以判断多个边沿或者判断边沿之后写个else什么的…怎么可能嘛…
这是硬件…当你发现写出来的代码不能编译通过时就想想,如果自己手动连线,得怎么连才能实现代码所描述的情况呢…?
就像把一个与门写成AND的形式,加法器写成加号的形式一样,这里只是把一个上升沿触发器写成了如下的形式而已…
IF(信号'EVENT AND 信号='1')THEN ... END IF;
而带有使能端的上升沿触发器写成了这样的形式:
IF(简单条件)THEN ... ELSEIF(信号'EVENT AND 信号='1')THEN ... END IF;
不知是不是上课的时候老师说过…process中代码是顺序执行的…
但我感觉这样说不恰当…应该说是在某一时刻上,把所有无法进入的分支语句去除后剩下的所有赋值语句之间,是并发的…
这是试了很多种写法得出的结论,比如在前一个判断语句中给某个信号赋值,然后在后面某个判断语句中判断这个信号的值,实际结果是,后面的判断语句中判断到的值是在未经前一个判断语句中赋值的…
又比如b<=c;a<=b;是和a<=b;b<=c;一样的...结果都是a得到原来b的值,b得到c的值
不过貌似没有试过用variable代替signal会发生什么情况?
至此,这个叫做counter的元件就写完了...
把代码人工翻译成示意图的话大致会是这个样子...

就像C++中函数内可以调用其他函数一样,vhdl中元件内也可以放其他元件,只要连上放进去的元件的引脚就行了
像上图中所有小方框都算是一个元件,寄存器内部可以是一组触发器做成的,[+1]也只是一些逻辑电路做成的加一的加法器…
在这里,比如门,加法器,触发器,都用逻辑运算符,加号,触发器的if语句对应了,就像…C++中的重载运算符…
但是如果要加入其他元件或者自己制作的元件,就要用port map来显式设置了
要使用某种元件的话,先要在architecture与它的begin之间,也就是定义内部信号量的地方,使用component语句来声明这个外部元件
LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY buzzer IS PORT(clk,done:IN STD_LOGIC; buzz:OUT STD_LOGIC); --buzzer:done出现上升沿时开始buzz输出1,持续30个clk周期,其他时候buzz输出0 --buzz:0-no sound, 1-buzz END ENTITY buzzer; ARCHITECTURE behav OF buzzer IS COMPONENT counter PORT(clk,en,reset:IN STD_LOGIC; target:IN STD_LOGIC_VECTOR(4 DOWNTO 0); q:OUT STD_LOGIC); END COMPONENT counter; SIGNAL cnt_30:STD_LOGIC_VECTOR(4 DOWNTO 0); SIGNAL wait_en,wait_reset,wait_done:STD_LOGIC; SIGNAL count_start,count_end,counting:STD_LOGIC; BEGIN cnt_30<="11110"; wait_en<='1'; wait_reset<=NOT counting; wait_30:counter PORT MAP(clk,wait_en,wait_reset,cnt_30,wait_done); counting<=count_start XOR count_end; buzz<=counting; st:PROCESS(done) IS BEGIN IF(done'EVENT AND done='1')THEN count_start<=NOT count_end; END IF; END PROCESS st; ed:PROCESS(wait_done) IS BEGIN IF(wait_done'EVENT AND wait_done='1')THEN count_end<=count_start; END IF; END PROCESS ed; END ARCHITECTURE behav;
在component中也要用port来声明该元件的引脚,直接从该元件的entity中的port声明里面复制下来就行了
然后在需要用这种元件的时候,就用port map来建一个该元件的副本,给个名字,把它的引脚与你的信号量或者引脚连接(映射)起来
前面代码中,buzzer元件内部就设置了一个counter元件的副本,名字叫做wait_30,并且将后面那些信号量或者引脚一一连到wait_30对应的引脚上
声明一次之后,当前元件的代码中可以多次用port map使用这种元件,像C++中函数内调用其他函数...
只不过这里每次都必须给不同的名字,因此使用多少次这种元件,它就会有多少个副本...
和软件中调用函数对比一下的话会很有趣呢...
软件中调用多次函数不会增加空间复杂度,但是会增加时间复杂度...
而硬件中使用多次元件的话不会增加时间复杂度,但是会增加空间复杂度...
那么就到这里吧...基本上用这些就能...嗯...至少应付这门课是没有问题了...
评论暂缺
Comments RSS
TrackBack Identifier URI
No comments. Be the first.
留下评论