新闻  |   论坛  |   博客  |   在线研讨会
典型逻辑电路的Verilog-HDL描述
0750long | 2009-07-20 12:14:48    阅读:3862   发布文章

典型逻辑电路的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触发器的仿真结果

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客