简   介:介绍了在VHDL编程设计中,描述方法对电路结构的影响,不同的状态机描述方法,层次化设计的基本思想和原则,Block RAM的结构、VHDL程序、宽度和深度组合形式,基于IP Core的Block RAM设计,数字延迟锁相环(DLL,Delay Locked Loop)、全局时钟网络(Global Clock Networks)、DCM(数字时钟管理器,Digital Clock Manager)的结构特点与应用。重点是掌握在VHDL编程设计中,锁存避免、寄存器使用、括号使用、并行结构、资源共享、数值比较等描述方法对电路结构的影响,单进程状态机、多进程状态机的基本结构模型,层次化设计的基本结构与方法,Block RAM的结构、编程、宽度和深度组合,基于IP Core的Block RAM设计,使用DLL、全局时钟网络、DCM消除时钟时延、频率合成和时钟相位调整等方法。 
 
VHDL设计是行为级的设计,所带来的问题是设计者的设计思考与实际电路结构是相脱节的。设计者主要是根据VHDL的语法规则,对系统目标的逻辑行为进行描述,然后通过综合工具进行电路结构的综合、编译、优化,通过仿真工具进行逻辑功能仿真和系统时延的仿真。实际设计过程中,由于每个设计工程师对语言规则、对电路行为的理解程度不同,每个人的编程风格不同,往往同样的系统功能,描述的方式是不一样的,综合出来的电路结构更是大相径庭。因此,即使最后综合出的电路都能实现相同的逻辑功能,其电路的复杂程度和时延特性都会有很大的差别,甚至某些臃肿的电路还会产生难以预料的问题。从这些问题出发,很有必要深入讨论在VHDL设计中如何简化电路结构,优化电路设计的问题。用VHDL进行设计,最终综合出的电路的复杂程度,除取决于设计要求实现的功能的难度外,还受设计工程师对电路的描述方法和对设计的规划水平的影响。最常见的使电路复杂化的原因之一是在设计中存在许多本不必要的类似LATCH的结构。而且由于这些结构通常都由大量的触发器组成,不仅使电路更复杂,工作速度降低,而且由于时序配合的原因会导致不可预料的结果。 
 
以下有2段设计,如果单从语法上来看是没有任何错误的,而且编译时都可以通过,但是如果从电路结构上考虑,它们都存在问题。 
 
Exam1: Process(A,B) 
 
Begin 
 
If A=’1’ then 
 
Q <= B ; 暗指锁存 
 
End if; 
 
End process; 
 
Exam2: process(C) 
 
Begin 
 
Case C is 
 
When ‘0’=> Q <= ‘1’; 
 
Z <= ‘0’; 
 
When ‘1’=> Q <= ‘0’; 缺少Z的值 
 
End case; 
 
End process; 
 
仔细观察,在Exam1和Exam2进程中的语句都有同一个毛病。Exam1进程中的if语句仅仅指明了A在高电平(1)的时候将B的值传到Q端,并没有指明A在低电平(0)的时候Q端应该是什么值。综合工具在综合时,发现这种不完全的状态描述,会将其综合为锁存(latch)。锁存的结构如图7.1.1所示。在ISE中,综合工具XST会发出一个警告: Found 1-bit latch for signal <q>。锁存是由与或非逻辑组成的,而这种结构在系统中多半会埋下不稳定的种子。 
 
再看看Exam2进程,有两个输出信号,信号的值在Case语句中被决定,但是Z的值只在C为‘0’时有明确的说明,当C为高‘1’时设计者并没有明确指出。所以同样会使综合工具理解为锁存。 
 
2.描述寄存器代替锁存 
 
既然锁存存在不稳定,那么有必要找到一个好的替代,那就是寄存器。寄存器由时钟触发,很大程度上抑制了毛刺。所以,尽可能的使用寄存器而避免锁存。 
 
下面是不同的D触发器的VHDL描述: 
 
DFF:process(clk) --D触发器 
 
Begin 
 
If rising_edge(clk) then 
 
Q <= A; 
 
End if; 
 
End process; 
 
DFF_AR:process(clk,reset)--带异步复位的D触发器 
 
Begin 
 
If reset=’1’ then 
 
Q <=’0’; 
 
Elsif rising_edge(clk) then 
 
Q <= A; 
 
End if; 
 
End process; 
 
DFF_AS:process(clk,reset)--带异步置位的D触发器 
 
Begin 
 
If reset=’1’ then 
 
Q <=’1’; 
 
Elsif rising_edge(clk) then 
 
Q <= A; 
 
End if; 
 
End process; 
 
DFF_SS:process(clk,reset)-- 带同步置位的D触发器 
 
Begin 
 
if rising_edge(clk) then 
 
if reset=’1’ then 
 
Q <=’1’; 
 
else 
 
Q <= A; 
 
End if; 
 
End if; 
 
End process; 
 
从上面的例子可以了解到不同的描述对电路结构的影响,在下面的一个例子中,有两种不同的加法描述,其电路结构也是不同的。 
 
3.使用括号描述想要的结构 
 
(1)Out1<=I1+I2+I3+I4; 
 
(2)Out2<=(I1+I2)+(I3+I4); 
 
从结构上看,第一种描述中没有使用括号,电路结构为三层,加法一级一级的进行。它的特点是,四个输入I1,I2,I3,I4到达加法器的路径不相等。第二种描述中的使用了括号,电路结构分为两层,显然它的特点是四个输入信号到达加法器的路径是相等的。这里并不能简单的评论两种结构的优劣,必须根据实际应用情况分析。 
 
4.并行结构 
 
在VHDL语言描述中,进程中的语句是顺序执行的。如果要设计一个并行的电路,不要使用顺序语句,而要使用并行语句。 
 
方法1. Process(A,B,C) 
 
begin 
 
if C=’0’then 
 
Y<=A; 
 
Else 
 
Y<=B; 
 
End if; 
 
End process; 
 
方法2. Y<=A when C=’0’else B; 
 
如果电路结构是并行的,那么使用方法2描述要比方法1好。 
 
5.资源共享 
 
如果有可能的话,尽量使资源共享。一块FPGA芯片中的资源是有限的。如果不有效利用仅有的资源,将会导致资源不够,系统冗余过多,稳定性也变会差。 
 
加法器的资源共享 
 
加法器的输出Z是A加上C或者B,A与C、B哪个输入相加由选择信号S决定,由于在设计中采用了多路选择器(Mux),在结构上可以得到两种形式。一种方法是采用了两个16位的加法器,两个加法器的结果通过MUX由S选择输出。而只采用了一个加法器,它将C、B信号通过MUX后的输出送到加法器的一端,另一端连接A。两种结构都能达到设计的要求,显然,后者的设计设计占用的资源要小些。 
 
在使用VHDL描述时,在process中比较容易做到资源共享,源程序如下: 
 
process(A,B,C,S) 
 
begin 
 
if S=’1’ then 
 
Z <= A+B; 
 
Else 
 
Z <= A+C; 
 
End if; 
 
End process; 
 
6.integer和std_logic_vector 
 
在VHDL中,信号使用限制了位数的std在VHDL中,信号使用限制了位数的std_logic_vector(标准逻辑向量)型,不使用integer(整型)。如果使用integer,最好在后面加上约束条件,例如:a:integer range 0 to 7;。如果使用了integer而没有对其进行位数的约束,那么在32位的PC机中,综合后将是一个32bit的信号,会造成大量的浪费。 _logic_vector(标准逻辑向量)型,不使用integer(整型)。如果使用integer,最好在后面加上约束条件,例如:a:integer range 0 to 7;。如果使用了integer而没有对其进行位数的约束,那么在32位的PC机中,综合后将是一个32bit的信号,会造成大量的浪费。 
 
7.1.2 不同的状态机描述 
 
1. 状态机的选型 
 
按状态机的输出方式分类,有限状态机可分为Mealy型和Moore型。从输出时序上看,状态机的输出是当前状态和所有输入信号的函数,它的输出是在输入变化后立即发生的,不依赖时钟的同步。Moore型状态机输出则仅为当前状态的函数,状态机的输入发生变化还必须与状态机的时钟同步。 
 
由于Mealy状态机的输出不与时钟同步,所以当在状态译码比较复杂的时候,很容易在输出端产生大量的毛刺,这种情况是无法避免的。在一些特定的系统中,这些毛刺可能造成不可预料的结果。但是,由于输入变化可能出现在时钟周期内的任何时刻,这就使得Mealy状态机对输入的响应可以比Moore状态机对输入的响应要早一个时钟周期。Moore状态机的输出与时钟同步,可以在一定程度上剔除抖动。从稳定性的角度来讲,建议使用Moore状态机以提高系统的稳定性。 
 
在实际工程中,具体电路有具体的设计要求,可能适应于Moore或Mealy状态机,也可能两种都可以实现设计,所以设计者必须根据实际情况和经验综合决定采用哪种状态机。 
 
2. 状态编码 
 
状态编码,是指定义状态机现态和次态,一般 有3种方式: 
 
(1)方式1 
 
signal curren_state:std_logic_vector(1 downto 0) 
 
signal next_state:std_logic_vector(1 downto 0) 
 
方式1定义的状态有比较多的毛病。首先,这种方式定义的状态为逻辑向量,缺乏具体的状态含义,程序的可读性较差,更重要的是,设计后期调试修改比较麻烦。 
 
2)方式2 
 
Type mystate is (st0,st1,st2,st3); 
 
Signal curren_state,next_state:mystate; 
 
采用方式2定义的状态有具体的状态含义,可读 性好,易于调试和修改。使用attribute可以对状态进行手动编码,通过设置用户型编码方式,综合工具很容易根据手动状态编码进行高效的综合。 
 
(3)方式3 
 
Signal curren_state,next_state:std_logic_vector(1downto 0); 
 
Constant st0:std_logic_vector(1 downto 0):=”00”; 
 
Constant st1:sta_logic_vector(1 donwot 0):=”01”; 
 
Constant st2:sta_logic_vector(1 donwot 0):=”10”; 
 
Constant st3:sta_logic_vector(1 donwot 0):=”11”; 
 
方式3比方式1要好,因为方式3的可读性比方式 1要好。但是方式3的修改没有方式2方便。 
 
状态机的编码在ISE中有6种,其中常用的是顺序编码和一位热码。在ISE中可以通过修改综合约束来指定状态机的编码。方法如下: 
 
①综合前,在Process for current sources窗口中选中Synthesize,单击鼠标右键,弹出下拉菜单,选中Properties(属性)。 
 
②在弹出的菜单中有3个选项栏,选中HDL options选项栏,如图7.1.6所示。在Properties name(属性名)下的第一项便是FSM Encoding Algorithm(有限状态机编码算法),在Value中打开下拉菜单,可以看到几种编码方式。其中Auto(自动)为ISE的缺省值,在一般情况下,如果目标芯片是FPGA,那么XST会将状态机编码为One-Hot(一位热码)。使用One-Hot(一位热码)的好处是,状态转换快,状态译码简单。 
 
采用Sequential(顺序编码)的好处是,所需要的寄存器少,冗余状态相对要少,系统进入其他状态的机会就更少。 
 
还有一个比较常用的编码方式,User(用户)编码方式。在前面讲过,设计者是可以手动编码的,只要加上Attribute语句便可,这里给出一个例子: 
 
需要提醒大家注意的是,HDL Options中的设置的优先级要高于用户的attribute属性。也就是说,如果使用了attribute语句进行了手动编码,但是在FSM Encoding Algorithm中选择的是Gray(格雷)码,那么实际综合的效果是Gray(格雷)编码。 
 
例子: 
 
type STATE_TYPE is (S1, S2, S3, S4); 
 
attribute ENUM_ENCODING: STRING; 
 
attribute ENUM_ENCODING of STATE_TYPE: type is "1110 1101 1011 0111"; 
 
上面的四个状态S1,S2,S3,S4其实也是一位热码,是用‘0’作为热码。选择User编码,那么综合后的实际状态机编码就是设计者在attribute中描述的状态码字。 
 
3. 多进程状态机 
 
在状态机的描述中,经典的应该属于3进程状态机,基本结构可以由下面的模型描述: 
 
SYNC_PROC: process (CLOCK, RESET) --同步进程 
 
begin 
 
if (RESET='1') then --现态 <= 初始状态; 
 
elsif (CLOCK'event and CLOCK = '1') then 
 
--现态 <= 次态; 
 
end if; 
 
end process; 
 
COMB_PROC1: process (--现态, 输入信号) –状态转化进程 
 
begin 
 
case 现态 is 
 
when初始状态=> 
 
if (--转换条件) then 
 
--次态赋值 
 
end if; 
 
--其他所有状态转换的描述 
 
end case; 
 
end process; 
 
COMB_PROC2:process(现态, 输入端) --输出描述进程 
 
Begin 
 
Case 现态 
 
When初始状态=> 
 
if (输入端的变化) then 
 
--输出赋值 
 
--其他所有状态下输出的描述 
 
end case 
 
end process; 
 
或者: 
 
COMB_PROC2:process(现态, 输入端) --输出描述进程 
 
Begin 
 
Case 现态 
 
When初始状态=> --输出赋值 
 
--其他所有状态下输出的描述 
 
end case 
 
end process; 
 
以上模型描述的3进程状态机中的各个进程分工相当明确。SYNC_PROC进程完成状态的同步描述和状态机的初始化,COMB_PROC1进程完成对状态转换的描述,COMB_PROC2完成对输出的描述。3个进程各负责一个工作互不扰,第一个进程的电路是时序逻辑,第二,三进程的电路是组合逻辑。COMB_PROC2进程中可能出现两种描述。第一种描述,输出端是现态和输入的函数,是Mealy状态机。第二种描述,输出端是仅为现态的函数,是Moore状态机。 
 
当然,COMB_PROC1 ,COMB_PROC2两个进程完全可以写到一个进程中。那么状态机的进程就分为时序进程和组合进程。 
 
4. 单进程状态机 
 
在实际的设计中,在输入端引入噪声是难免。如果使用的是Mealy状态机,噪声很容易传到输出端口,在输出端口出现毛刺,如果这个状态机的输出要被用于作为其他模块的同步信号的话,系统设计很有可能会失败。那么在这种情况下,最好采用Moore状态机。但是,在3进程状态机中,输出信号是由状态译码得来的,输出进程描述的电路是组合逻辑,组合逻辑没有抑制毛刺的能力。所以3进程的Moore状态机只是相对3进程的Mealy状态机有一定的抗干扰能力。 
 
为了使系统更加稳定,可以在输出组合逻辑后面在加一级寄存器,采用时钟边沿触发,这样,可以在很大程度上剔除毛刺,抑制噪声的干扰。这样采用单进程的状态机也可以达到设计目的。 
 
(1)单进程状态机的基本结构模型 
 
单进程状态机的基本结构可以由下面的模型描述: 
 
SYNC_PROC: process (CLOCK, RESET) --同步进程 
 
begin 
 
if (RESET='1') then 
 
--现态 <= 初始状态; 
 
elsif (CLOCK'event and CLOCK = '1') then 
 
--现态 <=现态; 
 
case 现态 is 
 
when初始状态=> if (--转换条件) then 
 
--现态赋值 
 
end if; 
 
if (--转换条件) then 
 
--输出赋值 
 
end if; 
 
--其他所有状态转换的描述 
 
--其他所有状态下输出的描述 
 
end if; 
 
end process; 
 
可以看出,由于状态机的所有工作都是在时钟上升沿触发下完成的,所以,输出组合逻辑的后面必定产生了一组寄存器,从而抑制了毛刺的产生。 
 
(2)状态机的比较 
 
下面以一个简单的状态机为例,验证上面的分析。其中EXAM1是2个进程的状态机,分为时序进程和组合进程;EXAM2是单进程状态机。这里不对状态机的工作做介绍,仅仅表现是两种方法描述同一个功能的状态机的差异。通过ISE5.2的RTL视图工具可以方便的看到综合后的实际结果,在RTL视图前对两种描述综合出的电路图做比较可以轻易地看出它们的不同。图7.1.7和图7.1.8分别为EXAM1和EXAM2的综合结果。 
 
①2个进程的状态机设计例 
 
EXAM1:-- EXAM1是2个进程的状态机,分为时序进程和组合进程。 
 
entity statemachine2 is 
 
Port ( in1 : in std_logic; 
 
in2 : in std_logic; 
 
reset : in std_logic; 
 
clk : in std_logic; 
 
out1 : out std_logic; 
 
out2 : out std_logic); 
 
end statemachine2; 
 
architecture Behavioral of statemachine2 is 
 
Type mystate is (st0,st1,st2,st3); 
 
Signal current_state,next_state:mystate; 
 
begin 
 
process (clk, RESET) 
 
begin 
 
if (RESET='1') then 
 
current_state <= st0; 
 
elsif rising_edge(clk) then 
 
current_state <= next_state; 
 
end if; 
 
end process; 
 
process(in1,in2,current_state) 
 
begin 
 
case current_state is 
 
when st0=> if in1='1' then 
 
next_state <= st1; 
 
end if; 
 
out1<='0'; 
 
out2<='0'; 
 
when st3=> if in1='0'and in2='0' then 
 
next_state <= st0; 
 
end if; 
 
out1<='1'; 
 
out2<='1'; 
 
when others => next_state <= st0; 
 
end case; 
 
end process; 
 
end Behavioral; 
 
②单进程状态机设计例 
 
EXAM2;-- EXAM2是单进程状态机 
 
entity statemachine is 
 
Port ( in1 : in std_logic; 
 
in2 : in std_logic; 
 
reset : in std_logic; 
 
clk : in std_logic; 
 
out1 : out std_logic; 
 
out2 : out std_logic); 
 
end statemachine; 
 
architecture Behavioral of statemachine is 
 
Type mystate is (st0,st1,st2,st3); 
 
Signal current_state:mystate; 
 
begin 
 
process (clk, RESET,in1,in2) 
 
begin 
 
if (RESET='1') then 
 
current_state <= st0; 
 
elsif rising_edge(clk) then 
 
current_state <= current_state; 
 
case current_state is 
 
when st0=> if in1='1' then 
 
current_state <= st1; 
 
end if; 
 
out1<='0'; 
 
out2<='0'; 
 
when st1=> if in2='1' then 
 
current_state <= st2; 
 
end if; 
 
out1<='1'; 
 
out2<='0'; 
 
when st2=> if in1='0'and in2='1' then 
 
current_state <= st3; 
 
end if; 
 
out1<='0'; 
 
out2<='1'; 
 
when st3=> if in1='0'and in2='0' then 
 
current_state <= st0; 
 
end if; 
 
out1<='1'; 
 
out2<='1'; 
 
when others => current_state <= st0; 
 
end case; 
 
end if; 
 
end process; 
 
end Behavioral; 
 
③比较 
 
2个进程的状态机和单进程状态机的电路不同,单进程状态机的输出端的后面采用了一组FDE(带使能的DFF)。采用单进程的状态机使用的资源要比2进程的状态机的要多。这也可以从综合报告中得到验证。 
 
a. 单进程状态机: 
 
Device utilization summary: 
 
--------------------------- 
 
Selected Device : 2s100epq208-6 
 
Number of Slices: 4 out of 1200 0% 
 
Number of Slice Flip Flops: 6 out of 2400 0% 
 
Number of 4 input LUTs: 7 out of 2400 0% 
 
Number of bonded IOBs: 5 out of 146 3% 
 
Number of GCLKs: 1 out of 4 25% 
 
b. 2进程状态机: 
 
Device utilization summary: 
 
--------------------------- 
 
Selected Device : 2s100epq208-6 
 
Number of Slices: 3 out of 1200 0% 
 
Number of Slice Flip Flops: 4 out of 2400 0% 
 
Number of 4 input LUTs: 6 out of 2400 0% 
 
Number of bonded IOBs: 5 out of 146 3% 
 
Number of GCLKs: 1 out of 4 25% |