掌握 Verilog 层次引用作用域:避免模块与块中的名称冲突
大多数编程语言都有作用域这一特性,它定义了代码某些部分对变量和方法的可见性。作用域定义了一个命名空间,以避免同一命名空间内不同对象名称之间的冲突。
Verilog 为模块、函数、任务、命名块和生成块定义了新的作用域。
module tb; reg signal; // 在同一作用域内不能声明同名变量 reg signal; // 然而,'signal' 可在任务内重用,因为它属于不同作用域 task display(); reg signal = 1; $display("signal = %0b", signal); endtask endmodule
在一个给定作用域中,一个标识符(如信号名)只能声明一种类型的项。这意味着同一作用域内不能有两个同名变量(无论数据类型是否相同),也不能有同名的任务与变量,甚至网表与门实例也不能同名。
Verilog 中每个标识符都有唯一的层次路径名,每个模块实例、任务、函数或命名的 begin end 或 fork join 块都定义了一个新的层级或作用域。
层次引用示例
module tb; // 创建两个不同模块的实例 A uA(); B uB(); // 创建一个命名块,声明信号并在仿真开始 10ns 时打印值 initial begin : TB_INITIAL reg signal; #10 $display("signal=%0d", signal); end // 尝试从该 initial 块通过层次引用访问其他作用域 initial begin TB_INITIAL.signal = 0; uA.display(); uB.B_INITIAL.B_INITIAL_BLOCK1.b_signal_1 = 1; uB.B_INITIAL.B_INITIAL_BLOCK2.b_signal_2 = 0; end endmodule
module A; task display(); $display("Hello, this is A"); endtask endmodule module B; initial begin : B_INITIAL #50; begin : B_INITIAL_BLOCK1 reg b_signal_1; #10 $display("signal_1=%0d", b_signal_1); end #50; begin : B_INITIAL_BLOCK2 reg b_signal_2; #10 $display("signal_2=%0d", b_signal_2); end end endmodule
仿真日志
xcelium> run Hello, this is A TB signal=0 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
向上名称引用
较低层级的模块可以引用层次结构中其上方模块的项。例如,TB_INITIAL 块中的 signal 在模块 A 的 display 任务中可见。
module A; task display(); $display("Hello, this is A"); // 向上引用,TB_INITIAL 在此模块中可见 #5 TB_INITIAL.signal = 1; endtask endmodule
注意 TB signal 现在变为 1 而非 0,因为模块 A 通过向上层次引用修改了该信号。
仿真日志
xcelium> run Hello, this is A TB signal=1 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
下面是另一个包含多层嵌套模块的示例,叶子节点可通过向上层次引用直接访问上层节点的成员。
module tb; A a(); function display(); $display("Hello, this is TB"); endfunction endmodule module A; B b(); function display(); $display("Hello, this is A"); endfunction endmodule module B; C c(); function display(); $display("Hello, this is B"); endfunction endmodule module C; D d(); function display(); $display("Hello, this is C"); endfunction endmodule module D; initial begin a.display(); // 或 A.display() b.display(); // 或 B.display() c.display(); // 或 C.display() a.b.c.display(); end endmodule
仿真日志
xcelium> run Hello, this is A Hello, this is B Hello, this is C Hello, this is C xmsim: *W,RNQUIE: Simulation is complete.
当编译器遇到 b.display() 时,
- 它在模块 D 的当前作用域查找 b 是否定义。若不存在,则向上查找包含作用域,直到到达模块作用域。若仍未找到,则进入下一步。
- 它在父模块的最外层作用域查找,若未找到则继续向上层次查找。
Verilog
- Verilog 入门:数字电路设计完整教程
- 掌握 Verilog 拼接操作:实用指南
- Verilog 核心要点速览
- 深入理解 Verilog 赋值语句:类型、语法与最佳实践
- Verilog阻塞与非阻塞赋值:关键区别详解
- 掌握Verilog函数:高效RTL设计的复用利器
- 理解Verilog任务:函数vs任务、语法与用法全解析
- 掌握 Verilog 层次引用作用域:避免模块与块中的名称冲突
- Verilog 时钟发生器设计与实现指南
- 掌握 Verilog 显示任务:高效调试与日志记录
- 掌握 Verilog 数学函数:高效硬件设计的利器
- 掌握 Verilog $timeformat:精度控制与显示格式化