时序电路的 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)中,还讨论了小型移位寄存器
的结构设计及其他一些类型的移位寄存器设计问题。