VHDL时序电路:D触发器/十进制加减可逆计数器/偶数分频器/位移寄存器

时序电路概述

什么是时序电路

  • 与时序电路相对的是组合逻辑电路,其没有记忆功能,输出取决于输入

  • 而时序电路有记忆功能,下一步的输出受被记忆的当前状态影响,还可以进一步分为两类

    • Moore型

      下一状态的输出依赖于电路的当前状态,其状态变化依赖于时钟(只能同步更新)

    • Mealy型

      输出不仅依赖于电路的当前状态,还依赖于当前的输入信号,其输出在输入变化后立即发生,不依赖时钟的同步(可以异步更新)

使用VHDL构建时序电路

  • 前文提到的VHDL基本语法,如条件、循环等语句可以构建组合逻辑电路,在此基础上增添检测电路的状态来决定输出的功能,便构成了时序逻辑电路
  • 使用VHDL构建时序电路,重点在于不完整的条件语句

D触发器

  • 介绍时序电路,从D触发器开始,因为其是最基本的时序电路元件,更复杂的JK触发器、T触发器都可以用D触发器构成;先看D触发器的代码,再介绍其中用到的语法

  • D触发器的功能:

    有两个输入:时钟信号CLK与输入D;一个输出Q

    在CLK上升沿时,Q的值随D的值变化而变化;其他时候D值改变不影响Q的值,Q将维持原值直到下一次更新

    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    ENTITY DFF1 IS
    	PORT(CLK, D:IN STD_LOGIC;
    			  Q:OUT STD_LOGIC);
    	END;
    ARCHITECTURE bhv OF DFF1 IS
    	SIGNAL Q1:STD_LOGIC;
    	BEGIN
    		PROCESS(CLK,Q1)
    		BEGIN
    			IF CLK'EVENT AND CLK ='1'
    				THEN Q1<=D;
    			END IF;
    		END PROCESS;
    		Q<=Q1;
    	END bhv;
    

边沿触发检测

边沿检测语句

  • 信号属性函数EVENT

    信号标识符'EVENT是对信号在当前一个极小时间段内进行检测的语句,其作用是检查是否发生了事件

    所谓事件,指的是信号从一种取值范围转变为另一种取值范围

  • 边沿检测表达式

    时序电路受时钟信号控制,为在程序中实现这一点,需要对时钟信号进行边沿检测

    IF CLK'EVENT AND CLK ='1'这就是一种边沿检测表达式,其功能是检测CLK信号是否到来上升沿

  • 具体工作流程如下

    1. 使用信号属性函数CLK'EVENT来检测CLK信号是否有跳变

    2. 之后,使用与操作符AND判断CLK信号是否处于高电平

    3. 当这个表达式为真,就表示CLK信号跳变至高电平,也就是产生了上升沿

边沿检测的其他表述

  • 信号属性函数LAST_VALUE

    上述的检测方法存在缺陷:CLK信号未必是从0跳变至1,若其从Z态跳变至1,依旧符合表达式,但就不是上升沿了,为了确保是上升沿,可以使用如下表达式:

    CLK'EVENT AND (CLK='1') AND (CLK'LAST_VALUE='0')

    LAST_VALUE语句检测信号在最近一次事件发生前的值

  • 使用程序包中的检测方式

    使用STD_LOGIC_1164中的函数来检测上升沿:

    IF rising_edge(CLK),同理有检测下降沿的falling_edge(CLK)

双边沿检测

  • 注意,一个进程语句引导的顺序语句内,只能检测一个边沿!在同一进程内对同一信号的双边沿处理是错误的
  • 而且,在不同进程中对同一信号的双边沿处理也是错误的,因为在并行信号中对同一信号赋值,最终不能知道赋的是哪个值
  • 可见VHDL不允许对同一个信号在时钟的两个边沿进行数据赋值处理,但现实中的确存在双边沿器件,其原理是时钟的上升沿写入数据,下降沿读出数据,所控制对象并非同一个端口,因此本质上还是单边沿操作

不完整条件语句

  • 所谓不完整条件语句,就是只有IF,却缺少ELSE的条件语句

  • 以上文的D触发器代码为例:

    		PROCESS(CLK,Q1)
    		BEGIN
    			IF CLK'EVENT AND CLK ='1'
    				THEN Q1<=D;
    			END IF;--只写了信号是上升沿时的操作,如果信号不是上升沿,就不知道如何处理了
    		END PROCESS;
    
    1. 其通过条件语句IF来判断信号是否满足条件(CLK信号上升沿)以进行下一步操作(将D值赋给Q1),

    2. 但是,这个条件语句对所有可能发生的条件给出对应的处理方式(没有ELSE),

      也就是说,在不满足设定的条件时,它将不进行操作,Q1不会改变

    3. 因此,Q的值被保持,除非满足了IF的条件,才会被更新

  • 像这样的过程必须引入时序元件来保持Q1的值,可见时序电路构建的基础就是不完整的条件语句


属性描述语句

属性语句的定义

  • 上文提到的EVENTLAST_VALUE都是属性语句

  • 一个数据对象只能有一个值,而可以有多个属性,某个项目的的特定属性可以用一个值或者表达式来表示,使用预定义属性描述语句就可以对这些属性加以访问,格式如下:

    属性测试项目名'属性标识符
    --属性测试项目名即属性对象,可以是类型、过程、函数、信号、变量……由相应的标识符表示
    --属性标识符是属性名
    
  • 以上文的EVENT为例来看属性语句的作用:

    CLK'EVENT
    --这样的写法对CLK信号这个属性对象在短时间段内是否发生了事件这一属性进行了访问
    --如果CLK发生了事件,那么这一语句返回TURE
    

信号类属性

  • EVENT最常用,已经反复提及,不再赘述

  • STABLE

    其功能与EVENT相反,它表示信号在一段时间内无事发生

    NOT(CLK'STABLE AND CLK='1')--这一句实际上不可综合,因为边沿测试语句不能当作操作数对待
    (CLK'EVENT AND CLK='1')
    --这两句意义相同
    

数据区间类属性

  • RANGE

    对属性项目取值区间进行测试,返还内容不是一个具体值,而是一个区间,以FOR LOOP语句为例展示其功能

    SIGNAL range1 : IN STD_LOGIC_VECTOR(0 TO 7)
    
    FOR i IN range1'RANGE LOOP
    --这样,i的循环范围即同range1,是(0 TO 7)
    

    另外还有REVERSE_RANGE,其功能同上,但是返回的范围是相反的,按上面的例子,返回的将是(7 DOWNTO 0)

数值类属性

  • 这一类的属性测试函数用于对测试目标的一些数值特性进行测试

  • LEFT和RIGHT
    --首先定义数据类型来用于演示
    TYPE obj IS ARRAY (0 TO 15) OF BIT;--定义obj,是BIT类型的数组,共有15个位
    
    S1 <= obj'LEFT;--测得obj最左侧位是15位,整数类型的S1被赋值15
    S2 <= obj'RIGHT;--测得obj最右侧位是0位,整数类型的S2被赋值0
    
  • HIGH和LOW

    类似于LEFT和RIGHT,接上例

    S1 <= obj'HIGH;--测得obj最高位是15位,整数类型的S1被赋值15
    S2 <= obj'LOW;--测得obj最低位是0位,整数类型的S2被赋值0
    

数组属性

  • LENGTH

    对数组的宽度或元素的个数进行测定

    TYPE obj IS ARRAY (0 TO 7) OF BIT;
    VAR:=obj'LENGTH;--得到var=8
    

常用的时序电路设计

计时器

  • 实用的十进制计数器
    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    USE IEEE.STD_LOGIC_UNSIGNED.ALL;
    ENTITY CNT10 IS
    	PORT(CLK,RST,EN,LOAD:IN STD_LOGIC;
    		 DATA:IN STD_LOGIC_VECTOR(3 DOWNTO 0);--预置数
    		 DOUT:OUT STD_LOGIC_VECTOR(3 DOWNTO 0);--计数值
    		 COUT:OUT STD_LOGIC	);--进位输出
    END CNT10;
    
    ARCHITECTURE bhv OF CNT10 IS
    BEGIN
    PROCESS(CLK,RST,EN,LOAD)
    	VARIABLE Q: STD_LOGIC_VECTOR(3 DOWNTO 0);
    	BEGIN
    		IF RST='0' THEN Q:=(OTHERS=>'0');
    		ELSIF CLK'EVENT AND CLK='1' THEN
    			IF EN='1' THEN
    				IF(LOAD='0') THEN Q:= DATA; ELSE
    					IF Q<9 THEN Q:Q+1;
    					ELSE Q:=(OTHERS=>'0');
    					END IF;
    				END IF;
    			END IF;
    		END IF;
    		IF Q="1001" THEN COUT<='1';
    		ELSE COUT<='0';
    		END IF;
    		DOUT <= Q;
    	END PROCESS;
    END bhv;
    
  • 加减可逆的十进制计数器
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.STD_LOGIC_UNSIGNED.ALL;
    --十进制可逆计数器,通过一个信号来判断应该加还是减,仿照74LS191
    --UD控制,低电平为加计数,高电平为减
    --EN为高电平时允许计数
    --RST为0时清零,LOAD为0时加载预置数DATA
    ENTITY timer IS
    PORT(CLK,RST,EN,LOAD,UD:IN STD_LOGIC;
    		DATA:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
    		DOUT:OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
    		COUT:OUT STD_LOGIC);--进位/借位输出
    END timer;
    
    ARCHITECTURE bhv OF timer IS
    BEGIN
    	PROCESS(CLK,RST,EN,LOAD,UD)
    	VARIABLE Q:STD_LOGIC_VECTOR(3 DOWNTO 0);--暂存计数值
    	BEGIN
    		IF RST='0' THEN Q:=(OTHERS=>'0');--RST清零
    		ELSIF CLK'EVENT AND CLK='1' THEN--时钟上升沿
    			IF EN='1' THEN--当计数使能时
    				IF(LOAD='0') THEN Q:=DATA;
    				ELSE
    					IF (UD='0') THEN 
    						IF(Q<9) THEN Q:=Q+1;
    						ELSE Q:=(OTHERS=>'0');--Q为9再加1,进位清零
    						END IF;
    					ELSE 
    						IF(Q>0) THEN Q:=Q-1;
    						ELSE Q:="1001";
    						END IF;
    					END IF;
    				END IF;
    			END IF;
    		END IF;
    		IF Q="1001" OR Q="0000" THEN COUT<='1';
    		ELSE COUT<='0';
    		END IF;
    		DOUT<=Q;
    	END PROCESS;
    END bhv;
    

分频器

  • 二分频

    每个上升沿才进行一次输出,因为从一个上升沿到下一个上升沿实际要经过两个CLK周期(CLK占空比1:1)

    process(clk)--clk输入时钟;
    begin
      if(clk;event and clk = '1')then
         clkout <= not clk;
      end if;
    end process;
    
  • 偶数次分频

    要进行2次以上的偶数分频,原理是利用一个计数器,计数值=分频值/2-1(因为计数从0开始),比如要进行4分频就接收两次CLK上升沿再输出

    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    USE IEEE.STD_LOGIC_UNSIGNED.ALL;
    
    entity divfreq is
    	--generic(N:integer:=6);
    	port(
    		clk:in std_logic;
    		div:in std_logic;--div决定分频系数,高电平是6分频,低电平是10分频
    		fout:out std_logic
    	);
    end divfreq;
    
    architecture bhv of divfreq is
    	signal cnt:integer:=0;
    	signal temp:integer:=2;
    	signal ftemp:std_logic;
    	begin
    		process(div)
    		begin
    			if(div='1') then temp<=2;
    			else temp<=4;
    			end if;
    		end process;
    
    		process(clk)
    		begin
    			if(clk'event and clk='1') then
    				if(cnt=temp) then
    					cnt <=0;
    					ftemp <= not ftemp;
    				else
    					cnt <= cnt+1;
    				end if;
    			end if;
    		end process;
    	fout <= ftemp;
    end bhv;
    

位移寄存器

  • 位移寄存器的功能是:当CLK上升沿到来时,LOAD为高电平,则把输入端口的8位二进制数并行置入位移寄存器中,作为串行右移输出的初始值,如果LOAD为低电平,则把寄存器中高七位右移一位,空出的最高位用最初并行预置数的最高位填补,被挤占的最低位则输出

    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    ENTITY SHFT IS
    	PORT(CLK,LOAD:IN STD_LOGIC;
    		 QB:OUT STD_LOGIC;
    		 DIN:IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    		 DOUT:OUT STD_LOGIC_VECTOR(7 DOWNTO 0) );
    	END SHFT;
    ARCHITECTURE bhv OF SHFT IS
    	SIGNAL REG8:STD_LOGIC_VECTOR(7 DOWNTO 0);
    BEGIN
    	PROCESS(CLK,LOAD)
    		BEGIN
    			IF CLK'ENENT AND CLK='1' THEN
    				IF LOAD='1' THEN REG8 <= DIN;
    				ELSE REG8(6 DOWNTO 0) <= REG8(7 DOWNTO 1);
    				END IF;
    			END IF;
    	END PROCESS;
    	QB <= REG8(0);
    	DOUT <= REG8;
    END bhv;
    

posted on 2024-12-17 23:20  无术师  阅读(106)  评论(0编辑  收藏  举报