Verilog 过程结构
关键词:initial,always
- 过程结构语句有 2 种,initial 与 always 语句。它们是行为级建模的 2 种基本语句。
一个模块中可以包含多个 initial 和 always 语句,但 2 种语句不能嵌套使用。
这些语句在模块间并行执行,与其在模块的前后顺序没有关系。
但是 initial 语句或 always 语句内部可以理解为是顺序执行的(非阻塞赋值除外)。
每个 initial 语句或 always 语句都会产生一个独立的控制流,执行时间都是从 0 时刻开始。
initial 语句
- initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。
- 如果 initial 块内包含多个语句,需要使用关键字 begin 和 end 组成一个块语句。
- 如果 initial 块内只要一条语句,关键字 begin 和 end 可使用也可不使用。
- initial 理论上来讲是不可综合的,多用于初始化、信号检测等。
always 语句
- 与 initial 语句相反,always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。
- 由于循环执行的特点,always 语句多用于仿真时钟的产生,信号行为的检测等。
下面用 always 产生一个 100MHz 时钟源,并在 1010ns 时停止仿真代码如下:
`timescale 1ns/1ns
module test ;
parameter CLK_FREQ = 100 ; //100MHz
parameter CLK_CYCLE = 1e9 / (CLK_FREQ * 1e6) ; //switch to ns
reg clk ;
initial clk = 1'b0 ; //clk is initialized to "0"
always # (CLK_CYCLE/2) clk = ~clk ; //generating a real clock by reversing
always begin
#10;
if ($time >= 1000) begin
$finish ;
end
end
endmodule
仿真结果:
可见,时钟周期是我们想要得到的 100MHz。而且仿真在 1010ns 时停止。
Verilog 过程赋值
关键词:阻塞赋值,非阻塞赋值,并行
- 过程性赋值是在 initial 或 always 语句块里的赋值,赋值对象是寄存器、整数、实数等类型。
- 这些变量在被赋值后,其值将保持不变,直到重新被赋予新值。
- 连续性赋值总是处于激活状态,任何操作数的改变都会影响表达式的结果;过程赋值只有在语句执行的时候,才会起作用。这是连续性赋值与过程性赋值的区别。
Verilog 过程赋值包括 2 种语句:阻塞赋值与非阻塞赋值。
阻塞赋值
阻塞赋值属于顺序执行,即下一条语句执行前,当前语句一定会执行完毕。
阻塞赋值语句使用等号 =
作为赋值符。
前面的仿真中,initial 里面的赋值语句都是用的阻塞赋值。
非阻塞赋值
非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。
非阻塞赋值语句使用小于等于号 <=
作为赋值符。
利用下面代码,对阻塞、非阻塞赋值进行仿真,来说明 2 种过程赋值的区别。
`timescale 1ns/1ns
module test ;
reg [3:0] ai, bi ;
reg [3:0] ai2, bi2 ;
reg [3:0] value_blk ;
reg [3:0] value_non ;
reg [3:0] value_non2 ;
initial begin
ai = 4'd1 ; //(1)
bi = 4'd2 ; //(2)
ai2 = 4'd7 ; //(3)
bi2 = 4'd8 ; //(4)
#20 ; //(5)
//non-block-assigment with block-assignment
ai = 4'd3 ; //(6)
bi = 4'd4 ; //(7)
value_blk = ai + bi ; //(8)
value_non <= ai + bi ; //(9)
//non-block-assigment itself
ai2 <= 4'd5 ; //(10)
bi2 <= 4'd6 ; //(11)
value_non2 <= ai2 + bi2 ; //(12)
end
//stop the simulation
always begin
#10 ;
if ($time >= 1000) $finish ;
end
endmodule
仿真结果如下:
语句(1)-(8)都是阻塞赋值,按照顺序执行。
20ns 之前,信号 ai,bi 值改变。由于过程赋值的特点,value_blk = ai + bi 并没有执行到,所以 20ns 之前,value_blk 值为 X(不确定状态)。
20ns 之后,信号 ai,bi 值再次改变。执行到 value_blk = ai + bi,信号 value_blk 利用信号 ai,bi 的新值得到计算结果 7。
语句(9)-(12)都是非阻塞赋值,并行执行。
首先,(9)-(12)虽然都是并发执行,但是执行顺序也是在(8)之后,所以信号 value_non = ai + bi 计算是也会使用信号 ai,bi 的新值,结果为 7。
其次,(10)-(12)是并发执行,所以 value_non2 = ai2 + bi2 计算时,并不关心信号 ai2,bi2 的最新非阻塞赋值结果。即 value_non2 计算时使用的是信号 ai2,bi2 的旧值,结果为 4’hF。
使用非阻塞赋值避免竞争冒险
实际 Verilog 代码设计时,切记不要在一个过程结构中混合使用阻塞赋值与非阻塞赋值。两种赋值方式混用时,时序不容易控制,很容易得到意外的结果。
更多时候,在设计电路时,always 时序逻辑块中多用非阻塞赋值,always 组合逻辑块中多用阻塞赋值;在仿真电路时,initial 块中一般多用阻塞赋值。
如下所示,为实现在时钟上升沿交换 2 个寄存器值的功能,在 2 个 always 块中使用阻塞赋值。
因为 2 个 always 块中的语句是同时进行的,但是 a=b 与 b=a 是无法判定执行顺序的,这就造成了竞争的局面。
但不管哪个先执行(和编译器等有关系),不考虑 timing 问题时,他们执行顺序总有先后,最后 a 与 b 的值总是相等的。没有达到交换 2 个寄存器值的效果。
always @(posedge clk) begin
a = b ;
end
always @(posedge clk) begin
b = a;
end
但是,如果在 always 块中使用非阻塞赋值,则可以避免上述竞争冒险的情况。
如下所示,2 个 always 块中语句并行执行,赋值操作右端操作数使用的是上一个时钟周期的旧值,此时 a<=b 与 b<=a 就可以相互不干扰的执行,达到交换寄存器值的目的。
always @(posedge clk) begin
a <= b ;
end
always @(posedge clk) begin
b <= a;
end
//利用下面代码也可以实现交换寄存器值的功能,但是显然不如在 always 块中直接用非阻塞赋值简单直观。
always @(posedge clk) begin
temp = a ;
a = b ;
b = temp ;
end
Verilog 时序控制
关键词:时延控制,事件触发,边沿触发,电平触发
Verilog 提供了 2 大类时序控制方法:时延控制和事件控制。事件控制主要分为边沿触发事件控制与电平敏感事件控制。
时延控制
基于时延的时序控制出现在表达式中,它指定了语句从开始执行到执行完毕之间的时间间隔。
时延可以是数字、标识符或者表达式。
根据在表达式中的位置差异,时延控制又可以分为常规时延与内嵌时延。
- 常规时延
//遇到常规延时时,该语句需要等待一定时间,然后将计算结果赋值给目标信号。
reg value_test ;
reg value_general ;
#10 value_general = value_test ;
//该时延方式的另一种写法是直接将井号 # 独立成一个时延执行语句,例如:
#10 ;
value_ single = value_test ;
- 内嵌时延
遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。
内嵌时延控制加在赋值号之后。
reg value_test ;
reg value_embed ;
value_embed = #10 value_test ;
需要说明的是,这 2 种时延控制方式的效果是有所不同的。
当延时语句的赋值符号右端是常量时,2 种时延控制都能达到相同的延时赋值效果。
当延时语句的赋值符号右端是变量时,2 种时延控制可能会产生不同的延时赋值效果。
- (1)一般延时的两种表达方式执行的结果都是一致的。
- (2)一般时延赋值方式:遇到延迟语句后先延迟一定的时间,然后将当前操作数赋值给目标信号,并没有"惯性延迟"的特点,不会漏掉相对较窄的脉冲。
- (3)内嵌时延赋值方式:遇到延迟语句后,先计算出表达式右端的结果,然后再延迟一定的时间,赋值给目标信号。
边沿触发事件控制
在 Verilog 中,事件是指某一个 reg 或 wire 型变量发生了值的变化。
基于事件触发的时序控制又主要分为以下几种。
- 一般事件控制
事件控制用符号 @
表示。
语句执行的条件是信号的值发生特定的变化。
关键字posedge
指信号发生正向边沿跳变,negedge
指信号发生负向边沿跳变,未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。例如:
//信号clk只要发生变化,就执行q<=d,双边沿D触发器模型
always @(clk) q <= d ;
//在信号clk上升沿时刻,执行q<=d,正边沿D触发器模型
always @(posedge clk) q <= d ;
//在信号clk下降沿时刻,执行q<=d,负边沿D触发器模型
always @(negedge clk) q <= d ;
//立刻计算d的值,并在clk上升沿时刻赋值给q,不推荐这种写法
q = @(posedge clk) d ;
- 命名事件控制
用户可以声明 event(事件)类型的变量,并触发该变量来识别该事件是否发生。命名事件用关键字 event 来声明,触发信号用 -> 表示。例如:
event start_receiving ;
always @(posedge clk_samp) begin
-> start_receiving ; //采样时钟上升沿作为时间触发时刻
end
always @(start_receiving) begin
data_buf = {data_if[0], data_if[1]} ; //触发时刻,对多维数据整合
end
- 敏感列表
当多个信号或事件中任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 or
连接多个事件或信号。这些事件或信号组成的列表称为"敏感列表"。当然,or
也可以用逗号 ,
来代替。例如:
//带有低有效复位端的D触发器模型
always @(posedge clk or negedge rstn) begin
//always @(posedge clk , negedge rstn) begin
//也可以使用逗号陈列多个事件触发
if(! rstn)begin
q <= 1'b ;
end
else begin
q <= d ;
end
end
//当组合逻辑输入变量很多时,那么编写敏感列表会很繁琐。
//此时,更为简洁的写法是 @* 或 @(*),表示对语句块中的所有输入变量的变化都是敏感的。例如:
always @(*) begin
//always @(a, b, c, d, e, f, g, h, i, j, k, l, m) begin
//两种写法等价
assign s = a? b+c : d ? e+f : g ? h+i : j ? k+l : m ;
end
- 电平敏感事件控制
前面所讨论的事件控制都是需要等待信号值的变化或事件的触发,使用 @+敏感列表
的方式来表示的。
Verilog 中还支持使用电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。Verilog 中使用关键字 wait
来表示这种电平敏感情况。例如:
initial begin
wait (start_enable) ; //等待 start 信号
forever begin
//start信号使能后,在clk_samp上升沿,对数据进行整合
@(posedge clk_samp) ;
data_buf = {data_if[0], data_if[1]} ;
end
end
Verilog 语句块
关键词:顺序块,并行块,嵌套块,命名块,disable
Verilog 语句块提供了将两条或更多条语句组成语法结构上相当于一条一句的机制。主要包括两种类型:顺序块和并行块。
顺序块
顺序块用关键字 begin 和 end 来表示。
顺序块中的语句是一条条执行的。当然,非阻塞赋值除外。
顺序块中每条语句的时延总是与其前面语句执行的时间相关。
在本节之前的仿真中,initial 块中的阻塞赋值,都是顺序块的实例。
并行块
并行块有关键字 fork
和 join
来表示。
并行块中的语句是并行执行的,即便是阻塞形式的赋值。
并行块中每条语句的时延都是与块语句开始执行的时间相关。
顺序块与并行块的区别显而易见,下面用仿真说明。
`timescale 1ns/1ns
module test ;
reg [3:0] ai_sequen, bi_sequen ;
reg [3:0] ai_paral, bi_paral ;
reg [3:0] ai_nonblk, bi_nonblk ;
//(1)Sequence block
initial begin
#5 ai_sequen = 4'd5 ; //at 5ns
#5 bi_sequen = 4'd8 ; //at 10ns
end
//(2)fork block
initial fork
#5 ai_paral = 4'd5 ; //at 5ns
#5 bi_paral = 4'd8 ; //at 5ns
join
//(3)non-block block
initial fork
#5 ai_nonblk <= 4'd5 ; //at 5ns
#5 bi_nonblk <= 4'd8 ; //at 5ns
join
endmodule
嵌套块
顺序块和并行块还可以嵌套使用。
`timescale 1ns/1ns
module test ;
reg [3:0] ai_sequen2, bi_sequen2 ;
reg [3:0] ai_paral2, bi_paral2 ;
initial begin
ai_sequen2 = 4'd5 ; //at 0ns
fork
#10 ai_paral2 = 4'd5 ; //at 10ns
#15 bi_paral2 = 4'd8 ; //at 15ns
join
#20 bi_sequen2 = 4'd8 ; //at 35ns
end
endmodule
命名块
我们可以给块语句结构命名。
命名的块中可以声明局部变量,通过层次名引用的方法对变量进行访问。
`timescale 1ns/1ns
module test;
initial begin: runoob //命名模块名字为runoob,分号不能少
integer i ; //此变量可以通过test.runoob.i 被其他模块使用
i = 0 ;
forever begin
#10 i = i + 10 ;
end
end
reg stop_flag ;
initial stop_flag = 1'b0 ;
always begin : detect_stop
if ( test.runoob.i == 100) begin //i累加10次,即100ns时停止仿真
$display("Now you can stop the simulation!!!");
stop_flag = 1'b1 ;
end
#10 ;
end
endmodule
命名的块也可以被禁用,用关键字 disable
来表示。
disable 可以终止命名块的执行,可以用来从循环中退出、处理错误等。
与 C 语言中 break
类似,但是 break
只能退出当前所在循环,而 disable
可以禁用设计中任何一个命名的块。
`timescale 1ns/1ns
module test;
initial begin: runoob_d //命名模块名字为runoob_d
integer i_d ;
i_d = 0 ;
while(i_d<=100) begin: runoob_d2
# 10 ;
if (i_d >= 50) begin //累加5次停止累加
disable runoob_d3.clk_gen ;//stop 外部block: clk_gen
disable runoob_d2 ; //stop 当前block: runoob_d2
end
i_d = i_d + 10 ;
end
end
reg clk ;
initial begin: runoob_d3
while (1) begin: clk_gen //时钟产生模块
clk=1 ; #10 ;
clk=0 ; #10 ;
end
end
endmodule
仿真结果如下:
由图可知,信号 i_d 累加到 50 以后,便不再累加,以后 clk 时钟也不再产生。
可见,disable
退出了当前的 while 块。
需要说明的是,disable
在 always
或 forever
块中使用时只能退出当前回合,下一次语句还是会在 always
或 forever
中执行。因为 always
块和 forever
块是一直执行的,此时的 disable
有点类似 C 语言中的 continue
功能。
Verilog 条件语句
关键词:if,选择器
条件语句
条件(if)语句用于控制执行语句要根据条件判断来确定是否执行。
条件语句用关键字 if 和 else 来声明,条件表达式必须在圆括号中。
条件语句使用结构说明如下:
if (condition1) true_statement1 ;
else if (condition2) true_statement2 ;
else if (condition3) true_statement3 ;
else default_statement ;
下面代码实现了一个 4 路选择器的功能。
module mux4to1(
input [1:0] sel ,
input [1:0] p0 ,
input [1:0] p1 ,
input [1:0] p2 ,
input [1:0] p3 ,
output [1:0] sout);
reg [1:0] sout_t ;
always @(*) begin
if (sel == 2'b00)
sout_t = p0 ;
else if (sel == 2'b01)
sout_t = p1 ;
else if (sel == 2'b10)
sout_t = p2 ;
else
sout_t = p3 ;
end
assign sout = sout_t ;
endmodule
事例中 if 条件每次执行的语句只有一条,没有使用 begin 与 end 关键字。但如果是 if-if-else 的形式,即便执行语句只有一条,不使用 begin 与 end 关键字也会引起歧义。
//例如下面代码,虽然格式上加以区分,但是 else 对应哪一个 if 条件,是有歧义的。
if(en)
if(sel == 2'b1)
sout = p1 ;
else
sout = p0 ;
//例如上述代码稍作修改,就不会再有书写上的歧义。
if(en) begin
if(sel == 2'b1) begin
sout = p1 ;
end
else begin
sout = p0 ;
end
end
Verilog 多路分支语句
关键词:case,选择器
case 语句是一种多路条件分支的形式,可以解决 if 语句中有多个条件选项时使用不方便的问题。
case 语句
//case 语句格式如下:
case(case_expr)
condition1 : true_statement1 ;
condition2 : true_statement2 ;
……
default : default_statement ;
endcase
条件选项可以有多个,不仅限于 condition1、condition2 等,而且这些条件选项不要求互斥。虽然这些条件选项是并发比较的,但执行效果是谁在前且条件为真谁被执行。
ture_statement1 等执行语句可以是一条语句,也可以是多条。如果是多条执行语句,则需要用 begin 与 end 关键字进行说明。
下面用 case 语句代替 if 语句实现了一个 4 路选择器的功能。
module mux4to1(
input [1:0] sel ,
input [1:0] p0 ,
input [1:0] p1 ,
input [1:0] p2 ,
input [1:0] p3 ,
output [1:0] sout);
reg [1:0] sout_t ;
always @(*) begin
case(sel)
2'b00: begin
sout_t = p0 ;
end
2'b01: sout_t = p1 ;
2'b10: sout_t = p2 ;
default: sout_t = p3 ;
endcase
end
assign sout = sout_t ;
endmodule
case 语句中的条件选项表单式不必都是常量,也可以是 x 值或 z 值。
当多个条件选项下需要执行相同的语句时,多个条件选项可以用逗号分开,放在同一个语句块的候选项中。
但是 case 语句中的 x 或 z 的比较逻辑是不可综合的,所以一般不建议在 case 语句中使用 x 或 z 作为比较值。
例如,对 4 路选择器的 case 语句进行扩展,举例如下:
case(sel)
2'b00: sout_t = p0 ;
2'b01: sout_t = p1 ;
2'b10: sout_t = p2 ;
2'b11: sout_t = p3 ;
2'bx0, 2'bx1, 2'bxz, 2'bxx, 2'b0x, 2'b1x, 2'bzx :
sout_t = 2'bxx ;
2'bz0, 2'bz1, 2'bzz, 2'b0z, 2'b1z :
sout_t = 2'bzz ;
default: $display("Unexpected input control!!!");
endcase
casex/casez 语句
casex、 casez 语句是 case 语句的变形,用来表示条件选项中的无关项。
casex 用 “x” 来表示无关值,casez 用问号 “?” 来表示无关值。
两者的实现的功能是完全一致的,语法与 case 语句也完全一致。
但是 casex、casez 一般是不可综合的,多用于仿真。
例如用 casez 语句来实现一个 4bit 控制端的 4 路选择选择器。
module mux4to1(
input [3:0] sel ,
input [1:0] p0 ,
input [1:0] p1 ,
input [1:0] p2 ,
input [1:0] p3 ,
output [1:0] sout);
reg [1:0] sout_t ;
always @(*)
casez(sel)
4'b???1: sout_t = p0 ;
4'b??1?: sout_t = p1 ;
4'b?1??: sout_t = p2 ;
4'b1???: sout_t = p3 ;
default: sout_t = 2'b0 ;
endcase
assign sout = sout_t ;
endmodule
Verilog 循环语句
关键词:while, for, repeat, forever
Verilog 循环语句有 4 种类型,分别是 while,for,repeat,和 forever 循环。循环语句只能在 always 或 initial 块中使用,但可以包含延迟表达式。
while 循环
while 循环语法格式如下:
while (condition) begin
…
end
while 循环中止条件为 condition 为假。
如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。
当然,执行语句只有一条时,关键字 begin 与 end 可以省略。
下面代码执行时,counter 执行了 11 次。
`timescale 1ns/1ns
module test ;
reg [3:0] counter ;
initial begin
counter = 'b0 ;
while (counter<=10) begin
#10 ;
counter = counter + 1'b1 ;
end
end
//stop the simulation
always begin
#10 ; if ($time >= 1000) $finish ;
end
endmodule
for 循环
for 循环语法格式如下:
for(initial_assignment; condition ; step_assignment) begin
…
end
initial_assignment 为初始条件。
condition 为终止条件,condition 为假时,立即跳出循环。
step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。
下面 for 循环的例子,实现了与 while 循环中例子一样的效果。需要注意的是,i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i – 的形式。
// for 循环语句
integer i ;
reg [3:0] counter2 ;
initial begin
counter2 = 'b0 ;
for (i=0; i<=10; i=i+1) begin
#10 ;
counter2 = counter2 + 1'b1 ;
end
end
repeat 循环
repeat 循环语法格式如下:
repeat (loop_times) begin
…
end
repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
下面 repeat 循环例子,实现了与 while 循环中的例子一样的效果。
// repeat 循环语句
reg [3:0] counter3 ;
initial begin
counter3 = 'b0 ;
repeat (11) begin //重复11次
#10 ;
counter3 = counter3 + 1'b1 ;
end
end
forever 循环
forever 循环语法格式如下:
forever begin
…
end
forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。
forever 相当于 while(1) 。
通常,forever 循环是和时序控制结构配合使用的。
例如,使用 forever 语句产生一个时钟:
`timescale 1ns/1ns
reg clk ;
initial begin
clk = 0 ;
forever begin
clk = ~clk ;
#5 ;
end
end
Verilog 过程连续赋值
关键词:deassign,force,release
过程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。
与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。
过程连续性赋值主要有 2 种,assign-deassign 和 force-release。
assign, deassign
assign(过程赋值操作)与 deassign (取消过程赋值操作)表示第一类过程连续赋值语句。赋值对象只能是寄存器或寄存器组,而不能是 wire 型变量。
赋值过程中对寄存器连续赋值,寄存器中的值被保留直到被重新赋值。
force, release
force (强制赋值操作)与 release(取消强制赋值)表示第二类过程连续赋值语句。
使用方法和效果,和 assign 与 deassign 类似,但赋值对象可以是 reg 型变量,也可以是 wire 型变量。
因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用。
当 force 作用在寄存器上时,寄存器当前值被覆盖;release 时该寄存器值将继续保留强制赋值时的值。之后,该寄存器的值可以被原有的过程赋值语句改变。
当 force 作用在线网上时,线网值也会被强制赋值。但是,一旦 release 该线网型变量,其值马上变为原有的驱动值。
程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。
与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。
过程连续性赋值主要有 2 种,assign-deassign 和 force-release。
assign, deassign
assign(过程赋值操作)与 deassign (取消过程赋值操作)表示第一类过程连续赋值语句。赋值对象只能是寄存器或寄存器组,而不能是 wire 型变量。
赋值过程中对寄存器连续赋值,寄存器中的值被保留直到被重新赋值。
force, release
force (强制赋值操作)与 release(取消强制赋值)表示第二类过程连续赋值语句。
使用方法和效果,和 assign 与 deassign 类似,但赋值对象可以是 reg 型变量,也可以是 wire 型变量。
因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用。
当 force 作用在寄存器上时,寄存器当前值被覆盖;release 时该寄存器值将继续保留强制赋值时的值。之后,该寄存器的值可以被原有的过程赋值语句改变。
当 force 作用在线网上时,线网值也会被强制赋值。但是,一旦 release 该线网型变量,其值马上变为原有的驱动值。