我们通过一个小小的例子来学习VHDL程序设计,同时温习Quartus II工程开发,一举两得。 这个小小的例子最小目标就是要告诉大家怎么使用IF-THEN语句,另一方面通过这个小小的实例告诉大家好的coding就是对设计最大的优化。
IF-THEN是VHDL中典型的行为描述(Behavioral Descriptions)语句,并在进程(Process)中按顺序执行。IF-THEN语句多级嵌套即IF-ELSIF则产生更为复杂的输出逻辑,这些输出又都是互斥关系。同时,IF-ELSIF还将会比IF-THEN使用更多的逻辑资源而且会使得布局布线更加困难。问题在于如何未使用ELSIF的情况下有条件地测试这些互斥信号呢?答案是使用IF-THEN语句。以下代码是我们的测试目标:
以下是代码片段: LIBRARY ieee; USE ieee.std_logic_1164.ALL;
ENTITY if_thn IS PORT ( current_state : IN std_logic_vector(8 DOWNTO 0); x,y,z : IN std_logic; state_out : OUT std_logic_vector(2 DOWNTO 0) ); END if_thn;
ARCHITECTURE behavior OF if_thn IS
CONSTANT s0 : std_logic_vector(8 DOWNTO 0) := "000000000"; CONSTANT s1 : std_logic_vector(8 DOWNTO 0) := "100000001"; CONSTANT s2 : std_logic_vector(8 DOWNTO 0) := "100000010"; CONSTANT s3 : std_logic_vector(8 DOWNTO 0) := "100000100"; CONSTANT s4 : std_logic_vector(8 DOWNTO 0) := "100001000"; CONSTANT s5 : std_logic_vector(8 DOWNTO 0) := "100010000"; CONSTANT s6 : std_logic_vector(8 DOWNTO 0) := "100100000"; CONSTANT s7 : std_logic_vector(8 DOWNTO 0) := "101000000"; CONSTANT s8 : std_logic_vector(8 DOWNTO 0) := "110000000";
SIGNAL output1 : std_logic; SIGNAL output2 : std_logic; SIGNAL output3 : std_logic;
BEGIN
PROCESS(current_state,x,y,z) BEGIN IF (current_state = s1) OR (current_state = s3) OR (current_state = s4) THEN output1 <= x; ELSIF (current_state = s0) OR (current_state = s2) OR (current_state = s5) THEN output2 <= y; ELSIF (current_state = s6) OR (current_state = s7) OR (current_state = s8) THEN output3 <= z; ELSE output1 <= '0'; output2 <= '0'; output3 <= '0'; END IF; END PROCESS;
state_out <= output1 & output2 & output3;
END behavior; |
下面我们创建一个新的Quartus II工程,打开Quaruts II软件,从File菜单选择New Project Wizard,如图1所示。
第一步:设置工作路径,如图1所示,大家可以在自己的电脑上选择一个没有空格和中文字符的路径作为自己的工作路径。我们为这个新创建的工程命名为if_thn,软件自动为顶层设计实体命名为if_thn。点击Next进入下一步。
第二步:如图2所示,将上述代码保存到一个名为if_thn.vhd的文件里。器件家族选择Cyclone II,并将该源最好放到工作目录下,当然大家以后项目复杂以后可以专门建立一个存放源文件的目录,笔者喜欢在工程目录下创建一个src作为存放所有源文件的目录,创建一个debug目录用于存放虚拟JTAG调试工具以及调试数据的目录。闲话少说,浏览找到源文件,点击Add按钮将源文件加入到工程,点击Next进入下一步。
图2:给工程添加设计文件
第三步:如图3所示,器件型号选择笔者demo板上的EP2C5Q208C8,剩下的设置默认即可,点击Finish按钮完成Quartus II工程创建。
图3:为工程指定设计目标器件
在Quartus II菜单Process里的Start中选择综合命令,或者直接软件工具条中选择综合命令按钮
图4:IF语句综合出Latch的警告
我们可以通过RTL Viewer来进一步检查这些警告信息,从Quartus II软件的Tools菜单下Netlist Viewers里选择RTL Viewer命令,即可打开RTL Viewer来查看刚才的综合结果。如图5所示,我们看到三个输出是各自经过一个Latch出来的,后面笔者在介绍时性分析以及优化的时候,会告诉大家编译后意外产生的这些不受欢迎的锁存器会使得设计的时序分析复杂化,而且也不能精确反映设计者脑子里的本领设计意图。
图5:使用RTL Viewer查看工程综合结果
关闭RTL Viewer,下面我使用另外一个网表查看工具再来看看上述综合结果,即从刚才打开RTL Viewer的菜单里我们选择打开Technolgy Map Viewer,如图6所示。
图6:使用Technology Map Viewer查看工程综合结果
图6所示的Technology Map Viewer显示了工程实际的逻辑实现,也即前面代码中的IF-THEN语句的实际逻辑实现。我们可以看到最后三个输出经过了最后一级的三个LCELL,注意这三个LCELL的输出除了驱动设计中的三个输出意外,这三个LCELL的输出同时还反馈到LCELL的输入。后面我们将讨论这种没有必要的反馈走线会使得设计变得意想不到的复杂,应该在coding的时候加以避免。
既然我们已经发现了代码中的这些问题,那么下面我就通过修改代码来解决这些问题。将上述代码源文件另存为if_thn2.vhd。将原代码里使用单个IF-ELSIF语句修改为使用多个IF-THEN语句,同时修改程序的实体名字,使之和文件名字保持一致。修改后的代码如下所示。
LIBRARY ieee; USE ieee.std_logic_1164.ALL;
ENTITY if_thn2 IS PORT ( current_state : IN std_logic_vector(8 DOWNTO 0); x,y,z : IN std_logic; state_out : OUT std_logic_vector(2 DOWNTO 0) ); END if_thn2;
ARCHITECTURE behavior OF if_thn2 IS
CONSTANT s0 : std_logic_vector(8 DOWNTO 0) := "000000000"; CONSTANT s1 : std_logic_vector(8 DOWNTO 0) := "100000001"; CONSTANT s2 : std_logic_vector(8 DOWNTO 0) := "100000010"; CONSTANT s3 : std_logic_vector(8 DOWNTO 0) := "100000100"; CONSTANT s4 : std_logic_vector(8 DOWNTO 0) := "100001000"; CONSTANT s5 : std_logic_vector(8 DOWNTO 0) := "100010000"; CONSTANT s6 : std_logic_vector(8 DOWNTO 0) := "100100000"; CONSTANT s7 : std_logic_vector(8 DOWNTO 0) := "101000000"; CONSTANT s8 : std_logic_vector(8 DOWNTO 0) := "110000000";
SIGNAL output1 : std_logic; SIGNAL output2 : std_logic; SIGNAL output3 : std_logic;
BEGIN
PROCESS(current_state,x,y,z) BEGIN IF (current_state=s1) OR (current_state=s3) OR (current_state=s4) THEN output1 <= x; ELSE output1 <= '0'; END IF; IF (current_state=s0) OR (current_state =s2) OR (current_state=s5) THEN output2 <= y; ELSE output2 <= '0'; END IF; IF (current_state=s6) OR (current_state =s7) OR (current_state=s8) THEN output3 <= z; ELSE output3 <= '0'; END IF; END PROCESS;
state_out <= output1 & output2 & output3;
END behavior; |
源文件修改后,将其加入到工程中,具体方法可以通过settings里加入文件到工程的方法,也可以直接在工程导航(Project Navigator)下的Files页面通过右击鼠标弹出的Add/Remove Files in Project…命令来添加文件到工程或者从工程中删除文件。如图7所示。
图7:网工程添加文件或从工程删除文件
文件添加到工程以后,if_thn2.vhd会出现在图7所示的Files列表里,鼠标右击该文件,选择Set as Top-Level Entity命令将其设置为工程的顶层实体。最后,对工程进行重新综合。查看编译报告,这次只消耗了9个逻辑单元,而且没有任何蓝色的警告信息。
启动RTL Viewer查看重新综合结果,如图8所示。可以看到RTL直观显示了修改后的代码只有简单的9个Equation,看不到后面的锁存器。
图8:使用RTL Viewer查看工程修改后综合结果
关闭RTL Viewer,我们再次启动Technology Map Viewer,如图9所示。可以看到工程实际逻辑实现消耗的9个LCELL,而且和图2-15比较,我们看到最后三个驱动输出引脚的LCELL的输出没有任何反馈到其输入的逻辑路径。
图9:使用Technology Map Viewer查看工程修改后综合结果
所以通过比较我们可以得出一个结论是IF-ELSE-END IF语句除了避免不受欢迎的意外产生的锁存器意外,还可以更有效的利用逻辑。
coyoo 2013-7-22 15:18
用户403664 2013-7-22 14:53
用户377235 2013-7-18 13:03
output3 <= '0'; state_out <= output1 & output2 & output3; 感觉这个地方是不是应该用 |运算,结果貌似为0
用户413652 2013-7-17 21:11
coyoo 2013-7-9 19:54
用户403664 2013-7-9 17:01
coyoo 2013-7-9 16:17
我用了一下,是这样吗?名字似乎叫“代码样式”。
用户403664 2013-7-9 15:39
coyoo 2013-7-9 12:35
用户305823 2010-10-29 15:47