目录
数据类型:
1.wire类型
用于表示硬件电路单元之间的物理连线。未声明信号类型默认为wire类型。wire不允许在always块内部赋值, 但可以在always块中被用作非阻塞赋值(<=)的一个源。可对wire类型在always块外部用assign连续赋值。
module d_flip_flop( //以D触发器为例:输出信号有reg,wire两种类型,以说明其赋值操作区别
input wire clk, //时钟
input wire rst_n, //复位低有效
input wire D, //输入
output reg Q //reg 类型输出
output wire Q_n //wire类型输出
);
reg Q_n_reg = 1'd0; //定义内部变量
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
Q <= 1'd1 ; //复位
Q_n_reg <= 1'd0 ;
end
else begin
Q <= D ; //wire类型可作为源进行非阻塞赋值
Q_n_reg <= !D ;
end
end
assign Q_n = Q_n_reg; //对wire类型的赋值需在外部进行
endmodule
2.reg类型 、integer整数、real实数
verilog中数字表达形式为:(位宽)'(进制)(数值) 。其中,进制b、o、d、h分别为二、八、十、十六进制。比如:2'b10表示2位二进制数10,即十进制2。4'hA表示4位十六进制数A,即十进制10。
(注意:位宽为二进制的位宽,比如1'd2就是错误表达因为二进制2至少两位)
module xxx(
input reg a,b,
input reg c
);
reg d = 4'b0001 ;
real e = 3.14 ;
interger f = 3e2 ;//即3*10^2=300
......
3.向量(数组)
module xxx(
input wire [7:0] a, //8位宽的输入
output reg [15:0] b, //16位宽的输出
output reg [7:0] c //8位宽的输出
);
reg [7:0] d[3:0] //4个8位的向量
//向量的拼接
always @* begin
b = {a, a}; //将a重复拼接组成16位的向量b
end
//向量的按位截取
always @* begin
c = b[15:8]; //截取b的高8位输出
end
endmodule
4.parameter 参数
用于声明设计参数,是一个常量,不支持小数
parameter time_count = 26'd24_999_999;
parameter width = 8;
当代码多次使用同一个参数时(比如计时器的计数上限值),一个个修改起来较为麻烦,可以通过定义为 parameter 参数,需要修改直接在定义位置修改。
还可在例化模块时用 defparam 进行参数设置,例如例化同一模块使用不同的参数:
timer timer_l(
.clk (clk),
.rst_n (rst_n),
.flag (flag)
)
defparam timer_l.time_count = 26'd24_999_999;
timer timer_2(
.clk (clk),
.rst_n (rst_n),
.flag (flag)
)
defparam timer_2.time_count = 25'd24_999_99;
此外还有另外一种例化参数的方法:
timer
#(
.count(24_999_99)
)
timer_1(
.clk (clk),
.rst_n (rst_n),
.flag (flag)
);
赋值语句:
左侧必须是变量数据类型,可以是reg、integer或real。右侧可以是任何有效的表达式或信号。
1.过程赋值
= 阻塞赋值 (前面的赋值语句阻塞后面的,即按顺序执行,a值给b后,b值再给c)
always @(posedge clk) begin
b = a;
c = b;
end
<= 非阻塞赋值 (不会阻塞,即同时执行)
always @(posedge clk)begin
b <= a;
c <= b;
end
2.连续赋值
用于表示组合逻辑,左侧必须是wire或tri。始终处于活动状态即右侧一变化即会使左侧值被更新。
assign a = b&c;
常用语句
1.if 语句
当 if/ else if/ else 满足条件时只执行一条语句时,可以省略begin end.
当执行多条语句时,用begin end括起来,例如:if(条件1) begin 执行1;执行2; 执行3;end
if (条件1) 执行1;
else if(条件2) 执行2;
else if(条件3) 执行3;
else 执行4;
2.case 语句
用一个四选一数据选择器为例,当sel信号分别为0123时对应输出abcd。
满足当前情况只执行一条语句时,可以省略begin end,当执行多条语句时,用begin end括起来
两种情况执行内容一样时可以用逗号省略,例如:2'd0 ,2'd1 : dout = a;
case(sel)
2'd0 : dout = a;
2'd1 : dout = b;
2'd2 : dout = c;
2'd3 : dout = d;
default : ; //其他情况,即使条件列完也需要写default: ;(相当于什么都不执行)
endcase
testbench仿真模块
1.注意事项
(1)`timescale 接时间单位/精度,#10即表示延时10个时间单位
(2)仿真模块一般没有端口列表(input/output),因为通常输入信号为自定义为reg类型,而输 出则定义为wire类型。
(3)模块例化:仿真模块相当于顶层文件,将待仿真模块例化后再进行验证,例化名可自定义。
(4)若使用linux操作系统,跑仿真需要生成fsdb波形文件,需在代码内直接加入以下代码:
initial begin
$fsdbDumpfile("test.fsdb");
$fsdbDumpvars;
$fsdbDumpMDA;
end
2.代码示例
以验证D触发器功能为例编写一段tb文件代码(dff_tb.v)
`timescale 1ns / 1ps //timescale 时间单位/精度
module dff_tb( ); //仿真一般无端口列表
reg clk_sim;
reg d_sim;
wire q_sim;
wire q_n_sim;
always #10 clk_sim = ~clk_sim; //每10个时间单位对clk进行翻转,即周期为20个单位
initial begin
clk_sim = 0;
d_sim = 0;
#20; //延时20个时间单位(20ns)
d_sim = 1;
#40;
d_sim = 0;
#100;
$stop; //停止仿真 也可以写$finish;
end
dff dff_1( //模块例化: 原模块名----在顶层文件中命名为
.clk (clk_sim), //原模块端口名----要连接的端口名
.d (d_sim),
.q (q_sim),
.q_n (q_n_sim)
);
endmodule