Verilog模块例化

简介: 这种方法将需要例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,可以与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可。

关键字:例化,generate,全加器,层次访问

在一个模块中引用另一个模块,对其端口进行相关连接,叫做模块例化。模块例化建立了描述的层次。信号端口可以通过位置或名称关联,端口连接也必须遵循一些规则。

命名端口连接

这种方法将需要例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,可以与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可。

下面是例化一次 1bit 全加器的例子:

full_adder1  u_adder0(
    .Ai     (a[0]),
    .Bi     (b[0]),
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),
    .So     (so_bit0),
    .Co     (co_temp[0]));

如果某些输出端口并不需要在外部连接,例化时 可以悬空不连接,甚至删除。一般来说,input 端口在例化时不能删除,否则编译报错,output 端口在例化时可以删除。例如:

//output 端口 Co 悬空
full_adder1  u_adder0(
    .Ai     (a[0]),
    .Bi     (b[0]),
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),
    .So     (so_bit0),
    .Co     ());
//output 端口 Co 删除
full_adder1  u_adder0(
    .Ai     (a[0]),
    .Bi     (b[0]),
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),
    .So     (so_bit0));

顺序端口连接

这种方法将需要例化的模块端口按照模块声明时端口的顺序与外部信号进行匹配连接,位置要严格保持一致。例如例化一次 1bit 全加器的代码可以改为:

full_adder1  u_adder1(
    a[1], b[1], co_temp[0], so_bit1, co_temp[1]);

虽然代码从书写上可能会占用相对较少的空间,但代码可读性降低,也不易于调试。有时候在大型的设计中可能会有很多个端口,端口信号的顺序时不时的可能也会有所改动,此时再利用顺序端口连接进行模块例化,显然是不方便的。所以平时,建议采用命名端口方式对模块进行例化。

端口连接规则

输入端口

模块例化时,从模块外部来讲, input 端口可以连接 wire 或 reg 型变量。这与模块声明是不同的,从模块内部来讲,input 端口必须是 wire 型变量。

输出端口

模块例化时,从模块外部来讲,output 端口必须连接 wire 型变量。这与模块声明是不同的,从模块内部来讲,output 端口可以是 wire 或 reg 型变量。

输入输出端口

模块例化时,从模块外部来讲,inout 端口必须连接 wire 型变量。这与模块声明是相同的。

悬空端口

模块例化时,如果某些信号不需要与外部信号进行连接交互,我们可以将其悬空,即端口例化处保留空白即可,上述例子中有提及。

output 端口正常悬空时,我们甚至可以在例化时将其删除。

input 端口正常悬空时,悬空信号的逻辑功能表现为高阻状态(逻辑值为 z)。但是,例化时一般不能将悬空的 input 端口删除,否则编译会报错,例如:

//下述代码编译会报Warning
full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .c      (),
    .so     (so),
    .co     (co));
//如果模块full_adder4有input端口c,则下述代码编译是会报Error
full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .so     (so),
    .co     (co));

一般来说,建议 input 端口不要做悬空处理,无其他外部连接时赋值其常量,例如:

full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .c      (1'b0),
    .so     (so),
    .co     (co));

位宽匹配

当例化端口与连续信号位宽不匹配时,端口会通过无符号数的右对齐或截断方式进行匹配。

假如在模块 full_adder4 中,端口 a 和端口 b 的位宽都为 4bit,则下面代码的例化结果会导致:u_adder4.a = {2'bzz, a[1:0]}, u_adder4.b = b[3:0] 。

full_adder4  u_adder4(
    .a      (a[1:0]),      //input a[3:0]
    .b      (b[5:0]),      //input b[3:0]
    .c      (1'b0),
    .so     (so),
    .co     (co));

端口连续信号类型

连接端口的信号类型可以是,1)标识符,2)位选择,3)部分选择,4)上述类型的合并,5)用于输入端口的表达式。

当然,信号名字可以与端口名字一样,但他们的意义是不一样的,分别代表的是 2 个模块内的信号。

用 generate 进行模块例化

当例化多个相同的模块时,一个一个的手动例化会比较繁琐。用 generate 语句进行多个模块的重复例化,可大大简化程序的编写过程。

重复例化 4 个 1bit 全加器组成一个 4bit 全加器的代码如下:

module full_adder4(
    input [3:0]   a ,   //adder1
    input [3:0]   b ,   //adder2
    input         c ,   //input carry bit
    output [3:0]  so ,  //adding result
    output        co    //output carry bit
    );
    wire [3:0]    co_temp ;
    //第一个例化模块一般格式有所差异,需要单独例化
    full_adder1  u_adder0(
        .Ai     (a[0]),
        .Bi     (b[0]),
        .Ci     (c==1'b1 ? 1'b1 : 1'b0),
        .So     (so[0]),
        .Co     (co_temp[0]));
    genvar        i ;
    generate
        for(i=1; i<=3; i=i+1) begin: adder_gen
        full_adder1  u_adder(
            .Ai     (a[i]),
            .Bi     (b[i]),
            .Ci     (co_temp[i-1]), //上一个全加器的溢位是下一个的进位
            .So     (so[i]),
            .Co     (co_temp[i]));
        end
    endgenerate
    assign co    = co_temp[3] ;
endmodule
`timescale 1ns/1ns
module test ;
    reg  [3:0]   a ;
    reg  [3:0]   b ;
    //reg          c ;
    wire [3:0]   so ;
    wire         co ;
    //简单驱动
    initial begin
        a = 4'd5 ;
        b = 4'd2 ;
        #10 ;
        a = 4'd10 ;
        b = 4'd8 ;
    end
    full_adder4  u_adder4(
               .a      (a),
               .b      (b),
               .c      (1'b0),   //端口可以连接常量
               .so     (so),
               .co     (co));
    initial begin
        forever begin
            #100;
            if ($time >= 1000)  $finish ;
        end
    end
endmodule // test

仿真结果如下,可知 4bit 全加器工作正常:

网络异常,图片无法展示
|

层次访问

每一个例化模块的名字,每个模块的信号变量等,都使用一个特定的标识符进行定义。在整个层次设计中,每个标识符都具有唯一的位置与名字。

Verilog 中,通过使用一连串的 . 符号对各个模块的标识符进行层次分隔连接,就可以在任何地方通过指定完整的层次名对整个设计中的标识符进行访问。

层次访问多见于仿真中。

例如,有以下层次设计,则叶单元、子模块和顶层模块间的信号就可以相互访问。

//u_n1模块中访问u_n3模块信号:
a = top.u_m2.u_n3.c ;
//u_n1模块中访问top模块信号
if (top.p == 'b0) a = 1'b1 ;
//top模块中访问u_n4模块信号
assign p = top.u_m2.u_n4.d ;

网络异常,图片无法展示
|

相关文章
|
6月前
|
存储 编译器 索引
Verilog基础【一】
Verilog基础【一】
178 0
|
存储 开发工具 异构计算
第三章 硬件描述语言verilog(二) 功能描述-组合逻辑(下)
第三章 硬件描述语言verilog(二) 功能描述-组合逻辑
876 0
第三章 硬件描述语言verilog(二) 功能描述-组合逻辑(下)
|
5月前
|
监控 算法 编译器
初识 Verilog HDL , 什么是verilog HDL?
这是一篇关于Verilog HDL的学习笔记摘要。Verilog是一种硬件描述语言,用于数字系统的多层抽象设计,包括行为、数据流和结构。设计流程包括功能设计、Verilog描述、软件模拟、逻辑综合和硬件实现。模块是Verilog的基本单元,代表逻辑实体,通过并行运行和分层连接实现复杂系统。模块包含端口列表和定义,通过模块调用(实例化)实现子模块连接。Verilog的参数声明和预处理指令(如`define、`include和`timescale)增加了代码的可读性和灵活性。笔记指出Verilog与C语言有相似之处,易于学习。
|
6月前
|
存储 人工智能 安全
Verilog基础【二】
Verilog基础【二】
186 1
|
6月前
|
C++
Verilog 函数和任务
Verilog 函数和任务
|
6月前
|
传感器 芯片 异构计算
常见的基础模块电路,你都能看懂吗?
常见的基础模块电路,你都能看懂吗?
92 0
|
算法 关系型数据库 MySQL
FPGA:Verilog HDL程序的基本结构
FPGA:Verilog HDL程序的基本结构
160 0
FPGA:Verilog HDL程序的基本结构
|
开发工具 芯片 异构计算
Verilog 设计方法
Verilog 的设计多采用自上而下的设计方法(top-down)。即先定义顶层模块功能,进而分析要构成顶层模块的必要子模块;然后进一步对各个模块进行分解、设计,直到到达无法进一步分解的底层功能块。这样,可以把一个较大的系统,细化成多个小系统,从时间、工作量上分配给更多的人员去设计,从而提高了设计速度,缩短了开发周期。
155 1
|
算法 异构计算
Verilog HDL函数与任务的使用
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
113 0
Verilog HDL函数与任务的使用