IEEE 1364-2005 Verilog 学习导读
先建立正确模型
跳转到“先建立正确模型”IEEE 1364-2005 同时定义了 RTL、门级/开关级建模、事件驱动仿真、时序标注、波形交换和 C 接口。学习时不要把所有章节当成同一层次:
flowchart LR A["词法、四态数据、表达式"] --> B["过程语句与赋值"] B --> C["模块、端口、参数、generate"] C --> D["可综合 RTL"] B --> E["事件调度与 Testbench"] C --> F["门级、specify、SDF"] E --> G["VCD 与 VPI"] F --> G推荐顺序:先学 Clause 3-6、9-12,再学 17-19;需要门级时序仿真时读 7-8、14-16;需要工具扩展时读 20、26-28。Clause 21-25 的 TF/ACC 接口已弃用。
十条高频规则
跳转到“十条高频规则”net表示连接和驱动解析,reg表示过程赋值目标;reg这个名字不保证综合出触发器。- Verilog 值域是
0/1/x/z。x是未知,z是高阻,不能把它们当普通二进制值忽略。 - 组合过程通常用阻塞赋值
=,时钟过程通常用非阻塞赋值<=。 - 位宽和 signedness 必须主动控制;加法不会自动多出进位位,窄赋值会丢弃高位。
==/!=遇到无法确定的x/z可返回x;===/!==始终返回0/1,主要用于 Testbench。if将x/z条件视为不成立;?:在一位未知条件下会逐位合并两个候选值。- 并发过程在同一时间槽的 Active 区内没有固定先后顺序;不要依赖源码排列消除竞态。
- 模块端口的 signed 属性不会因连接自动传播,边界处应显式声明或转换。
- generate 在 elaboration 阶段展开,不在仿真期间反复执行。
specify、timing check 和 SDF 面向时序仿真,不应与可综合 RTL 混为一谈。
四态数据与对象
跳转到“四态数据与对象”| 对象 | 驱动方式 | 未初始化/未驱动值 | 典型用途 |
|---|---|---|---|
wire、tri | 连续赋值、端口、实例输出,可解析多驱动 | z | 组合连接、总线 |
uwire | 单一驱动 | z | 用工具检查意外多驱动 |
wand/wor | 按 wired-AND/OR 解析多驱动 | z | 特殊板级/开关模型 |
tri0/tri1 | 无有效驱动时带弱下拉/上拉 | 0/1 | 拉电阻模型 |
trireg | 保存电荷并可衰减 | 依驱动与电荷状态 | 开关级电容模型 |
reg、integer、time | 过程赋值 | x | RTL 状态、循环和计时 |
real、realtime | 过程赋值 | 0.0 | 仿真计算,不是常规 RTL |
reg [7:0] mem [0:255];// ^ packed 向量 ^ unpacked 数组/存储器下标位选择和部分选择的结果总是 unsigned。数组与 packed 向量是不同维度,连接端口时尤其要检查方向、范围和位宽。
常量、位宽与符号
跳转到“常量、位宽与符号”8'hA5 // 8 位无符号十六进制8'shA5 // 8 位有符号十六进制'hA5 // 无尺寸、无符号,至少与 integer 同宽-12 // 一元负号作用于无尺寸十进制数4'b10xz // 四态二进制常量- 无尺寸十进制常量是 signed;带基数但没有
s的常量是 unsigned。 - 无尺寸常量至少 32 位;拼接中禁止使用无尺寸常量,因为结果位宽不明确。
- 算术表达式中出现
x/z,结果通常整体变成x。 - 算术右移
>>>只有在左操作数的表达式类型为 signed 时才复制符号位。 - 赋值先按表达式规则求值,再按左值宽度扩展或截断;需要进位时主动扩宽操作数。
| 表达式 | 结果宽度要点 | ||
|---|---|---|---|
+ - * / % & | ^ | 通常由上下文与最大操作数宽度决定,不能假定 + 自动加 1 位 | ||
== != === !== < <= > >= | 1 位 | ||
| `&& | !` | 1 位 | |
| 归约运算 | 1 位 | ||
a << n、a >> n | 与左操作数宽度相同,移位量宽度不扩展结果 | ||
{a,b}、{N{a}} | 各项宽度之和;各项是 self-determined | ||
cond ? a : b | 两个结果分支共同决定宽度和类型 | ||
| 位选 | 1 位且 unsigned | ||
| 部分选择 | 选择宽度且 unsigned |
安全写法:
wire [7:0] a, b;wire [8:0] sum = {1'b0, a} + {1'b0, b};wire signed [7:0] s;wire signed [7:0] half = s >>> 1;运算与四态传播
跳转到“运算与四态传播”| 场景 | 语义 | 实践建议 |
|---|---|---|
a == b | 含未知位且无法判定时为 x | RTL 普通比较 |
a === b | 把 x/z 也作为字面值比较 | Testbench 自检 |
if (cond) | cond 为 x/z 时走 else | 不要让未知控制关键路径 |
cond ? a : b | 条件未知时逐位合并 a/b | 比 if 更能暴露部分未知 |
case | 0/1/x/z 精确逐位匹配 | 默认选择 |
casez | z/? 为通配符 | 仅用于明确掩码意图 |
casex | x/z/? 都为通配符 | 避免,容易吞掉设计错误 |
括号优先于背运算符优先级。混合 signed/unsigned、比较、移位或三目运算时,显式扩宽和转换比依赖隐式规则可靠。
赋值与调度
跳转到“赋值与调度”flowchart LR A["Active<br/>求值、阻塞赋值、连续赋值更新"] --> B["Inactive<br/>#0 事件"] B --> C["NBA update<br/>非阻塞赋值更新"] C --> D["Monitor<br/>$strobe / $monitor"] D --> E["下一个时间槽"]Active 区内不同并发过程的先后顺序未定义,是仿真竞态的主要来源。#0 只把事件移到 Inactive 区,并不能建立可靠同步协议。
always @(posedge clk) begin q1 <= d; q2 <= q1;end两个右值都在 Active 区求值,更新进入 NBA 区,因此这段代码描述两级流水。对同一过程、同一时间点排入的多个 NBA,标准保留源码顺序;不同过程同时写同一变量仍可能不确定,应保持单写者。
| 写法 | 采样时刻 | 更新时刻 |
|---|---|---|
#5 a = b; | 延迟结束后 | 立即 |
a = #5 b; | 进入语句时 | 延迟结束后,过程阻塞 |
#5 a <= b; | 延迟结束后 | 当时的 NBA 区 |
a <= #5 b; | 进入语句时 | 5 个时间单位后的 NBA 区 |
过程、事件与边沿
跳转到“过程、事件与边沿”| 事件 | 标准识别的主要跃迁 |
|---|---|
posedge | 0->1、0->x/z、x/z->1 |
negedge | 1->0、1->x/z、x/z->0 |
向量边沿只检查最低有效位。always 必须包含能让时间前进或等待事件的控制,否则会形成零延时死循环。
always @* begin y = 1'b0; case (sel) 1'b0: y = a; 1'b1: y = b; default: y = 1'bx; endcaseend
always @(posedge clk or negedge rst_n) begin if (!rst_n) q <= 1'b0; else q <= d;endbegin-end 顺序执行过程语句;fork-join 并发启动分支。命名块可建立作用域,也可被 disable 终止。循环应区分固定次数、可综合展开与仅用于 Testbench 的运行时控制。
模块、端口、参数与 generate
跳转到“模块、端口、参数与 generate”flowchart TD T["top"] --> U0["u_core"] T --> U1["u_io"] U0 --> R0["g_lane[0].u_reg"] U0 --> R1["g_lane[1].u_reg"]- 参数覆盖使用位置或命名方式之一,不能混用;实例端口连接也不能混用两种方式。
localparam表示不可从实例外覆盖的本地常量。- 优先使用
#(.WIDTH(8)),避免跨层级、难追踪的defparam。 - generate 条件必须在 elaboration 时确定;显式命名生成块,避免依赖工具生成的
genblkN。 - 层次名是作用域路径,不是文件路径。向上引用和跨层引用会增加复用难度。
module pipe #(parameter WIDTH = 8) ( input wire clk, input wire [WIDTH-1:0] d, output reg [WIDTH-1:0] q); always @(posedge clk) q <= d;endmodule门级、路径延迟与时序检查
跳转到“门级、路径延迟与时序检查”门/开关原语和 UDP 用于低层模型;RTL 学习者应能读懂,但通常不手写复杂强度网络。多驱动 net 会根据值与强度解析,无法唯一决定时产生 x。
(a => y) :并行路径,按对应关系描述 N 条路径(a *> y) :全连接路径,描述输入到输出的 N x M 组合| timing check | 约束对象 | 典型含义 |
|---|---|---|
$setup / $hold / $setuphold | 数据与参考边沿 | 建立、保持或组合窗口 |
$recovery / $removal / $recrem | 异步控制与时钟 | 异步释放约束 |
$skew / $timeskew / $fullskew | 两个事件 | 偏斜上限与事件窗口 |
$width | 脉冲 | 最小脉宽 |
$period | 周期事件 | 最小周期 |
$nochange | 数据窗口 | 指定区间内不允许改变 |
flowchart LR A["综合后的网表"] --> B["specify 路径和 timing checks"] C["布局布线延迟"] --> D["SDF"] D -->|"$sdf_annotate"| B B --> E["门级时序仿真"]SDF 的 DELAY 对应模块/器件延迟,TIMINGCHECK 对应系统时序检查,INTERCONNECT 对应互连延迟,LABEL 可覆盖参数类数据。它们服务于后仿真,不是综合电路行为。
Testbench、系统任务与 VCD
跳转到“Testbench、系统任务与 VCD”| 目的 | 常用接口 |
|---|---|
| 即时输出 | $display、$write |
| NBA 后观察 | $strobe、$monitor |
| 文件 I/O | $fopen、$fclose、$fgets、$fscanf |
| 存储器初始化 | $readmemb、$readmemh |
| 时间 | $time、$stime、$realtime、$timeformat |
| 控制仿真 | $finish、$stop |
| 命令行参数 | $test$plusargs、$value$plusargs |
| 数学/转换 | $clog2 等系统函数 |
flowchart LR A["$dumpfile"] --> B["$dumpvars(level, scope)"] B --> C["仿真值变化"] C --> D["四态 VCD"] E["$dumpoff / $dumpon"] --> C F["$dumpflush / $dumpall"] --> D$dumpvars(0, top) 递归记录 top 下全部层级;$dumpvars(1, top) 只记录本层。大型设计应限制层级和信号范围,避免波形文件失控。
编译指令
跳转到“编译指令”建议每个独立编译单元明确环境:
`resetall`default_nettype none
module example(/* ... */); // ...endmodule
`default_nettype wire`define是预处理文本替换,不是类型安全函数;带副作用的实参若在宏体中出现多次,也会被求值多次。- 递归宏非法。用完局部宏后
`undef;`resetall不会清除宏定义。 `include按文本插入文件,需控制搜索路径和重复包含。`timescale指定时间单位/精度;同一设计不能出现一部分模块声明、一部分完全未声明的混合状态。`celldefine、`default_nettype、`unconnected_drive等指令会改变后续文本语义,库文件应恢复环境。
VPI 与受保护内容
跳转到“VPI 与受保护内容”新工具扩展应使用 VPI,不再使用已弃用的 TF/ACC。
flowchart LR A["vlog_startup_routines[]"] --> B["vpi_register_systf"] B --> C["compiletf / sizetf"] B --> D["calltf"] D --> E["vpi_handle"] D --> F["vpi_iterate + vpi_scan"] E --> G["vpi_get / vpi_get_str"] F --> H["vpi_get_value / vpi_put_value"]VPI 四态向量用 aval/bval 编码:00=0、10=1、11=X、01=Z。对象关系与回调生命周期比单个函数名更重要,配套头文件以 Annex G 的 vpi_user.h 为准。
Clause 28 的 protected envelope 是标准当年的加密封装互操作机制,不等于现代安全建议。其算法登记表包含已经不安全的 DES、MD5、SHA-1;新系统不得据此选型。
flowchart LR P["Verilog 明文"] --> S["对称加密"] K["随机会话密钥"] --> S K --> A["用授权方公钥封装"] S --> E["protected envelope"] A --> E附录怎么用
跳转到“附录怎么用”- Annex A:规范性 BNF,语法有歧义时的最终检索入口。
- Annex B:规范性关键字表,检查标识符冲突。
- Annex C/D:信息性内容且依赖实现,不应当作可移植语义保证。
- Annex E/F:已弃用 PLI 头文件,仅维护旧工具时查阅。
- Annex G:规范性
vpi_user.h。 - Annex H:信息性的加密流程示例,仅用于理解封装结构。
- Annex I:参考文献。
学习与验收清单
跳转到“学习与验收清单”- 能解释
wire、reg与物理组合/时序电路为何不是一一对应。 - 能手算混合位宽、signed/unsigned、移位和截断结果。
- 能按 Active、Inactive、NBA、Monitor 判断一个时间槽内的输出。
- 能识别多过程写同一变量、
#0、casex和零延时always的风险。 - 能写参数化模块、命名端口连接和命名 generate 块。
- 能区分 RTL 仿真、门级时序仿真、SDF 回标和 VCD 波形记录。
- 能判断何时只需 HDL,何时才需要 VPI。
逐章页码、全部图表系列和 Syntax Box 覆盖清单见 IEEE 1364-2005 参考地图。