verilog学习——Verilog的行为建模/Behavioral modeling(2)
(六)Verilog functions/函数
函数的用途是返回要在表达式中使用的值。函数定义始终以关键字function开始,后跟返回类型、名称和在参数中的端口列表。函数以关键字endfunction结束。
函数至少有一个输入,如果不返回任何内容,则返回类型为void。
1.语法
function [automatic] [return_type] name ([port_list]);
[statements]
endfunction
2.函数声明
由两种方法可以声明函数的输入;
function [7:0] sum;
input [7:0] a, b;
begin
sum = a + b;
end
endfunction
function [7:0] sum (input [7:0] a, b);
begin
sum = a + b;
end
endfunction
3.从函数返回值
函数定义隐式地创建一个与函数同名的内部变量。通过将函数结果分配给内部变量来初始化返回值。
sum = a + b;
4.调用函数
函数调用是带有表达式的操作数。
reg [7:0] result;
reg [7:0] a, b;
initial begin
a = 4;
b = 5;
#10 result = sum (a, b);
end
5.函数规则
① 函数不能包含任何时间控制语句,如#、@、wait、posedge、negedge;
② 函数无法开始一个任务/task,因为它可能会消耗模拟时间,但可以调用其他函数;
③ 一个函数至少有一个输入;
④ 函数不能具有非阻塞赋值、force-release或assign-deasssign;
⑤ 函数不能有任何触发器;
⑥ 函数不能有output或inout。
6.递归函数
调用自身的函数称为递归函数;
(七)Verilog task/任务
Function对输入进行处理并返回单个值。而task可以计算多个结果值,并使用output和inout类型参数返回。Task可以包含模拟耗时的元素,如@、posedeg等;
1.语法
Task不需要在端口列表中有一组参数,可以为空;
// Style 1
task [name];
input [port_list];
inout [port_list];
output [port_list];
begin
[statements]
end
endtask
// Style 2
task [name] (input [port_list], inout [port_list], output [port_list]);
begin
[statements]
end
endtask
// Empty port list
task [name] ();
begin
[statements]
end
endtask
2.静态task/static task
静态task中,所有成员变量将在已启动并发运行的同一任务的不同调用之间共享;
3.自动task/automatic task
关键字automatic使task重入,否则默认情况下它是静态的。Automatic task中的所有项目都是为每次调用动态分配的,不会在同时运行的同一task的调用之间共享。注意,分层引用将无法访问automatic task的项。
4.全局task
在所有模块外部声明的task成为全局task,可以在任何模块中调用;
// This task is outside all modules
task display();
$display("Hello World !");
endtask
module des;
initial begin
display();
end
endmodule
如果task display是在模块des中生命的,则必须引用模块实例名称来调用它。
des u0();
…
u0.display();
5.function和task的区别
(八)verilog参数
参数是 Verilog 结构,允许使用不同的规范重用模块
parameter MSB = 7; // MSB is a parameter with a constant value 7
parameter REAL = 4.5; // REAL holds a real number
parameter FIFO_DEPTH = 256,
MAX_WIDTH = 32; // Declares two parameters
parameter [7:0] f_const = 2'b3; // 2 bit value is converted to 8 bits; 8'b3
参数基本上是常量,所以在运行时修改其值是非法的。重新声明net、变量或其他参数已使用的名称是非法的。
有两种主要类型的参数,module和specify;他们都有范围规范;
1.模块参数/module parameters
模块参数可用于覆盖模块中的参数定义,使得模块在编译时具有一组不同的参数。可以用defparam语句或者模块实例语句来修改参数。通常在参数名称中使用大写字母。
module design_ip
#(parameter BUS_WIDTH=32,
parameter DATA_WIDTH=64) (
input [BUS_WIDTH-1:0] addr,
// Other port declarations
);
2.overriding parameters/覆盖参数
模块实例化期间可以使用新值覆盖参数。两种方法:模块实例化语句或defparam语句。
module tb;
// Module instantiation override
design_ip #(BUS_WIDTH = 64, DATA_WIDTH = 128) d0 ( [port list]);
// Use of defparam to override
defparam d0.FIFO_DEPTH = 128;
endmodule
3.specify parameters指定参数
参数值主要用于提供时序和延迟值,使用关键字specparam声明。可以在指定块和主模块主体中使用。
// Use of specify block
specify
specparam t_rise = 200, t_fall = 150;
specparam clk_to_q = 70, d_to_q = 100;
endspecify
// Within main module
module my_block ( ... );
specparam dhold = 2.0;
specparam ddly = 1.5;
parameter WIDTH = 32;
endmodule
4.指定参数和模块参数之间的区别
(九)Verilog ‘ifdef ’elsif 条件编译
条件编译,将代码包括在编译器指令中,这些指令通过设置标志告诉编译器包含或排除要编译的代码。
1.语法
使用关键字 ‘ifdef和’ifndef关键字实现条件编译。这些关键字可以出现在设计中的任何位置,并且可以嵌套在另一个中。
关键字 ‘ifdef 告诉编译器包含这段代码,直到遇到关键字 ‘else或者’endif;
// Style #1: Only single `ifdef
`ifdef <FLAG>
// Statements
`endif
// Style #2: `ifdef with `else part
`ifdef <FLAG>
// Statements
`else
// Statements
`endif
// Style #3: `ifdef with additional ifdefs
`ifdef <FLAG1>
// Statements
`elsif <FLAG2>
// Statements
`elsif <FLAG3>
// Statements
`else
// Statements
`endif
(十)Verilog延迟控制
Verilog中有两种类型的时序控制:delay and event expression延迟表达式和事件表达式
延迟控制是在模拟器遇到语句的时间和实际执行语句的时间之间添加延迟的一种方式。
事件表达式允许将语句延迟到某个模拟事件的发生。
1.延迟表达式控制
如果延迟表达式的计算结果为未知或高阻抗值,则它将被解释为零延迟。如果它的计算结果为负值,它将被解释为与时间变量大小相同的 2 补码无符号整数。
`timescale 1ns/1ps
module tb;
reg [3:0] a, b;
initial begin
{a, b} <= 0;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10;
a <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10 b <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#(a) $display ("T=%0t After a delay of a=%0d units", $realtime, a);
#(a+b) $display ("T=%0t After a delay of a=%0d + b=%0d = %0d units", $realtime, a, b, a+b);
#((a+b)*10ps) $display ("T=%0t After a delay of %0d * 10ps", $realtime, a+b);
#(b-a) $display ("T=%0t Expr evaluates to a negative delay", $realtime);
#('h10) $display ("T=%0t Delay in hex", $realtime);
a = 'hX;
#(a) $display ("T=%0t Delay is unknown, taken as zero a=%h", $realtime, a);
a = 'hZ;
#(a) $display ("T=%0t Delay is in high impedance, taken as zero a=%h", $realtime, a);
#1ps $display ("T=%0t Delay of 10ps", $realtime);
end
endmodule
T=0 a=x b=x
T=10000 a=0 b=0
T=20000 a=4 b=0
T=24000 After a delay of a=4 units
T=29000 After a delay of a=4 + b=1 = 5 units
T=29050 After a delay of 5 * 10ps
T=42050 Expr evaluates to a negative delay
T=58050 Delay in hex
T=58050 Delay is unknown, taken as zero a=x
T=58050 Delay is in high impedance, taken as zero a=z
T=58051 Delay of 10ps
2.事件表达式控制
网络和变量的值的改变可以用作同步事件来触发执行其他过程语句,是隐式事件。也可以是基于方向的变化。
Negedge是从1到x、z或0,以及从x或z到0的过渡;
Posedge是从0到x、z或1,以及从x或z到1 的过渡。
如果表达式的计算结果相同,则不能将其视为事件;
module tb;
reg a, b;
initial begin
a <= 0;
#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#15 a <= 1;
end
// Start another procedural block that waits for an update to
// signals made in the above procedural block
initial begin
@(posedge a);
$display ("T=%0t Posedge of a detected for 0->1", $time);
@(posedge b);
$display ("T=%0t Posedge of b detected for X->1", $time);
end
initial begin
@(posedge (a + b)) $display ("T=%0t Posedge of a+b", $time);
@(a) $display ("T=%0t Change in a found", $time);
end
endmodule
T=10 Posedge of a detected for 0->1
T=20 Posedge of b detected for X->1
T=30 Posedge of a+b
T=45 Change in a found
3.命名事件/named events
关键字event可用于声明可以显示触发的命名事件。Event不存储任何数据,没有持续时间,可以在任何特定的时间发生。
在事件名之前加上前缀->;
->a_event;
可以使用@运算符等待命名事件。
always @ (a_event) $display ("T=%0t [always] a_event is triggered", $time);
4.event or operator /or运算符
运算符or可用于等待,直到在表达式中触发列出的任何一个事件,也可以用逗号“,“代替or运算符。
// Use "or" between events
always @ (posedge a or posedge b)
$display ("T=%0t posedge of a or b found", $time);
// Use a comma between
always @ (posedge a, negedge b)
$display ("T=%0t posedge of a or negedge of b found", $time);
5.隐式事件表达式列表/implicit event expression list
敏感度列表或事件表达式列表通常是 RTL 中许多功能错误的常见原因。因为在程序块中引入新信号后,可能会忘记更新灵敏度列表。
Verilog允许将敏感度列表替换为*,
always @ * begin
6.level sensitive event control
过程语句的执行也可以延迟,直到条件变为真,使用关键字wait完成。
wait (ctr);
$display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
(十)Verilog赋值间或赋值内延迟
Verilog延迟语句可以在赋值运算符的左侧或右侧指定延迟;
1.赋值间延迟
// Delay is specified on the left side
#<delay> <LHS> = <RHS>
赋值间延迟语句在赋值运算符的 LHS 上具有延迟值,语句本身在延迟到期后执行。
2.赋值内延迟
// Delay is specified on the right side
<LHS> = #<delay> <RHS>
赋值内延迟是指在赋值运算符的RHS上存在延迟,首先在RHS上获得所有新值,然后,在延迟到期后,才会赋值给LHS。
(十一)Verilog的分层参考
Verilog为模块/module、函数/function、任务/task、命名块/named block和生成块/generate block定义了新的范围。
module tb;
reg signal;
// Another variable cannot be declared with
// an already existing name in the same scope
reg signal;
// However, the name 'signal' can be reused inside
// a task because it belongs to a different scope.
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
upwards name referencing/向上名称引用:
较低级别的模块可以引用层次结构中其上较高级别的模块中的项。
(十二)Verilog编码风格效果
结构良好、组织有序的Verilog代码可以用更少的硬件实现更高效的综合并节省空间和功耗。