转载地址http://blog.163.com/softtwo_2/blog/static/2700770220074105421210/
一 端口
VHDL共定义了5中类型端口,分别是 in、out、inout、buffer和Linkage。一般设计中只会用到前四种。
设计中最好不要使用Buffer类型,因为他不能直接连接到其他类型的端口上,不可例化。若要完成其回读功能可以添加中间变量来实现。
双端口inout类型处理:将inout类型数据分别赋值给in和out类型两个信号。由这两个信号完成后端连接。
① DataB<=Din when CE=’1’ and Rd=’0’ else
② (others=>’Z’);
③ Dout<=DataB when CE=’1’ and Rd=’1’ else
④ ( others=>’1’ );
… …
程序中DataB为双向端口,编程时应注意的是,当DataB作为输出且空闲时,必须将其设为高阻态挂起,即有类似第②行的语句,否则实现后会造成端口死锁。而当DataB作为有效输入时, DataB输出必须处于高阻态,对于该例子中即,当 CE=’1’ and Rd=’1’时,输出DataB应处于高阻态。
二.信号和变量
常数、信号和变量是VHDL中最主要的对象,分别代表一定的物理意义。常数对应于数字电路中的电源或地;信号对应某条硬件连线;变量通常指临时数据的局部存储。信号和变量功能相近,用法上却有很大不同。
表1 信号与变量主要区别
信号(<=) 变量(:=)
赋值延迟 至少有△延时 无,立即变化
相关信息 有,可以形成波形 无,只有当前值
进程敏感 是 否
全局性 具有全局性,可存在于多个进程中 只能在某个进程或子程序中有效
相互赋值关系 信号不能给变量赋值 变量可以给信号赋值
对于变量赋值操作无延迟,初学者认为这个特性对VHDL设计非常有利,但这只是理论上的。基于以下几点原因,我们建议,编程时还是应以信号为主,尽量减少变量的使用。
(1)变量赋值无延时是针对进程运行而言的,只是一个理想值,对于变量的操作往往被综合成为组合逻辑的形式,而硬件上的组合逻辑必然存在输入到输出延时。当进程内关于变量的操作越多,其组合逻辑就会变得越大越复杂。假设在一个进程内,有关于变量的3个 级连操作,其输出延时 分别为5ns,6ns,7ns,则其最快的时钟只能达到18ns。相反,采用信号编程,在时钟控制下,往往综合成触发器的形式,特别是对于FPGA芯片而言,具有丰富的触发器结构,易形成流水作业,其时钟频率只受控于延时最大的那一级,而不会与变量一样层层累积。假设某个设计为3级流水作业,其每一级延时分别为10ns,11ns,12ns,则其最快时钟可达12ns。因此,采用信号反而更能提高设计的速度。
(2)由于变量不具备信息的相关性,只有当前值,因此也无法在仿真时观察其波形和状态改变情况,无法对设计的运行情况有效验证,而测试验证工作量往往会占到整个设计70%~80%的工作量,采用信号则不会存在这类问题。
(3)变量有效范围只能局限在单个进程或子程序中,要想将其值带出与其余进程、子模块之间相互作用,必须借助信号,这在一定程度上会造成代码不够简洁,可读性下降等缺点。
当然,变量也具有其特殊的优点,特别是用来描述一些复杂的算法,如图像处理,多维数组变换等。
类似于Verilog语言中的阻塞赋值和非阻塞赋值:阻塞赋值(=)后续赋值必须等待前一句赋值完成,且更新值,用于组合逻辑;非阻塞赋值(<=)则同时发生,不会立即更新,用于时序控制。
三.关于进程
进程(Process)是VHDL中最为重要的部分,大部分设计都会用到Process结构,因此掌握Process的使用显得尤为重要。以下是初学和使用Process经常会出错的例子。
1. 多余时钟的引入
在设计时往往会遇到这种情况,需要对外部某个输入信号进行判断,当其出现上跳或下跳沿时,执行相应的操作,而该信号不像正常时钟那样具有固定占空比和周期,而是很随机,需要程序设计判断其上跳沿出现与否。这时,很容易写出如下程序:
① process(Ctl_a) -- Ctl_a即为该输入信号
② begin
③ if Ctl_a’event and Ctl_a=’1’ then
④ … … ; --执行相应操作
⑤ end if ;
⑥ end process;
由于出现第③行这类语句,综合工具自动默认Ctl_a为时钟,某些FPGA更会强行将该输入约束到时钟引脚上。而设计者的初衷只是想将其作为下位机的状态输入以进行判断。上面的程序容易造成多时钟现象,增加设计的难度。解决的办法可以如下,将Ctl_a增加一级状态Ctl_areg寄存,通过对Ctl_a和Ctl_areg状态判断上跳与否,改正程序如下:
① process(clk)
② begin
③ if clk’event and clk=’1’ then
④ Ctl_areg<=Ctl_a;--产生相邻状态 -----判别上升沿的办法
⑤ if Ctl_areg=’0’ and Ctl_a=’1’ then--上跳判断
⑥ … … ; --执行相应操作
⑦ end if;
⑧ end if;
⑨ end process;
程序中第④行用以产生两个相邻状态,第⑤行对前后状态进行判断是否有上跳现象发生。其中,需注意的是clk的时钟频率应明显快于Ctl_a信号的变化频率,以保证正确采样。
2. 输出多驱动
误用Process经常会引起输出多驱动源的发生,即在两个以上的进程内对同一信号赋值操作。以下程序就出现了这类情况:
⑴ Proc_a: process(clk)
⑵ begin
⑶ if clk’event and clk=’1’ then
⑷ Dout<=Din_A;
⑸ end if
⑹ end process;;
⑺
⑻ Proc_b:process(sel_en)
⑼ begin
⑽ if sel_en=’1’ then
⑾ Dout<=Din_B;
⑿ end if;
⒀ end process;
进程Proc_a和Proc_b中都出现了对Dout的赋值语句,设计者原本的想法是,只要合理控制好clk和sel_en输入,使其不发生冲突,即clk上升沿时sel_en不为’1’;sel_en为’1’时,不出现clk的上升沿,这样Proc_a,Proc_b两个进程就不会发生冲突。但综合时,综合工具会将所有可能情况全部罗列进去,包括第⑶行和第⑽行同时成立的情况,此时对于Dout就有Din_A和Din_B两个输入驱动,Dout不知接收哪一个,因此该程序无法综合,改正的方法是只要将两个进程合并成一个即可。
由于进程在VHDL中的重要性,对此专门做了一个总结如下:
(1)一个进程中不允许出现两个时钟沿触发,(Xilinx公司CoolRunner系列CPLD支持单个时双钟的双触发沿除外)
(2)对同一信号赋值的语句应出现在单个进程内,不要在时钟沿之后加上else语句,如
if clk’event and clk=’1’ then - else … 的结构,现有综合工具支持不了这种特殊的触发器结构
(3)当出现多层IF语句嵌套时,最好采用CASE语句替代,一是减少多层嵌套带来的延时,二来可以增强程序的可读性
(4)顺序语句如IF语句、CASE语句、LOOP语句、变量赋值语句等必须出现在进程、函数或子程序内部,而不能单独出现在进程之外
(5)进程内部是顺序执行的,进程之间是并行运行的;VHDL中的所有并行语句都可以理解为特殊的进程,只是不以Process结构出现,其输入信号和判断信号就是隐含的敏感表
(6)并不是有process的段落就代表一个时序电路,要看进程敏感表,也行它只是个组合逻辑电路。。。
与软件语言相比,VHDL最重要的特点就在于它的并行运行特性,当设计好的电路上电后,器件内部所有信号将同时并发工作,而不会以软件方式按照程序顺序执行,即使在进程内部也是趋向并行工作的。
因此,VHDL学习过程中,应加强硬件概念的理解,没有硬件概念或是硬件概念不强,在设计时,往往会将VHDL设计以软件编程的方式来处理,而得出一些不可思议的结果。
文章评论(0条评论)
登录后参与讨论