当前位置: 首页 > 教程 > Verilog >

1.2 Verilog 开关级建模

精华
小牛编辑
281浏览
2023-03-14

关键词:MOS, CMOS, 双向开关, PAD

开关级建模是比门级建模更为低级抽象层次上的设计。在极少数情况下,设计者可能会选择使用晶体管作为设计的底层模块。随着电路设计复杂度及相关先进工具的出现,以开关为基础的数字设计慢慢步入黄昏。目前,Verilog 仅仅提供了用逻辑值 0、1、x、z 作为相关驱动强度的数字设计能力,因此,Verilog 中晶体管也仅被当做导通或截止的开关。


MOS 开关

MOS 开关有 2 种,用如下关键字声明:

nmos(N 类型 MOS 管)   pmos(P 类型 MOS 管)  
rnmos (带有高阻抗的 NMOS 管) rpmos(带有高阻抗的 PMOS 管)

MOS 管用来为开关逻辑建模,数据从输入流入输出,可通过适当设置来开、关数据流。

带有阻抗的 MOS 管,源极到漏极的阻抗较高,且在传递信号时会减小信号的强度。

MOS 管开关结构图如下所示。

例化时,MOS 管第一个端口为输出端,第二个端口为数据输入端,第三个端口为控制输入端。

实例

    //tri
    pmos pmos1           (OUTX , IN1 , CTRL1 ) ;
    //no instantiation name
    nmos                 (OUTX1 , IN1 , CTRL2 ) ;

MOS 管真值表如下所示,与三态门非常相似。

nmos 控制端         pmos 控制端      
  0 1 x z     0 1 x z
0 z 0 0/z 0/z   0 0 z 0/z 0/z
1 z 1 1/z 1/z   1 1 z 1/z 1/z
x z x x x   x x z x x
z z x x x   z x z x x

CMOS 开关

CMOS 开关用关键字 cmos 和 rcmos (带有高阻抗)声明。

CMOS 有一个数据输出,一个数据输入和 2 个控制输入,结构示意图如下:

信号 PControl 与 Ncontrol 通常是互补的。当信号 Ncontrol 为 1 且 PControl 为 0 时,开关导通。 当信号 Ncontrol 为 0 且 PControl 为 1 时,开关输出为高阻。可以将 CMOS 开关看做是 NMOS 与 PMOS 开关的组合体。

例化时,CMOS 管第一个端口为输出端,第二个端口为数据输入端,第三个端口为 Ncontrol 控制输入端,第四个端口为 Pcontrol 控制输入端。

CMOS 开关例化格式如下。

实例

    //coms
    cmos c1               (OUTY , IN1 , NCTRL , PCTRL ) ;
    //no instantiation name
    cmos                 (OUTY1 , IN1 , NCTRL , PCTRL ) ;

既然 CMOS 可以看做是 NMOS 与 PMOS 开关的组合体,所以还可以用这两种 MOS 开关去搭建 CMOS 开关,如下:

实例

    //the same 2-way instantiation of cmos
    nmos n2               (OUTY , IN1 , NCTRL ) ;
    pmos p2               (OUTY , IN1 , PCTRL ) ;

CMOS 真值表与 MOS 开关类似,注意 Ncontrol 与 Pcontrol 信号的互补性。


双向开关

NMOS、PMOS、CMOS 开关门都是从漏极向源极导通,方向是单向的。Verilog 中还提供了双向导通的开关器件,数据可以双向流动,两边的信号都可以是驱动信号。

双向开关及其阻抗模式的关键字声明如下:

tran  tranif1  tranif0   rtran  rtranif1  rtranif0

双向开关结构图如下:

tran 开关为两个信号直接的缓存,inout1 或 inout2 均可以是驱动信号。

tranif1 仅当 control 信号为 1 时,开关两边的信号导通。当 control 为 0 时,两个信号断开,有驱动源的信号会和驱动源保持一致的信号值,没有驱动源的信号则呈现为高阻状态。

tranif0 同理。

因此,双向开关常用来进行总线或信号之间的隔离。

例化时,双向开关前

两个端口为数据端,第三个端口为 control 控制输入端。

双向开关例化举例如下:

实例

    tranif0 tr0               (inout1 , inout2 , control ) ;
    //no instantiation name
    tranif1                   (inout1 , inout2 , control ) ;

电源和地

晶体管级电路需要源极(Vdd, 逻辑 1)与地极(Vss, 逻辑 0),分别用关键字 supply1 和 supply0 来定义。 用法如下:

实例

    supply1              VDD ;
    supply0              GND ;
    wire                 siga = VDD ; //siga is connected to logic 1
    wire                 sigb = GND ; //sign is connected to logic 0

PAD 模型仿真

在《Verilog 教程》的《5.1 Verilog 模块与端口》一节中,涉及过 PAD 模型的编写与仿真。下面,利用三态门对 PAD 模型进行重塑,上下拉功能固定,并利用双向开关对 PAD 连接性进行测试。

利用三态门编写的带有 pullup 功能的 pad 模型如下,pulldown 功能的 pad 模型切换下注释即可。

实例

module PADUP (
    //DIN, pad driver when pad configured as output
    //OEN, pad direction(1-input, o-output)
    input        DIN , OEN ,
    inout        PAD ,
    //pad load when pad configured as input
    output       DOUT
  ) ;
    //input:(not effect pad external input logic), output: DIN->PAD
    bufif0 (PAD , DIN , OEN ) ;     //0-output
    bufif1 (DOUT , PAD , OEN ) ;     //1-input
    pullup (PAD ) ;
    //pulldown (PAD);   //pulldown
endmodule

利用双向开关控制 PAD IO 连接性的 testbench 编写如下。

测试流程为 PAD0/1 互连,然后 PAD0 作为输出,PAD1 作为输入,驱动 PAD0, 读取 PAD1 的值。然后两者方向各自取反,驱动 PAD1 读取 PAD0 的值。

PAD2/3 测试过程完全一样。

实例

`timescale 1ns / 1ns
module test ;
    parameter    PULL_UP         = 1 ;
    parameter    PULL_DOWN       = 0 ;
    parameter    IO0_OUT         = 0 ;
    parameter    IO1_OUT         = 1 ;
    parameter    IO2_OUT         = 2 ;
    parameter    IO3_OUT         = 3 ;
    parameter    IO0_IN           = 0 ;
    parameter    IO1_IN           = 1 ;
    parameter    IO2_IN           = 2 ;
    parameter    IO3_IN           = 3 ;
    reg [ 3 : 0 ]    DIN , OEN ;
    wire [ 3 : 0 ]   DOUT ;
    wire [ 3 : 0 ]   PAD ;
    //test connection control, using tranif1
    reg [ 1 : 0 ]    con_ena ;
    tranif1 (PAD [ 0 ] , PAD [ 1 ] , con_ena [ 0 ] ) ;
    tranif1 (PAD [ 2 ] , PAD [ 3 ] , con_ena [ 1 ] ) ;
    reg err = 0 ;
    task test_io_conn ;
      //test pull
      input             pull_type ;
      //test conn
      input [ 1 : 0 ]       xout ;     //output postion
      input [ 1 : 0 ]       yin ;     //output postion
      DIN [xout ]         = ~pull_type ;
      # 20 ;
      if (DOUT [yin ] != ~pull_type ) begin
          $display ( "write value and get value is: %h, %h" , ~pull_type , DOUT [yin ] ) ;
         err         |= 1 ;
      end
      DIN [xout ]         = pull_type ;
      # 20 ;
      if (DOUT [yin ] != pull_type ) begin
          $display ( "write value and get value is: %h, %h" , pull_type , DOUT [yin ] ) ;
         err         |= 1 ;
      end
    endtask
    initial begin
      con_ena   = 2'b01 ;
      OEN       = 4'b1111 ;
      # 13 ;
      //test between io0/io1
      OEN [ 0 ]     = 0 ;
      OEN [ 1 ]     = 1 ; //gpio0 -> gpio1
      test_io_conn (PULL_UP , IO0_OUT , IO1_IN ) ;
      OEN [ 1 ]     = 0 ;
      OEN [ 0 ]     = 1 ; //gpio0 -> gpio1
      test_io_conn (PULL_UP , IO1_OUT , IO0_IN ) ;
      OEN       = 4'b1111 ;
      con_ena   = 2'b10 ;
      OEN [ 2 ]     = 1'b0 ;
      OEN [ 3 ]     = 1'b1 ;
      test_io_conn (PULL_DOWN , IO2_OUT , IO3_IN ) ;
      OEN [ 3 ]     = 1'b0 ;
      OEN [ 2 ]     = 1'b1 ;
      test_io_conn (PULL_DOWN , IO3_OUT , IO2_IN ) ;
    end
   PADUP        u_pad_up0 ( DIN [ 0 ] , OEN [ 0 ] , PAD [ 0 ] , DOUT [ 0 ] ) ;
   PADUP        u_pad_up1 ( DIN [ 1 ] , OEN [ 1 ] , PAD [ 1 ] , DOUT [ 1 ] ) ;
   PADDOWN      u_pad_down3 ( DIN [ 2 ] , OEN [ 2 ] , PAD [ 2 ] , DOUT [ 2 ] ) ;
   PADDOWN      u_pad_down4 ( DIN [ 3 ] , OEN [ 3 ] , PAD [ 3 ] , DOUT [ 3 ] ) ;
    initial begin
      forever begin
          # 100 ;
          //$display("---gyc---%d", $time);
          if ( $time >= 1000 ) begin
            $finish ;
          end
      end
    end
endmodule // test

仿真结果如下。

由图可知,13ns 之内,4 个 PAD 均为输入时,PAD 值均与 pull 功能对应,即 PAD0-1 均有上拉功能,PAD2-3 均有下拉功能。

13-53ns 之内,PAD0 作为输出,PAD1 作为输入,并且相连,两者的逻辑值变化一致。同理,53ns-93ns 之内,PAD1 作为输出,PAD0 作为输入, 相连状态下两者逻辑值也是一致的。这说明 PAD0/1 的输入输出功能都是正常的。

PAD2/3 结果也类似,这里不再做说明。

本章节源码下载

Download