引言
现场可编程门阵列(FPGA)的出现是超大规模集成电路(VLSI)技术和计算机辅助设计(CAD)技术发展的结果。FPGA器件具有集成度高、体积小、可以通过用户编程实现专门应用的特点。这些特点非常适合大学计算机教学中的计算机硬件实验。在计算机硬件实验中,三态电路有着广泛的应用,例如构建一个具有分时共享功能的总线电路就需要用到多个三态电路。传统的实验方法要先画出原理图,然后通过手工连线各个芯片来搭建三态电路。在基于FPGA的硬件实验中,一种方法是利用图形方式,在MAX+PLUSⅡ中画出三态电路图,并编译完成实现三态的功能;另一种方法是直接用VHDL语言编写出三态电路程序,同样需要编译完成实现三态的功能。但是,在MAX+PLUSⅡ环境下,应用三态电路时常会遇到了很多问题,这些问题阻碍着用VHDL语言正确使用三态电路的功能。我们在FPGA应用设计中也发现了类似问题,经过仔细的分析和对多种不同实现方法的尝试,最后掌握了正确的实现方法,同时也找出了一般方法出错的原因。
在MAX+PLUSⅡ环境下最常见的三态应用程序及问题
在MAX+PLUSⅡ环境下,由于软件本身提供了三态总线电路的模块,因此可以在VHDL编程时直接调用lpm_bustri模块。下面是一个用VHDL编写的8位单向总线电路的程序片断:
entity tri_bus is
port(a,b: in std_logic_vector(7 downto 0);
aen,ben: in std_logic;
q:out std_logic_vector(7 downto 0));
end tri_bus;
architecture tri_bus_body of tri_bus is
component lpm_bustri
generic(lpm_width:positive);
port (data : in std_logic_vector(lpm_width-1 downto 0);
enabledt: in std_logic:=‘0’;
tridata:inout std_logic_vector(lpm_width-1 downto 0));
end component;
signal temp: std_logic_vector(7 downto 0);
begin
u1:lpm_bustri
generic map(lpm_width=》8)
port map(data=》a,enabledt=》aen,tridata=》temp);
u2:lpm_bustri
generic map(lpm_width=》8)
port map(data=》b,enabledt=》ben,tridata=》temp);
q《=temp;
end tri_bus_body;
以上程序很简单,通过调用lpm中的三态模块,以实现三态输出传输至总线的功能。程序中将两个三态模块的输出连接在一起,构成一个8位总线,总线输出结果取决于两个三态模块中哪一个的使能信号有效。这段程序理论上不存在问题,然而编译却无法通过!编译器报错,指出信号temp被多次赋值。
显然,在u1和u2中有两次出现tridata=》temp,但这对于三态电路来说是允许的,因为三态输出是可以并联的。那么是否因为lpm_bustri模块不能正确实现三态功能呢?我们首先用图形方式来验证该模块的功能。由于只是验证,这里只设置了一位的数据输入和输出,如图1,图中a为数据输入端,q为数据输出端,aen为使能端。
图 1
该图形文件成功地通过了编译,而且仿真结果表明,三态功能完全正确,即使能aen有效,输出为a,使能aen无效,输出为高阻。然后,我们又用图形方式搭建了前面的VHDL程序逻辑,建好的图形文件如图2所示。
图 2
这个图形文件所示逻辑同前面VHDL程序的内容完全相同。信号q的输出取决于两个使能端中哪一个有效。然而编译还是出错,这次指出的错误是两个lpm_bustri的输出tridata连接在一起了。
从理论上来说,两个三态的输出是可以接在一起的。为了证明这一点我们不再采用lpm_bustri模块,而是在MAX+PLUSⅡ环境下用图形方式直接画出两个三态元件,然后再将它们的输出连接在一起,如图3。
图 3
这各图形文件顺利通过编译!仿真结果也完全正确,当aen使能有效,q输出为a;当ben使能有效,q输出为b。如果没有两个使能端均无效,输出为三态,如果两个使能端均有效,输出结果为a和b的线与。(由于在实际总线电路中,aen和ben不可能同时有效,所以此种情况并不影响结果的正确性,我们只要知道这种情况下的输出结果是两个信号的线与就可以了。)
修改后的三态电路应用程序
既然三态电路用图形方式在MAX+PLUSⅡ环境下可以正确实现,那么VHDL程序也应该是可以的。前面编译出错的程序和电路都是因为用到了lpm_bustri模块,如果不用它程序将如何修改呢?在这种疑问下我们改变了思路,修改后的VHDL程序如下:
library ieee;
use ieee.std_logic_1164.all;
entity tri_state is
port(a,b :in std_logic_vector(7 downto 0);
aen, ben :in std_logic;
q :out std_logic_vector(7 downto 0));
end tri_state;
architecture tri_state_body of tri_state is
signal control:std_logic_vector(1 downto 0);
begin
control(1)《=aen;
control(0)《=ben;
process(a,b,control)
begin
case control is
when “10”=》q《=a;
when “01”=》q《=b;
when others=》q《=(others=》‘Z’);
end case;
end process;
end tri_state_body;
这段VHDL程序同最初那段VHDL程序表达的功能是一样的,当aen使能有效,输出为a,当ben使能有效,输出则为b,如果不是这两种情况均输出三态。这次经过编译和仿真,功能正确实现了。
分析不同程序不同结果的原因
为什么最初的VHDL程序和用lpm模块搭建的图形会出错呢?带着这个问题将两个程序进行对比便可找出问题的所在。在修改后的程序中,我们用了一个control二维数组来控制对输出信号q的赋值,虽然输出信号q也是有两个输入源a和b,但是程序控制了它们赋值的时间,也就是不可能同时被赋值,所以编译器没有报错。而最初的程序的却出现temp被多次赋值,再来看看源程序:
u1:lpm_bustri
generic map(lpm_width=》8)
port map(data=》a,enabledt=》aen,tridata=》temp);
u2:lpm_bustri
generic map(lpm_width=》8)
port map(data=》b,enabledt=》ben,tridata=》temp);
q《=temp;
其中,虽然实际中不可能让三态的两个使能同时有效,但是对于编译器而言,它只能识别语句的逻辑,在上面那种逻辑下,aen和ben是完全可能同时有效的(仅仅是逻辑,只对语句而言)。如果这种情况发生,编译器将无法正确对q赋值,所以报错,提示信号temp被多次赋值,这完全是因为编译器严谨的结果。
在一个编译器中,语法的正确性检查是基于一种规则和逻辑的,它是适用于一切的语言描述。虽然三态的输出可以连接在一起,但是当程序直接将它们连接在一起的时候(也就是多源赋值的时候),编译器不可能因为程序编的是三态逻辑而通过这种潜在的错误,它的编译原理是基于语句而不是编出来的结果。这样就可以解释为什么简单的元件搭建出来的三态输出连接在一起可以通过编译,而用lpm模块描述出来的和我们最初的三态程序描述出来的三态输出却不能通过编译。因为它们都需要通过编译器的语法检查。而多源赋值又是一种潜在的错误,所以不能通过。那么有些人会不理解,都是图形描述,为何简单的元件搭建可以通过而高级的lpm模块搭建却不行?答案是高级的lpm模块也是用语言编写出来的,它也要通过编译器的语法检查。简单的元件搭建可以通过是因为在两个使能都成立的情况下,软件可以将输出信号按线与处理,从而避免了冲突和不确定性。那么如何利用lpm模块实现三态的功能?这就要看如何运用在具体的应用中了。如果仅仅需要将两个三态的输出连接在一起,通过各自的使能端控制输出的话,可以在一个原理图中分别建立两个单独的lpm模块,不要将它们的输出端连接在一起(否则编译器会报错),然后直接编译,编译通过后可以在配置FPGA的时候将建立的两个lpm模块的输出连接到同一个管脚上,经过验证,这样在应用中也是可以正常实现三态功能的(因为其避免了在编译前就将输出口连接在一起而不能通过编译的情况的发生)。
总结
通过以上的分析和说明,我们知道MAX+PLUSⅡ环境下,是可以正确实现三态电路应用的。主要有三种方法:
① 用类似于我们上面改编出来的程序来实现;
② 用自搭建的图形描述实现;
③ 在配置FPGA时再将不同的三态输出端连接到同一个管脚上。
需要注意的是在编写程序或者利用软件本身提供的模块搭建电路时要了解软件自身或者所调用的模块在编译时是否会引起编译器的冲突。在了解了编译器编译原理后,才能在编写和调试程序时游刃有余,及时发现和改正问题。
创新观点:本文指出了在MAX+PLUSⅡ环境下运用三态电路常见错误的原因,并指明了几种正确的实现方法。
责任编辑:gt
评论
查看更多