Verilog 数学函数可替代常量表达式,支持整数和实数运算。 整数数学函数 $clog2 返回给定参数以 2 为底对数的天花板值。常用于计算给定大小存储器所需最小地址位宽。 例如设计有 7 个并行加法器,表示所有 7 个加法器所需最小位数为 $clog2(7) = 3。 module des #(parameter NUM_UNITS = 7) // 使用此系统函数减少模块输入线数 (input [$clog2(NUM_UNITS)-1:0] active_unit); initial $monitor(active_unit = %d, active_unit); endmodule `
显示系统任务主要用于输出信息和调试消息,跟踪仿真流程,加速调试。有不同组别的显示任务和打印格式。 显示/写入任务 语法 $display 和 $write 均按参数列表顺序显示参数。 $display(<list_of_arguments>); $write(<list_of_arguments>); $write 不在字符串末尾追加换行符 \n,而 $display 会自动换行,如下例所示。 示例 module tb; initial begin $display (This ends with a new line ); $write (This does n
时钟是构建数字电路的基础,使不同模块能相互同步。 时钟特性 数字时钟的关键特性包括频率(决定时钟周期)、占空比和相对于其他时钟的相位。 时钟周期 频率表示单位时间内的周期数,时钟周期即完成 1 个周期所需时间。 时钟占空比 时钟高电平持续时间与周期之比定义占空比。 时钟相位 若将一个时钟周期视为 360 度圆,另一时钟可相对偏移不同相位。例如同周期时钟右移 1/4 周期,相位差 90 度。 Verilog 时钟发生器 仿真在指定精度的 timescale 上运行,故 timescale 精度必须足以表示时钟周期。例如频率 640000 kHz,周期 1.5625 ns,若 timesca
Verilog 设计和测试平台通常包含许多 always 或 initial 块、连续赋值及其他过程语句,它们在仿真过程中不同时间变为活动。 Verilog 模型中信号值的每次变化视为一个更新事件。对这些更新事件敏感的进程(如 always 和 assign 块)以任意顺序求值,称为求值事件。因这些事件发生在不同时间,需通过按仿真时间排列的事件队列管理并确保正确执行顺序。 module tb; reg a, b, c; wire d; // always 进程在 a 或 b 更新时求值 // a 或 b 值变化称为更新事件 // always 块因 a 或 b 变化触发称为求值事件 alwa
Verilog 仿真依赖于时间的定义,因为仿真器需要知道 #1 在时间上代表什么。`timescale 编译指令指定后续模块的时间单位和精度。 语法 `timescale <time_unit>/<time_precision> // 示例 `timescale 1ns/1ps `timescale 10us/100ns `timescale 10ns/1ns time_unit 是延迟和仿真时间的度量单位,time_precision 指定延迟值在仿真中使用前的舍入精度。 使用以下 timescale 结构可在同一设计中使用不同时间单位。记住设计中的延迟规范不可综
Verilog 是硬件描述语言,设计者无需仿真即可将 RTL 转换为逻辑门。那么为什么要仿真? 仿真是在不同时间向设计施加不同输入激励,检查 RTL 代码是否按预期行为工作的技术。本质上,仿真是验证设计鲁棒性的常用手段。这也类似于制造出的芯片在真实世界中的使用方式及其对不同输入的反应。 例如,上图设计表示一个正边沿检测器,输入时钟和信号在周期性间隔求值得到输出 pe。仿真让我们查看相关信号的时序图,理解 Verilog 描述的实际行为。 多家 EDA 公司开发仿真器,能计算设计对各种输入的输出。Verilog 基于离散事件执行模型定义,不同仿真器可使用不同算法提供一致结果。Verilog
标准 Verilog 基本单元(如 nand、not)并不总能轻松或充分表示复杂逻辑。可定义新的基本单元——用户定义基本单元(UDP)来建模组合或时序逻辑。 所有 UDP 均有且仅有一个输出,取值只能是 0、1 或 X,不支持 Z。任何值为 Z 的输入将被视为 X。 Verilog UDP 符号 UDP 可与 module 定义同级编写,但不能置于 module 和 endmodule 之间。可有多个输入端口但仅一个输出端口,不支持双向端口。所有端口信号必须为标量(1 位宽)。 硬件行为用基本单元状态表描述,在 table 和 endtable 间列出输入组合及对应输出。输入输出信号值用以下符
Verilog 也提供晶体管级建模支持,但现代设计复杂度要求设计者转向更高抽象层级,因此开关级建模已较少使用。 NMOS/PMOS 晶体管 module des (input d, ctrl, output outn, outp); nmos (outn, d, ctrl); pmos (outp, d, ctrl); endmodule module tb; reg d, ctrl; wire outn, outp; des u0 (.d(d), .ctrl(ctrl), .outn(outn), .outp(outp)); initial begin {d, ctrl} <=
数字元件是二值实体,只能保持 0 或 1。然而 0→1 和 1→0 的转换存在过渡延迟,每个门元件将输入值传播到输出也需要时间。 例如,两输入与门需在两输入均为 1 时将输出切换为 1,任一输入变 0 时切换回 0。这些门及引脚间延迟可在实例化逻辑基本单元时指定。 上升、下降与关断延迟 门输出从某值变为 1 所需时间称为上升延迟;从某值变为 0 称为下降延迟;从某值变为高阻态称为关断延迟。 这些延迟实际上适用于任何信号,因为实际电路中所有信号都可能随时上升或下降,不限于门输出。有三种方式表示门延迟,双延迟格式适用于输出不转高阻的大多数基本单元。例如三延迟格式不能用于与门,因为其输出对任何输入组
上文讨论了主要内置基本单元,下面通过简单 and、nor 和 not 门的实际例子加深理解。 注意:要使用门编写 Verilog 代码,必须知道如何连接元件。这与行为描述截然不同,后者由综合工具自动选择和连接元件。 示例 1:2x1 多路选择器 模块输出必须声明为 wire 类型,才能连接基本单元的输出端口。 module mux_2x1 ( input a, b, sel, output out); wire sel_n; wire out_0; not (sel_n, sel); and (out_0, a, sel); and (out_1, b, sel_n); or (out, o
大多数数字设计在更高抽象层级(如 RTL)完成,但有时使用组合元件(如 and 和 or) 构建较小的确定性电路更直观。这种建模层级称为门级建模,因为它涉及门,且硬件原理图与 Verilog 代码一一对应。 Verilog 支持一些基本逻辑门,称为基本单元,因为它们像模块一样可以实例化,且已预定义。 与/或/异或门 这些基本单元实现与和或门,接受多个标量输入并提供单个标量输出。参数列表中的第一个端口是输出,当任一输入变化时更新。 module gates ( input a, b, output c, d, e); and (c, a, b); // c 为输出,a、b 为输入 or (d,
大多数编程语言都有作用域这一特性,它定义了代码某些部分对变量和方法的可见性。作用域定义了一个命名空间,以避免同一命名空间内不同对象名称之间的冲突。 Verilog 为模块、函数、任务、命名块和生成块定义了新的作用域。 module tb; reg signal; // 在同一作用域内不能声明同名变量 reg signal; // 然而,signal 可在任务内重用,因为它属于不同作用域 task display(); reg signal = 1; $display(signal = %0b, signal); endtask endmodule 在一个给定作用域中,一个标识符(如信号名)
Verilog 延迟语句可以在赋值运算符的左侧或右侧指定延迟值。 赋值间延迟 // 延迟指定在左侧 #<delay> <LHS> = <RHS> 赋值间延迟语句的延迟值位于赋值运算符的左侧(LHS)。这表示该语句本身在延迟过期后执行,是最常用的延迟控制形式。 module tb; reg a, b, c, q; initial begin $monitor([%0t] a=%0b b=%0b c=%0b q=%0b, $time, a, b, c, q); // 在时间 0 初始化所有信号为 0 a <= 0; b <= 0; c <
两类时序控制 Verilog含延时控制和事件控制。延时控制:仿真器遇到语句到实际执行间插入延时。事件控制:延迟至仿真事件发生(网络/变量值变化隐式事件、显式命名事件触发)。门/网络内部延时模型也推进仿真时间。 延时控制详解 延时表达式为X/Z视为零延时;负值视为同位宽时间变量的2补无符号整数。支持常数、变量、表达式、十六进制、时间单位(如10ps)等多种形式。$realtime显示高精度时间。 事件控制 隐式事件:变量值变化触发。posedge(向1变化:0→X/Z/1、X/Z→1)、negedge(向0变化:1→X/Z/0、X/Z→0)。仅能检测向量信号LSB的边沿。同状态到同状态非事件
语法形式 `ifdef和`ifndef实现条件编译,可嵌套。`ifdef FLAG:宏定义时包含代码至`else或`endif。`ifndef FLAG:宏未定义时包含。三种风格:仅`ifdef、带`else、带`elsif链。 设计实例:可选复位端口 模块my_design用`ifdef INCLUDE_RSTN包裹rstn端口及复位逻辑。测试平台同理条件实例化端口。编译时通过命令行+define+INCLUDE_RSTN控制是否包含复位功能。实验对比有无宏定义时的端口列表差异。 `ifdef/`elsif 与 `ifndef/`elsif 行为对比 `ifdef MACRO1...`e
参数基本概念 参数使模块可按不同规格复用。如4位加法器参数化位宽,实例化时传入新值变为8/16位加法器。类似函数调用传参。参数本质为常量,运行时不可修改,不可与网络/变量/其他参数重名。分模块参数和指定参数两大类。 模块参数 可在实例化或defparam中覆盖。Verilog-1995风格模块头声明参数;ANSI风格在模块名后#(parameter ...)声明。常用大写命名显眼。 覆盖方式: 1. 实例化传参:design_ip #(BUS_WIDTH=64, DATA_WIDTH=128) d0 (...);(RTL设计常用) 2. defparam:defparam d0.FIFO_D
任务与函数核心区别 函数处理输入返回单值;任务更通用,可通过output/inout参数返回多值,可包含消耗仿真时间的元素(@、posedge等)。 任务声明语法 两种风格:传统分行声明输入/输出/inout,或ANSI风格括号内声明。端口列表可为空。automatic使任务可重入,每次调用动态分配内存不共享。 静态任务 vs 自动任务 静态任务:成员变量跨并发调用共享。示例中4个并发initial块调用静态display任务,内部整型i共享递增,输出1,2,3,4。 自动任务:每次调用分配独立内存,同例中输出全为1。 全局任务 模块外声明为全局任务,任意模块可调用。模块内声明则需通过实
函数定义与调用 函数用于返回表达式中使用的值。以function关键字开始,含返回类型、名称、端口列表,以endfunction结束。至少一个输入,无返回值则返回类型为void。automatic关键字使函数可重入,内部项动态分配,适合递归和并发调用。 两种输入声明风格 传统风格:function [7:0] sum; input [7:0] a, b; ANSI风格:function [7:0] sum (input [7:0] a, b); 返回值机制 函数隐式创建同名内部变量,赋值给该变量即为返回值:sum = a + b;。非法在函数内声明同名变量。 函数调用 作为表达式操作数:
语法结构 case语句以case开始、endcase结束。表达式计算一次,按书写顺序与备选项比较,匹配则执行对应语句。多语句用begin-end分组。default可选,仅允许一个,无匹配且无default则退出不执行任何操作。支持嵌套。 示例:3选1多路选择器 2位选择信号sel路由三个3位输入a,b,c到输出out。sel有4种组合(0-3),default处理sel=3置零。综合原理图显示4选1多路器结构。 X/Z值处理差异 标准case要求每位精确匹配(含0,1,x,z)。若sel含x/z,无备选匹配,执行default输出全零。若备选项显式包含x/z(如2bxz),则精确匹配含x
语法结构 for (<initial_condition>; <condition>; <step_assignment>) begin // Statements end 三部分:初始条件、条件判断、步进赋值。初始化和更新包含在循环内,区别于while需单独指定。for循环有明确始终,适合迭代器可用、条件依赖迭代器值的场景。 简单示例 循环10次打印计数器值,注意Verilog无++运算符。 设计实例:参数化8位左移寄存器 无for循环版:逐位手写赋值op[1] <= op[0]...op[7] <= op[6],代码冗长、难扩展。 for循环版
Verilog