原创 典型逻辑电路的Verilog-HDL描述

2009-7-18 11:54 3780 6 6 分类: FPGA/CPLD

典型逻辑电路的Verilog-HDL描述


 















Verilog-HDL与CPLD/FPGA设计应用讲座

 第 5 讲 典型基本逻辑路的Verilog-HDL描述  
5.1 数据选择器  
5.2 编码器  
5.3 同步RS触发器  
5.4 带有复位端的同步D触发器  


  本讲列举几个典型又简单的基本逻辑电路的Verilog-HDL描述。需要说明,仅有本讲的知识还不能具备用Verilog-HDL描述逻辑电路和系统的基本功,但却可以通过几个例子了解Verilog-HDL描述逻辑电路的过程和方法。


5.1 数据选择器


  数据选择器又称为多路开关,简称MUX(Multiplexer)。它的逻辑功能是在地址选择信号SEL的控制下,从多路输入(A、B…)数据中选择某一路数据作为输出,一个2-1数据选择器的逻辑电路符号如图1所示。


  
      图1 2-1数据选择器的逻辑符号


  逻辑电路如图2所示,有关A、B及SEL的解释读者还可参考有关书籍(1)。
点击看大图  
 
               图2 2-1数据选择器的逻辑电路


例1 2-1数据选择器的Verilog-HDL描述


/* 2-1 SELECTOR */


module SEL ( A, B, SEL, F ); //模块名及端口参数,范围至endmodule。


  input  A, B, SEL; //输入端口定义


  output  F ; //输出端口定义


  assign F = ~SEL & A | SEL & B; //assign语句,实现功能:F=(/SEL·A)+(SEL·B)。


endmodule //模块结束



例2 使用case语句的2-1数据选择器


/* 2-1 SELECTOR */


module SEL ( A, B, SEL, F );      //模块名及参数定义,范围至endmodule。


  input A, B, SEL;         //输入端口定义


  output F;             //输出端口定义


  assign F = SEL2_1_FUNC ( A, B, SEL );   //用assign语句实现function函数调用


  function SEL2_1_FUNC;           //function函数及函数名,至endfunction为止。


    input A, B, SEL;           //输入端口定义


    case ( SEL )             //case语句,至于endcase为止。


      0: SEL2_1_FUNC = A;        //功能:SEL=0时,返回A。


      1: SEL2_1_FUNC = B;        //功能:SEL=1时,返回B。


    endcase               //case语句结束


  endfunction               //function函数结束


endmodule                  //模块结束



【解说】if_else语句的使用方法


  if_else 语句用来先判定所给的条件是否满足,然后根据判定的结果(真或假)来执行所给出的两种操作之一。



例3 使用if_else语句的2-1数据选择器


  /* 2-1 SELECTOR */


module SEL ( A, B, SEL, F );  //模块名及参数定义,范围至endmodule。


  input A, B, SEL;      //输入端口定义


  output F;         //输出端口定义


  assign F = SEL2_1_FUNC ( A, B, SEL ); //用assign语句实现function函数调用


  function SEL2_1_FUNC;         //function函数及函数名,至endfunction为止。


    input A, B, SEL;          //输入端口定义


    if ( SEL == 0 )           // if语句,与else配合使用。


      SEL2_1_FUNC = A;        //如果SEL= 0,则返回A。


      else             //if相呼应


      SEL2_1_FUNC = B;        //否则返回B


  endfunction              //function函数结束


endmodule                 //模块结束



  【解说】function函数


  function函数的目的是返回一个用于表达式的值 ,函数的形式为:


  function <返回值位宽或类型说明> 函数名;


    端口定义;


    局部变量定义;


    其他语句;


  endfunction


  在例2中,用到了function函数。


  下面是2-1数据选择器的测试模块。



  例4 2-1数据选择器的测试模块


  /* 2-1 SELECTOR_TEST */


`timescale 1ns/1ns           //将仿真的单位设定为 1ns


module SEL_TEST;            //测试模块名及参数定义,范围至endmodule。


  reg [1:0] IN;            //寄存器定义,输入端口,数据宽度为2bit。


  reg SEL_IN;             //寄存器定义,输入端口。


  wire F;               //线网定义,输出端口。


  SEL SEL ( IN[0], IN[1], SEL_IN, F ); //底层模块名、实例名及参数。


  always #150 SEL_IN = ~SEL_IN;    //每隔150ns,SEL_IN反相一次(注1)。


  always #50 IN = IN + 1;       //每隔50ns,IN = IN + 1(注2)。


  initial begin            //从initial始,输入信号变化。begin与end相呼应。


  SEL_IN = 0; IN = 0;         //一开始,SEL_IN = 0,IN = 0。


                    //之后,按(注1)、(注2)规律变化。


  #450 $finish;            //仿真到450ns时结束


  end                 //与begin呼应


endmodule               //模块结束



  Verilog HDL 提供了多种编写测试模块的方法。上例中的 always 语句意指反复执行。例如"always #50 IN = IN + 1;"是说每隔一个仿真单位(在此为50ns)IN便加1;而"always #150 SEL_IN = ~SEL_IN;"是说每隔一个仿真单位(在此为150ns)SEL_IN便反转一次。在initial程序段中,将 "SEL_IN" 和 "IN" 初始化为0,并用"#450 $finish"标示仿真在450ns时结束。
  图3给出了2-1数据选择器的仿真结果。通过该图我们可以验证用Verilog-HDL描述的数据选择器是否正确。例如:随机抽取其中的某个时刻(光标指向处)。此时,SEL_IN=0,IN[0]=0,IN[1]=1,F=0。符合数据选择器的功能。
 
点击看大图  


                                                       图3 2-1数据选择器的仿真结果


5.2 编码器
  在数字系统中,常需要将特定意义的信息编成若干二进制代码,这个过程称为编码,实现编码的数字电路称为编码器。
  图4示出了一个2位二进制编码器的功能框图。它有4个输入端,即IN0、IN1、IN2和IN3,2个输出端Y0和Y1。在此,设图中的4个开关在同一时刻只有一个闭合,并规定开关处于打开状态时,对应的输入为逻辑"0",闭合时为逻辑"1"。


   


         图4 二进制编码器的功能框图



  上述二进制编码器的真值表如表1所示。


 
        表1 二进制编码器的真值表
 
  由此可知其逻辑表达式为:


  Y0= IN1+ IN3
  Y1= IN2+ IN3


  由逻辑关系式便可以很容易地画出逻辑电路,如图5所示。由逻辑电路图可知,当Y0 和Y1均为逻辑"0"时,与输入IN0没有任何关系。因此,当4个开关都打开时,输出Y0 和Y1也为逻辑"0"。这在实际应用问题中是需要考虑的,在此不作进一步的讨论。
   


图5 二进制编码器的逻辑电路



例5 2位二进制编码器的Verilog-HDL描述
/* Data Difinision */
`define SW_IN0 4'b0001      //编译时,将SW_IN0转换为4bit的二进制数0001
`define SW_IN1 4'b0010      //编译时,将SW_IN0转换为4bit的二进制数0010
`define SW_IN2 4'b0100      //编译时,将SW_IN0转换为4bit的二进制数0100
`define SW_IN3 4'b1000      //编译时,将SW_IN0转换为4bit的二进制数1000
/* ENCORDER */
module ENC ( IN, Y );          //模块名及参数定义,范围至endmodule。
  input [3:0] IN;           //输入端口定义
  output [1:0] Y;           //输出端口定义
  assign Y = FUNC_ENC ( IN );     //用assign语句实现function函数调用


  function [1:0] FUNC_ENC;      //function函数及函数名,至endfunction为止。


  input [3:0] IN;           //输入端口定义


  case ( IN )             //case语句,至于endcase为止。


    `SW_IN0: FUNC_ENC = 0;      //当IN= SW_IN0时,返回0。


    `SW_IN1: FUNC_ENC = 1;      //当IN= SW_IN1时,返回1。


    `SW_IN2: FUNC_ENC = 2;      //当IN= SW_IN2时,返回2。


    `SW_IN3: FUNC_ENC = 3;      //当IN= SW_IN3时,返回3。


  endcase               //case语句结束


  endfunction             //function函数结束
endmodule                //模块结束



例6 2位二进制编码器的测试模块
`timescale 1ns/1ns
/* ENCORDER_TEST */
module ENC_TEST;             //测试模块名及参数定义,范围至endmodule。
  reg [3:0] IN;             //寄存器定义,输入端口,数据宽度为4bit。
  wire [1:0] Y;             //线网定义,输出端口,数据宽度为2bit。
  integer i,j;             //定义i、j为抽象型整形数。


  ENC ENC ( IN, Y );          //底层模块名、实例名及参数。


  initial begin            //从initial始,输入信号变化。begin与end相呼应。


  j = {2'b10, 2'b0, 1'b1};       //位拼接运算,意为10与00与1拼接。


  for ( i = 0; i <= 3; i = i + 1 )   //循环,在begin与end之间。


  begin          // begin与end相呼应


  j=j>>1;         //右移1位


  IN = j[3:0];       //代入


  #200;          //经过200ns


  end           //与begin呼应


  $finish;        //停止运行


  end           //与begin呼应


endmodule         //模块结束



  2位二进制编码器的的仿真结果如图6所示,IN表示编码器的输入,Y表示输出。通过观察,可以得到仿真结果和前述设计的2位二进制编码器完全一致。即:当IN为"1000、0100、0010、0001"时,Y对应为"11、10、01、00"。


点击看大图  
                                           图6 2位二进制编码器的仿真结果



5.3 同步RS触发器
  同步RS触发器有一个时钟端CLK。只有在时钟端的信号电平发生上跳(正触发)或下跳(负触发)时,触发器的输出才发生变化。
图7为正触发型同步RS触发器的逻辑符号。R和S为输入端,CLK为时钟端,上升沿触发有效,Q和 是互为反相的输出端。


  
    图7 正触发型同步RS触发器的逻辑符号


  图8为正触发型同步RS触发器的时序图。从图中可以看出,只有当时钟脉冲CLK的上升沿到来时电路的输出状态才有可能发生变化,而变化与否又取决于R端和S端。R端和S端均为低电平时,输出保持电路原有状态(在图8中,原有状态为"不定");R端和S端分别为高电平和低电平时,输出Q为低电平;R端和S 端分别为低电平和高电平时,输出Q为高电平;R端和S端均为高电平时,输出为"不定"。在实际应用中应避免这种R端和S端均为高电平的情况出现。


 
          图8 正触发型同步RS触发器的时序图



例7 同步RS触发器的Verilog-HDL描述
/* SY_RS_FF */
module SY_RS_FF ( R, S, CLK, Q, QB ); //模块名及参数定义,范围至endmodule。
  input R, S, CLK;         //输入端口定义
  output Q, QB;          //输出端口定义
  reg Q;              //寄存器定义
  assign QB = ~Q;         //assign语句,QB=/Q。
  always @( posedge CLK )     //在CLK的上跳沿,执行以下语句。
  case ({ R ,S })         //case语句,至于endcase为止。
    1:Q <= 1;          //当R,S的组合为01,则令Q=1。
    2:Q <= 0;          //当R,S的组合为01,则令Q=1。
    3:Q <= 1'bx;         //当R,S的组合为11,则令Q为1bit的数,数值为不定(x)。
  endcase            //case语句结束
endmodule              //模块结束


例8 同步RS触发器的测试模块
/* SY_RS_FF_TEST */
`timescale 1ns/1ns               //将仿真的单位设定为 1ns
module SY_RS_FF_TEST;              //测试模块名及参数定义,范围至endmodule。
  reg R, S, CLK;               //寄存器定义,输入端口。
  wire Q, QB;                 //线网定义,输出端口。
  Parameter STEP = 40;            //定义STEP为40,在以后,凡STEP都视为40。



  SY_RS_FF SY_RS_FF ( R, S, CLK, Q, QB );  //底层模块名、实例名及参数。


  always #( STEP/2 ) CLK = ~CLK;      //每隔STEP/2,CLK就反转一次。
  initial begin               //从initial始,输入信号变化。begin与end相呼应。
  CLK = 1; R = 0; S = 0;           //参数初始化
  #( 2*STEP-10 ) R = 1;            //在经过( 2*STEP-10 )后,R = 1。
  #( STEP/2 ) R = 0;              //在经过( STEP/2 )后,R = 0。
  #( STEP/2 ) S = 1;              //在经过( STEP/2 )后,S = 1。
  #STEP R = 1;                 //在经过STEP后,R = 1。
  #( STEP/2 ) S = 0;             //在经过( STEP/2 )后,S = 0。
  #STEP R = 0;                 //在经过STEP后,R = 0。
  #STEP $finish;               //在经过STEP后,停止运行。
  end                     //与begin呼应
endmodule                   //模块结束



  图9为同步RS触发器的仿真结果。由图可以看出,当R端和S端均为相同电平时,输出波形为"不变"或"不定"。


点击看大图  
                                                     图9 同步RS触发器的仿真结果


5.4 带有复位端的同步D触发器[To top]
  在同步D触发器的实际应用中,有时需要有一个非同步的复位端。这种D触发器的逻辑符号如图10所示。其时序图如图11所示。


 
                图10 带有复位端的同步D触发器的逻辑符号


 
     图11 带有复位端的同步D触发器的时序图


  从图11可以看出,在初始状态下,电路的逻辑处于"不定"状态,复位脉冲的到来将电路初始化为Q=0的状态。随后,在D端的控制下,电路的状态作相应的翻转。图中,t1-t6时刻说明了电路的逻辑状态。


带有复位端的同步D触发器的Verilog-HDL描述


例9 带有R端D触发器的Verilog-HDL描述


/* R_SY_D_FF */


module R_SY_D_FF ( RB, D, CLK, Q, QB );  //模块名及参数定义,范围至endmodule。
  input RB, D, CLK;           //输入端口定义
  output Q, QB ;            //输出端口定义
  reg Q;                //寄存器定义


  assign QB = ~Q;             //assign语句,QB=/Q。
  always @( posedge CLK or negedge RB ) //如果CLK端有上跳或RB端有下跳脉冲,
                      //则执行下面(Q <= ( !RB )? 0: D;)的语句。
  Q <= ( !RB )? 0: D;           //如果RB是低电平,则Q=0,否则Q=D。
endmodule                  //模块结束


例10 D触发器的测试模块
`timescale 1ns/1ns     //将仿真的单位设定为 1ns
module R_SY_D_FF_TEST;   //模块名及参数定义,范围至endmodule。
  reg RB, D, CLK;    //寄存器定义,输入端口。
  wire Q, QB;      //线网定义,输出端口。
  parameter STEP = 50; //定义STEP为50,在以后,凡STEP都视为50。


  R_SY_D_FF R_SY_D_FF ( RB, D, CLK, Q, QB ); //底层模块名、实例名及参数。 always #( STEP/2 )   CLK = ~CLK;                 //每隔STEP/2,CLK就反转一次。


  initial
  begin            //从initial始,输入信号变化。begin与end相呼应。


  RB = 1; D = 0; CLK = 0;   //参数初始化


  #( STEP/4 ) RB = 0;     //在经过( STEP/4 )后,RB = 0。


  #( STEP*3/4 ) D = 1;    //在经过( STEP*3/4 )后,D = 1。


  #( STEP/4 ) RB = 1;    //在经过( STEP/4 )后,RB = 1。


  #( 2*STEP ) D = 0;     //在经过( 2*STEP )后,D = 0。


  #STEP D = 1;        //在经过STEP后,D = 1。


  #( STEP/2 ) RB = 0;    //在经过( STEP/2 )后,RB = 0。


  #( STEP/2 ) RB = 1;    //在经过( STEP/2 )后,RB = 1。


  #( STEP/4 ) $finish;   //在经过STEP后,停止运行。


  end           //与begin呼应


endmodule          //模块结束


  图12示出了带有复位端的同步D触发器的仿真结果。RB是复位信号,当RB="0"时,Q="0"。从图中可以看出:在复位信号RB的作用下,Q="0"。其后,RB="1"。当CLK的上跳沿到来时,Q就变化为和D相同的状态;上跳沿以外的其它时刻,Q保持状态不变。


点击看大图  
 


           图12 带有复位端的同步D触发器的仿真结果
参考书
(1) John M. Yarbrough著,李书浩等译:数字逻辑应用与设计, 机械工业出版社,北京,pp141-145, 2000.4.
(2) 夏宇闻:复杂数字电路与系统的Verilog HDL设计技术,北京航空航天大学出版社,1998.
(3) 常晓明:Verilog-HDL实践与应用系统设计,北京航空航天大学出版社, 2003.1.

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条