相关阅读
Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482
Verilog有八个标识符命名空间:其中两个是全局的(定义命名空间和文本宏命名空间),六个是局部的(模块命名空间,块命名空间、生成块命名空间、端口命名空间、specify块命名空间、属性命名空间)。
一、定义命名空间
定义命名空间统一了所有编译单元中,模块(module)和原语(primitive)标识符。一旦一个名称被用来在一个编译单元内定义一个模块和原语,该名称就不能再(包括其他编译单元中)被用来声明另一个模块和原语。
在举例说明之前,有必要首先阐述一下编译单元的概念:编译单元指的是一起编译的一个或多个Verilog源文件的集合,但定义哪些文件构成一个编译单元的确切机制是工具决定的。然而,兼容Verilog标准的工具应该提供允许选择以下两种方式:
1、给定编译命令行上的所有文件构成单个编译单元(多文件单编译单元)。
2、给定编译命令行上的每个文件都是一个单独的编译单元(单文件单编译单元)。
这里的第二种方式更像C语言中编译的概念,每一个C语言源文件都是一个编译单元,编译汇编成一个目标文件,最后链接。Modelsim或Questa提供了编译时的-sfcu选项(Single-file compilation unit(default))和-mfcu选项(Multi-file compilation unit)用于支持标准。
下面的项目在编译单元间(或内)互相可见:模块和原语。这也就解释了为什么,模块的定义和实例化没有顺序要求,如例1所示,甚至可以将模块定义和实例化放在两个不同的文件中,如例2所示。不论是否是一个编译单元,它们都是互相可见的。
// 例1
// 文件1: my_module.v
module my_module;
// 模块内容
endmodule
// 文件2: top.v
module top;
my_module inst(); // 可以在另一个文件中实例化my_module
endmodule
// 例2
// 文件:top.v
module top;
sub_module u1(); // 这里实例化了my_module,虽然它还未定义
endmodule
// 之后才定义模块
module my_module;
// 模块内容
endmodule
回到正题,当出现了定义名称空间中标识符同名的情况(不论是否是同一类型,如例3所示),不同的仿真工具的默认行为是不同的:有些会直接报错(Aldec Riviera、Cadence Xcelium、Synopsys VCS),有些则会用后识别的标识符覆盖之前的定义并给出警告(Mentor Modelsim\Questa)。
// 例3
// 文件1: module_example.v
module my_module; // 定义名为my_module的模块
// 模块内容
endmodule
primitive my_module; // 同文件重复定义(不同类型)
// 原语内容
endprimitive
// 文件2: conflicting_module.v
module my_module; // 重复定义(同类型)
// 模块内容
endmodule
二、文本宏命名空间
像C语言一样,Verilog中的文本宏只在一个编译单元中,宏定义位置后生效,如例4所示。与编译单元域命名空间一样,如果是多文件单编译单元,一个文件的宏是否能在另一个文件中生效,取决于文件编译的顺序,如例5所示。
// 例4
// 文件:macro.v
`define WIDTH 8
module example;
logic [`WIDTH-1:0] data; // 使用文本宏 WIDTH
endmodule
// 例5
// 文件1:macro.v
`define WIDTH 8; // 在文本宏命名空间中定义一个宏
// 文件2:module.v
module my_module; // 使用多文件单编译单元编译
logic [WIDTH-1:0] data; // 访问文本宏命名空间中的参数WIDTH
initial begin
data = 8'hFF;
$display("Data width is %0d bits", WIDTH);
end
endmodule
// 当先编译module.v时,会报错;当先编译macro.v时不会报错
// 对于Modelsim,可以通过改变命令行中这两个源文件的前后顺序,改变其编译顺序
因为宏使用时,需要用`符号作为前缀,它和其他命名空间的标识符不会出现重名现象。 当一个文本宏命名空间中,出现了两个同名宏,则在后定义的宏会在其后的范围覆盖之前的宏。
三、模块命名空间
模块命名空间由下面结构引入:模块和原语。它统一了这些结构中的函数(function)、任务(task)、命名块(named block)、实例名(instance name)、生成块(generated block)、参数(parameter)、命名事件(named event)、genvar声明、线网声明(net declaration)和变量声明(variable declaration)的标识符(注意,这两种结构并不一定能包括所有这些元素,比如原语中不能定义函数)。
例6给出了一些模块命名空间的标识符重名的例子(以一个模块为例)。
// 例6
// 文件:module.v
module example;
// 定义一个参数,名称为 "my_name"
parameter int my_name = 32;
// 定义一个变量,名称为 "my_name"
reg my_name;
// 定义一个线网,名称为 "my_name"
wire my_name;
// 定义一个命名事件,名称为 "my_name"
event my_name;
// 定义一个函数,名称为 "my_name"
function [3:0] my_name;
input [3:0] a, b;
begin
my_name = a + b;
end
endfunction
// 定义一个任务,名称为 "my_name"
task my_name;
input [3:0] a;
begin
$display("Value: %d", a);
end
endtask
// 定义一个命名块,名称为 "my_name"
initial begin : my_name
my_name(4); // 调用任务
end
// 定义一个生成块,名称为 "my_name"
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : my_name
example ui();
end
endgenerate
endmodule
四、块命名空间
块命名空间由下面结构引入:命名块、函数和任务。它统一了这些结构中的命名块、参数、命名事件、变量声明(注意,这五种结构并不一定能包括所有这些元素,比如函数中不能定义命名事件)。
例7给出了一些模块命名空间的标识符重名的例子(以一个命名块为例)。
// 例7
// 文件:block.v
module ExampleModule;
initial begin: top
// 参数名为test
parameter test = 1;
// 命名事件名为test
event test;
// 变量名为test
reg test;
// 在命名块中定义另一个命名块test,根据Verilog语法规则,块语句必须在其他声明后
begin: test
$display("This is a test");
end
end
endmodule
由于篇幅较长,本文分上、下两节更新