模块

Verilog使用模块(module)的概念来代表一个基本的功能块。一个模块可以是一个元件,也可以是低层次模块的组合。常用的设计方法是使用元件构建在设计中多个地方使用的功能块,以便进行代码重用。模块通过接口(输入和输出)被高层的模块调用,但隐藏了内部的实现细节。这样就使得设计者可以方便地对某个模块进行修改,而不影响设计的其他部分。
在verilog中,模块声明由关键字module开始,关键字endmodule则必须出现在模块定义的结尾。每个模块必须具有一个模块名,由它唯一地标识这个模块。模块的端口列表则描述这个模块的输入和输出端口。

module <模块名>(<模块端口列表>);
...
<模块的内容>
...
...
endmodule

使用Verilog既可以进行行为描述,同时也可以进行结构描述。根据设计需要,设计者在每个模块内部可以在4个抽象层次中进行描述,而模块对外显示的功能都是一样的,仅与外部环境有关,而与抽象层次无关。模块的内部结构对于外部环境来讲是透明的。因此,对模块内部抽象层次的改变不会影响外部环境。这些抽象层次的定义如下:

  • 行为或算法级:Verilog所支持的最高抽象层次。设计者只注重其实现的算法,而不关心其具体的硬件实现细节。在这个层次上进行的设计与C语言编程非常类似。
  • 数据流级:通过说明数据的流程对模块进行描述。设计者关心的是数据如何在各个寄存器之间流动,以及如何处理这些数据。
  • 门级:从组成电路的逻辑门及其相互之间的互连关系的角度来设计模块。这个层次的设计类似于使用门级逻辑简图来完成设计。
  • 开关级:Verilog所支持的最低抽象层次。通过使用开关、存储节点及其互连关系来设计模块。在这个层次进行设计需要了解开关级的实现细节。

Verilog允许设计者在一个模块中混合使用多个抽象层次。在数字电路设计中,术语寄存器传输级(RTL)描述在很多情况下是指能够被逻辑综合工具接受的行为级和数据流级的混合描述。

例化

端口是模块与外部环境交互的通道,只有在模块有端口的情况下才需要有端口列表和端口声明。模块内部的5个组成部分是:变量声明、数据流语句、低层模块实例、行为语句块以及任务与函数。这些部分可以在模块中的任意位置,以任意顺序出现。在模块的所有组成部分中,只有module、模块名和endmodule必须出现,其他部分都是可选的,用户可以根据设计的需要随意选用。在一个Verilog源文件中可以定义多个模块,Verilog对模块的排列顺序没有要求。
为了理解模块的各个组成部分,我们以SR锁存器为例进行详细说明,如下。
SR锁存器构成如下:

 
  // 本例说明模块的构成部件
 
  // 模块名和端口列表
  // SR锁存器模块
 
  module SR_latch(Q, Qbar, Sbar, Rbar);
 
  // 端口声明
  output Q, Qbar;
  input Sbar, Rbar;
 
  // 调用(实例引用)较低层次的模块
  // 本例中调用(实例引用)的是Verilog原语部件nand,即与非门
  // 注意它们之间互相交叉连接的情况
  nand n1(Q, Sbar, Qbar);
  nand n2(Qbar, Rbar, Q);
  // 模块语句结束
  endmodule
 
  // 模块名和端口列表
  // 测试激励信号模块
  module Top;
 
  // 声明wire, reg和其他类型的变量
  wire q, qbar;
  reg set, reset;
 
  // 调用(实例引用)较低层次的模块
  // 本模块中调用(实例引用)的是SR_latch
  SR_latch m1(q, qbar, ~set, ~reset);
 
  // 行为模块, 初始化
  initial
  begin
   $monitor($time, " set = %b, reset= %b, q= %b\n",set,reset,q);
   set = 0; reset = 0;
   #5 reset = 1;
   #5 reset = 0;
   #5set = 1;
  end
 
  // 模块语句结束
  endmodule