时序电路的 VHDL 设计 Mealy 状态机设计要点: 设定若干状态; 用输入和状态控制进程; 用 case 语句分别选择每一个状态; 用 if 语句确定输入条件,指定相应的下一状态和输出值; 输出立即赋值(使用一个进程); 状态等待时钟条件满足再进行赋值(使用另一个进程); 例 Mealy状态机设计 该状态机具有 4 个状态,输入 x,输出 z;状态转换图如下所 示; library ieee; use ieee.std_logic_1164.all; entity mealy is port(x,clk: in std_logic; z: out std_logic); end mealy; architecture beh of mealy is type state is (s0,s1,s2,s3); signal current_state,next_state: state; begin aaa:process(current_state,x) begin case current_state is when s0=> if x='0' then z<='0';next_state<=s0; else z<='1';next_state<=s2; end if; when s1=> if x='0' then z<='0';next_state<=s0; else z<='0';next_state<=s2; end if; when s2=> if x='0' then z<='1';next_state<=s2; else z<='0';next_state<=s3; end if; when s3=> if x='0' then z<='0';next_state<=s3; else z<='1';next_state<=s1; end if; end case; end process aaa; sync:process begin wait until clk'event and clk='1'; current_state<=next_state; end process sync; end beh; counter 计数器 计数器通常以 clk 信号为基本输入,对输入信号进行计数: 在 clk 每个周期中改变一次计数器状态,状态可以输出; 经过 n 次计数后,计数器将回到初始状态,并给出进位输 出信号; 计数器的 VHDL 设计的要点在于正确安排状态的变化次 序,以及满足类型及运算的相容性; 例 1 p.708 表 8-14 4 位二进制加法计数器 74163 具有同步复位,同步置数和进位控制功能; library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity k74163 is port ( clk,clrl,ldl,enp,ent: in std_logic; d: in unsigned (3 downto 0); q: out unsigned (3 downto 0); rco:out std_logic); end k74163; architecture beh of k74163 is signal iq: unsigned (3 downto 0); begin process (clk,ent,iq) begin if clk'event and clk='1' then if clrl='0' then iq<= (others=>'0'); elsif ldl='0' then iq<=d; elsif (ent and enp)='1' then iq<=iq+1; end if; end if; if (iq=15) and (ent='1') then rco<='1'; else rco<='0'; end if; q<=iq; end process; end beh; 将上述程序中的 iq<=iq-1 ,就构成减法计数器; 注意:对于 usigned 类型,在位数固定的条件下,加/ 减 1 的运 算可以自动产生循环,不会有进位或借位问题发生; 若希望输入和输出都是 std_logic 类型,则可在程序中进 行如下变化: library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity k74163 is port ( clk,clrl,ldl,enp,ent: in std_logic; d: in std_logic_vector (3 downto 0); q: out std_logic_vector (3 downto 0); rco:out std_logic); end k74163; architecture beh of k74163 is signal iq: unsigned (3 downto 0); begin process (clk,ent,iq) begin if clk'event and clk='1' then if clrl='0' then iq<= (others=>'0'); elsif ldl='0' then iq<=unsigned(d); elsif (ent and enp)='1' then iq<=iq+1; end if; end if; if (iq=15) and (ent='1') then rco<='1'; else rco<='0'; end if; q<=iq+0; end process; end beh; 在 p.709 表 8-15 中显示了余 3 码计数器,与 74163 相比,变 化为:只有 10 个状态(从 3 到 12) 在状态 12 时输出进位信号; 程序的改变只有两句: elseif (ent and enp)='1' and (iq=12) then iq<="0011"; if (iq=12) and (ent='1') then rco<='1'; 前一句指定加法循环的起点和终点;后一句指定进位信号; 采用类似方案很容易构成任意进制的计数器; 计数器设计的一个关键问题在于电路综合的效果,很多 综合工具会将计数器的每一步(加 1)作为一个加法器进行综 合,导致庞大的组合电路;为了避免出现这种情况,通常只 用上述方式构成小规模计数器,而对大规模计数器则采用结 构设计方式,由小规模计数器级连构成; 例: 16 位加法二进制计数器的结构设计(模 65536) 采用 4 个 74163 级连构成 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity kcount16 is port ( clk,clrl,ldl,en: in std_logic; d: in unsigned (15 downto 0); q: out unsigned (15 downto 0); rco:out std_logic); end kcount16; architecture str of kcount16 is component k74163 port ( clk,clrl,ldl,enp,ent: in std_logic; d: in unsigned (3 downto 0); q: out unsigned (3 downto 0); rco:out std_logic); end component; signal co0,co1,co2:std_logic; begin u1: k74163 port map (clk,clrl,ldl,en,en,d(3 downto 0),q(3 downto 0),co0); u2: k74163 port map (clk,clrl,ldl,co0,co0,d(7 downto 4),q(7 downto 4),co1); u3: k74163 port map (clk,clrl,ldl,co1,co1,d(11 downto 8),q(11 downto 8),co2); u4: k74163 port map (clk,clrl,ldl,co2,co2,d(15 downto 12),q(15 downto 12),rco); end str; 该计数器同样具有同步复位、同步置数等功能; 计数器的应用 作为时序电路的重要功能器件,计数器可以构成许多特 定的功能部件; 将时钟输入信号作为时间信号,则输入与输出之间可以 保持一种时间关系,利用这种关系,可以将计数器设计为 定 时器,在电路中,每隔几个时钟周期就给出一个输出脉冲, 可用于规范电路中各功能块的执行时间(例如串- 并数据转换 电路); 若将输入时钟信号设定为标准时间单位(例如 ms、 s)等, 将计数器的模设置为满足相应的时间进位关系,则可以设计 出各类计时工具,如电子秒表、电子钟等; 采用模足够大的计数器,通过控制其使能端,使其在单 位时间对输入信号( clk)进行计数,则可以得到单位时间内 信号周期数,这正是数字频率计的基本设计思想; 若考虑输入信号和输出信号的频率关系,则计数器本身 就是分频器,对于二进制计数器,各状态输出端分别输出对 应输入信号的 2 n 分频,如 2 分频、4 分频、8 分频等; 在实际数字电路中,通常用晶体振荡器为电路提供外部 参考时钟,该时钟频率极高,由于对于计数器模的设置可以 得到输入信号的任意整数分频,降低时钟频率,因而计数器 可以用于设计频率发生器(时钟信号发生器) ,为内部不同器 件提供不同的时钟信号; 将计数器与二进制译码器结合,可构成顺序脉冲发生器 ; 将计数器与数据选择器结合,可构成序列信号发生器; Shift-Register 移位寄存器 移位寄存器可以寄存 n 位二进制数(可以将其视为串行 排列的数组),在每个时钟周期,内部寄存数据移动 1 位(向 右或向左),同时有 1 位数据移入或移出; 利用进程中简单的代入语句可以很方便地设计移位寄存 器; 例 1 简单的 4 位右移移位寄存器; 功能要求:串行输入、串行输出 library ieee; use ieee.std_logic_1164.all; entity kshfreg1 is port ( clk,d: in std_logic; y:out std_logic); end kshfreg1; architecture beh of kshfreg1 is signal q0,q1,q2:std_logic; begin process(clk) begin if (clk'event and clk='1') then q0<=d; q1<=q0; q2<=q1; y<=q2; end if; end process; end beh; 利用连接运算符号& ,也可以将上述程序改为如下形式: architecture beh of kshfreg1 is signal q:std_logic_vector(0 to 2); begin process(clk) begin if (clk'event and clk='1') then q<=d & q(0 to 1); y<=q(2); end if; end process; end beh; 例 2 典型的 8 位移位寄存器 功能要求:可实现异步复位、同步置数(并行输入) 、状态输 出(并行输出)、串入串出的左/ 右移控制; library ieee; use ieee.std_logic_1164.all; entity kshfreg2 is port ( clk,dr,dl: in std_logic; dir,clr,ld: in std_logic; d:in std_logic_vector(7 downto 0); q:out std_logic_vector(7 downto 0)); end kshfreg2; architecture beh of kshfreg2 is signal qi:std_logic_vector(7 downto 0); begin process(clk,clr) begin if clr='1' then qi<=(others=>'0'); elsif (clk'event and clk='1') then if ld='1' then qi<=d; elsif dir='0' then qi<= qi(6 downto 0) & dr; else qi<=dl & qi(7 downto 1); end if; end if; q<=qi; end process; end beh; 对于上述程序进行适当改动,可以构成各种类型的功能器件。 移位寄存器的应用 利用串行输入并行输出,可以形成 串 -并转换接口,接收 串行通信线路送来的信息;利用并行输入串行输出,可以形 成 并 -串转换接口,将系统信息通过串行方式发送出去;串行 输入/ 串行输出可以作为延迟器,实现信号的延迟; 在右移模式下,将内部信号 di(7)赋值给 di(0),就构成环 行计数器,在每个时钟周期,内部寄存数据循环右移 1 位; 同样可以设计循环左移的寄存器;循环移位寄存器的状态输 出可以构成简单的序列信号发生器或 顺序脉冲发生器,在时 钟信号驱动下,该电路在串行输出端输出周期性的序列信号, 或在并行输出端输出顺序脉冲信号; 适当循环移位寄存器设置内部数据及反馈结构,可以构 成 移位寄存型计数器,实现特定状态变化计数的功能,也可 以构成更为复杂的序列信号发生器;这种带反馈结构的移位 寄存型计数器可以作为伪随机信号发生器使用,在各类纠错 编码的设置中也经常采用这种结构(例如循环冗余校验器 CRC); 将串行输入端口接地,由并行输入/ 输出移位寄存器中的 数据,在一个时钟周期中,可以实现对寄存数据的乘 2(右移) 或除 2(左移)运算电路,在算术运算中发挥重要作用; 在 8.5.10 节( p.743—746)中,还讨论了小型移位寄存器 的结构设计及其他一些类型的移位寄存器设计问题。