在SV中,为了区分硬件设计和软件的世界,我们将定义的软件变量或者例化的硬件所在的空间称之为 “域”。
因此,module/endmodule,interface/endinterface可以被视为硬件世界,program/endprogram和class/endclass可以被视为软件世界。掌握了这一清晰的概念,有助于分析initial和always的使用域。
注:initial从其执行路径的属性来看,它不应该存在于硬件设计代码中,它本身不可综合,对于描述电路没有任何帮助,initial就是为了测试而生的,由于测试需要按照时间顺序的习惯即软件方式来完成,多以initial便可以实现这一要求,initial过程块可以在module、interface、program中使用。
initial语句在仿真一开始就立刻开始执行(0时刻),之后如果遇到延时,则延时之后执行接下来的语句,每个initial语句只执行一次。如下例:
initial //语句块内含2条或以上语句需要使用begin…end或
begin
statement1;//可以是过程赋值语句/分支语句
statement2; //循环语句,等待语句,顺序执行语句/并行执行语句
….. ;
statementN;
end
注意:
- 可以有多个(任意多)initial块。多个initial块并行执行,每个块的执行是独立的。
- 包含多条语句的initial过程块需要使用begin…end或fork…join块将这些语句封装成语句块块。
- 常用于测试模块,虚拟模块的编写,用于产生仿真测试激励信号,信号初始化等仿真环境。
module example_initial;
reg clk;
reg a,b;
initial begin
clk=1’b0;
#2 a=1;
#3 b=1;
#5 a=1;
b=1
end
endmodule
always块是Verilog中用来描述组合逻辑以及时序逻辑的语法。
需要补充的是一个设计中可以有多个always块,或者说一定有很多个always块。
这些硬件块都是相互独立同时工作的。每个块之间的连接是决定数据流的原因。为了模拟这种行为,一个always块被做成一个连续的过程(硬件不可能断断续续工作),当敏感列表中的一个信号变化时,它就会被触发并执行一些动作(always块内的语句)。
always是为了描述硬件的行为,多用于设计之中。需要注意的是,它有两种使用方式
1、用于时序逻辑电路描述:敏感信号列表应该出现的是 时钟信号
2、用于组合逻辑电路描述:敏感信号列表没有时钟信号
什么是敏感列表?
敏感列表就是触发always块内部语句的条件。
在下面的代码中,每当信号a或b的值发生变化时,always块中的所有语句都会被执行。
// Execute always block whenever value of "a" or "b" change
always @ (a or b) begin
[statements]
end
always中的@(event)敏感列表是为了描述硬件信号的触发行为。
所以说,always过程块是用来描述硬件时序电路和组合电路的正确打开方式,因此只可以在module或者interface中使用。
如果没有敏感列表怎么办?
其实,还真的可以没有敏感列表,这是仿真中的用法。我们经常使用没有敏感列表的always来表示不断的触发,用此特性来生成时钟。
always #10 clk = ~clk;
always块内的敏感列表就是为了控制内部语句什么时候触发的。那么可以理解为一种定时,如果没有了敏感列表,则为零延迟,那么就会不断的触发
always clk = ~clk;
当然,这种没有敏感列表的显示延迟,是不能综合的,只能用于仿真。
SystemVerilog对Verilog 的always语句做了扩展,除了always外还有:
- always_comb //组合逻辑建模
- always_latch //为latch建模
- always_ff //为时序逻辑建模
always_comb用于描述复杂电路,comb是combination组合的意思。该模块中输出是等号的左边,输入是等号的右边。
always_comb内部每条语句都是赋值语句。不能出现电路语句。
always_comb内部描述电路行为。
always_comb有以下性质: 内部覆盖性(如:上例中,a赋值了两遍)
对外原子性(begin…end的内部会影响外面的assign a = b)
assign a = b;
always_comb begin
b = 1'b1;
c = a; // 区别于c = b,c = b此处c=1
b = 1'b0;
end
//对外的结果b=0,a=0,c=0
阻塞赋值
always_comb内部允许if,case等控制语句。
always_comb中的控制语句:case
case语句常用于描述选择器和译码器。case类似于c的switch语句
unique case(独特)
always_comb begin
b = 1'b0;
unique case (a[3:0])
4'd1: begin
b = 1'b1;
end
4'd0: begin
b = 1'b0;
end
default: begin
b = 1'b0;
// b = b;//形成锁存器,该语句不正确
end
endcase
end
如果没列举所有情况且没有default,如何?
答:缺少可选择的项,等待,可能形成锁存器
priority case(优先级)
语义上,可以理解为if…else语句
//把常量当case的枚举项,可能出现多项匹配,a[3]和a[2]都可能=1,但是a[3]比a[2]优先级高
always_comb begin
priority case (1'b1)
a[3]: begin
...
end
a[2]: begin
...
end
default: begin
...
end
endcase
end
等同于下面的代码
always_comb begin
if(a[3]) begin
...
end
else if(a[2]) begin
...
end
else begin
...
end //...
end
always_comb中的控制语句:if和for
if和for是always_comb中的常用语法。
if和else用于条件判断。
always_comb begin
if (a[3]) begin
b = 1'b1;
end else if (a[2]) begin
b = 1'b0;
end else begin
b = 1'b0;
end
end
for
for在always_comb中,会被解释为循环展开。
for相关的语句:break,continue
logic [15:0]a;
logic [3:0] b;
//interger i;//此处是全局变量
always_comb begin
b = '0;
for (int i = 15; i >= 0; i--) begin//把i放在内部声明,是局部变量
if (a[i]) begin
b = i[3:0];
break;
end
end
end
for和if即可表示行为,也可生成电路。
always_comb begin
for (int i = 0; i < 16; i++) begin
a[i] = b[i] & (c[i] == d[i] | e[i]); // 编译器不认为i是常数,a[i:i+3]非法
end
end
for (genvar i = 0; i < 16; i++) begin
assign a[i] = b[i] & (c[i] == d[i] | e[i]); // 编译器认为i是常数,a[i:i+3]合法
always_comb begin
end
end
always_ff用于可综合时序逻辑的建模。
必须带由posedge或者negedge所定义的敏感列表。通常就是@(posedge clk, negedge rst).
在always_ff块中只能使用非阻塞赋值。
1.final块类似于intial块,它们都定义了一个过程化的语句块,不同的是,final块仅仅在仿真结束前执行。典型情况下,final块用来显示有关仿真的统计信息。
2. 在final块中可以使用的语句同允许在一个函数声明中使用的语句一样。与intial块不同,final块不会作为一个单独的进程执行。
3. 当一个显式或隐式的$finish调用引起仿真结束的时候会执行final块。
4. final块只能在一次仿真中触发一次
finial语句示例如下:
final
begin
$display("Number of cyclesexecuted %d",$time/period);
$display("Final PC = %h",PC);
end
initial begin
…
$finish();
end