推荐Verilog学习网址:
HDLbits
EDAPlayground
菜鸟教程
因为自己以前学过Verilog,现在要重新捡起来,所以就记录了一下不熟悉的部分。
时序控制
代码来自菜鸟教程4.3 Verilog 时序控制 | 菜鸟教程
1.时延控制
常规时延
#10 value1 = value_test;
内嵌时延
value1 = #10 value_test;
value_test的当前值(而不是#10后的值),在#10后,赋给value1
`timescale 1ns/1ns
module test ;
reg value_test ;
reg value_general, value_embed, value_single ;
//signal source
initial begin
value_test = 0 ;
#25 ; value_test = 1 ;
#35 ; value_test = 0 ; //absolute 60ns
#40 ; value_test = 1 ; //absolute 100ns
#10 ; value_test = 0 ; //absolute 110ns
end
//(1)general delay control
initial begin
value_general = 1;
#10 value_general = value_test ; //10ns, value_test=0
#45 value_general = value_test ; //55ns, value_test=1
#30 value_general = value_test ; //85ns, value_test=0
#20 value_general = value_test ; //105ns, value_test=1
end
//(2)embedded delay control
initial begin
value_embed = 1;
value_embed = #10 value_test ; //0ns, value_test=0
value_embed = #45 value_test ; //10ns, value_test=0
value_embed = #30 value_test ; //55ns, value_test=1
value_embed = #20 value_test ; //85ns, value_test=0
end
always begin
#10;
if ($time >= 150) begin
$finish ;
end
end
endmodule
2.事件触发
1. @ 敏感列表
e.g. always@(clk) //clk的上升沿与下降沿触发
always@(posedge clk or negedge rst) //clk的上升沿,rst的下降沿
always@(posedge clk , negedge rst)
always@(*)
2. 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
3.边沿触发
4.电平触发
initial begin
wait (start_enable) ; //等待 start 信号
forever begin
//start信号使能后,在clk_samp上升沿,对数据进行整合
@(posedge clk_samp) ;
data_buf = {data_if[0], data_if[1]} ;
end
end
块语句
1.顺序块 begin end
2.并行块 fork join
3.嵌套块
4.命名块
代码来自菜鸟教程4.4 Verilog 语句块 | 菜鸟教程
begin:模块名
int i; // module名.模块名.i 就可以调用此变量
end
模块名可以嵌套
`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
5.disable
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
disable在always或forever中使用时只能退出当前回合,下一次语句还是会在always或forever中执行。
casex/casez
casex 用 "x" 来表示无关值,casez 用问号 "?" 来表示无关值。
两者的实现的功能是完全一致的,语法与 case 语句也完全一致。
但是 casex、casez 一般是不可综合的,多用于仿真。
repeat
repeat (循环次数) begin //循环次数必须是常量
end
过程连续赋值
过程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。
与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。
assign-deassign
assign(过程赋值操作)与 deassign (取消过程赋值操作)
赋值对象只能reg型。
module dff_assign(
input rstn,
input clk,
input D,
output reg Q
);
always @(posedge clk) begin
Q <= D ; //Q = D at posedge of clock
end
always @(negedge rstn) begin
if(!rstn) begin
assign Q = 1'b0 ; //在复位信号为 0 时,Q 端被 assign 语句赋值,始终输出为 0。
end
else begin //cancel the Q value overlay
deassign Q ; //复位信号为 1 时,Q 端被deassign语句取消赋值,在时钟上升沿被重新赋值
end
end
endmodule
force-release
force (强制赋值操作)与 release(取消强制赋值)
赋值对象可以是 reg 型变量,也可以是 wire 型变量。
因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用。
当 force 作用在寄存器上时,寄存器当前值被覆盖;release 时该寄存器值将继续保留强制赋值时的值。之后,该寄存器的值可以被原有的过程赋值语句改变。
当 force 作用在线网上时,线网值也会被强制赋值。但是,一旦 release 该线网型变量,其值马上变为原有的驱动值。
在 Verilog 中,端口隐式的声明为 wire 型变量。
模块例化
generate
可以对同一个模块进行重复例化
defparam
可以用关键字 defparam 通过模块层次调用的方法,来改写低层次模块的参数值。
defparam 一般也不可综合。
defparam u_ram_4x4.MASK = 7 ;
ram_4x4 u_ram_4x4
(
.CLK (clk),
.A (a[4-1:0]),
.D (d),
.EN (en),
.WR (wr), //1 for write and 0 for read
.Q (q) );
//在ram_4x4模块中,定义了parameter MASK = 3;
将新的参数值写入模块例化语句
(推荐该方法)
module ram
#( parameter AW = 2 ,
parameter DW = 3 )
(
input CLK ,
input [AW-1:0] A ,
input [DW-1:0] D ,
input EN ,
input WR , //1 for write and 0 for read
output reg [DW-1:0] Q
);
endmodule
//例化:
ram #(.AW(4), .DW(4))
u_ram
(
.CLK (clk),
.A (a[AW-1:0]),
.D (d),
.EN (en),
.WR (wr), //1 for write and 0 for read
.Q (q)
);
在编译器看来,如果有模块在端口声明时的参数,那么实体中的参数将视为 localparam 类型,使用 defparam 将不能改写模块实体中声明的参数。