1.1 概述
1.2 模块Module
1.3 数据类型
1.3.1 wire/reg线网
wire和reg都是线类型,工程上没区别;只是always/initial模块中输出定义需要为reg型;
注意:不要将reg类型与D触发器混淆,reg理解为因为代码所产生的。例如:
wire [7:0] a; //定义了8位的wire型数据 wireb; // 定义了1位的wire型数据 |
reg [3:0]sum;//定义了一个4位的reg型数据 |
类型
格式
说明
parameter
parameter数据名= 表达式
parameterMSB = 7;
常量
<位宽><进制><数字>
二进制:B或b;
十进制:D或d;
八进制:O或o;
8’b1010_1100 (‘b表示二进制)
<数字>
默认十进制;
4值逻辑
0:Logic Low
低电平;
1:Logic High
高电平;
x:Unknow;
不确定;
z:High Impedance;
高阻态; //三态门
1.4 运算符
//定义参数MSB为常量7;推荐大写;
十六进制:H或h;
下画线“_”,提高阅读性。
1.4.1 概述
运算符 |
说明 |
算术运算符 |
+(加),-(减),*(乘),/(除),%(取模); |
每个运算符在电路中都是个模块,如加法器,减法器; !注意:除法,除2^n,是移位运算, 浮点运算就复杂了,因此浮点运算要专用除法器; |
|
关系运算符 |
>, <, >=, <=,==(相等), !=(不相等); |
逻辑运算符 |
&&(逻辑与). ||(逻辑或), !(逻辑非); 条件判断语句中,为避免歧义,逻辑运算符二边推荐为1bit; |
位运算符 |
&(与),|(或), ~(非), ^(异或); ~^(同或); |
移位运算符 |
<<(左移),>>(右移); |
归约操作 |
&,~&, |, ~|,^, ~^;//unary reduction; |
条件运算符 |
?: |
拼接运算符 |
{} //{3{a[0]}}:代表3根同样的a[0]线,{a[0],a[0],a[0]} |
1.5.1 assign(连续赋值)
1.1.1 always(过程块)
赋值方式 |
说明 |
=,阻塞赋值 |
always @(a or b or C or …) begin 语句块(=, if语句, case语句) end |
实现:组合逻辑电路;(注意!禁止用于时序逻辑电路) always块内,阻塞赋值:是顺序执行(类似C); |
|
敏感表:@(*) //“*”自动添加相关输入信号; |
|
避免出现Latch(锁存器) 分支语句(if语句,case语句)条件不满时,会在电路中自动生成锁存器来保存不满足条件的值,因此要补全if-else,和case的defalut语句; |
|
<=,非阻塞赋值 |
always @(posedge clk or negedge rst_n) begin 语句块(<=, if语句, case语句) end |
实现:时序逻辑电路;(注意!禁止用于组合逻辑电路) always块内,阻塞赋值:并行执行; |
if语句
条目 |
说明 |
格式1 |
if(条件)begin 语句1; 语句2; end else begin 语句1; 语句2; end |
格式2 |
if(条件)begin 语句1; 语句2; end else if begin 语句1; 语句2; end else begin 语句1; 语句2 end |
特点 |
分支语句,各个分支条件不同;顺序执行判断; |
注意 |
if-else成对使用; |
case语句
条目
说明
格式
case(表达式)
常量表达式1:begin
语句;
end
常量表达式2:begin
语句;
end
常量表达式3:begin
语句;
end
default:
语句;
endcase
特点
分支语句,各个分支条件相同;并行执行判断;
注意
default语句不可省略;
代码&硬件
条目 |
说明 |
格式 |
case(表达式) 常量表达式1:begin 语句; end 常量表达式2:begin 语句; end 常量表达式3:begin 语句; end default: 语句; endcase |
特点 |
分支语句,各个分支条件相同;并行执行判断; |
注意 |
default语句不可省略; |
1.5.3 模块例化
作用
系统设计时,建议遵循以下设计原则:
1.5.4 全加器
描述方式
描述方式
说明
位置关联
AND u1(a, b, and_out);
名字关联
AND u1(.a(a), .b(b), .o(and_out)); //推荐使用
1.6.1 结构
描述方式 |
说明 |
位置关联 |
AND u1(a, b, and_out); |
名字关联 |
AND u1(.a(a), .b(b), .o(and_out)); //推荐使用 |
1.6.2 特殊符号
语句
说明
`<标识符>
表示:
编译引导语,用于指导仿真编译器在编译时采取一些特殊处理;
编译引导语句一直保持有效,直到被取消或重写;
`timescale
`timescale <时间单位>/<时间精度>
例1:
`timescale 1ns/1ns //时间单位1ns;时间精度1ns;
#2 //延时2 ×1=2ns;
#2.1//延时2.1 × 1 = 2.1ns,精确到1ns,为2ns;
例2:
`timescale 1ns/100ps //时间单位1ns;时间精度100ps;
#2 //延时2 ×1= 2ns;
#2.1//延时2.1 × 1 = 2.1ns,精确到100s,为2.1ns;
`define
`include
`include “global.v”
包含另一个文件,完整拷贝过来;
`restall
把所有设置的编译引导恢复到缺省状态;
#<num>;
#10; //延迟10个时间单位
1.6.3 语句
语句 |
说明 |
`<标识符> |
表示: 编译引导语,用于指导仿真编译器在编译时采取一些特殊处理; 编译引导语句一直保持有效,直到被取消或重写; |
`timescale |
`timescale <时间单位>/<时间精度> 例1: `timescale 1ns/1ns //时间单位1ns;时间精度1ns; #2 //延时2 ×1=2ns; #2.1//延时2.1 × 1 = 2.1ns,精确到1ns,为2ns; 例2: `timescale 1ns/100ps //时间单位1ns;时间精度100ps; #2 //延时2 ×1= 2ns; #2.1//延时2.1 × 1 = 2.1ns,精确到100s,为2.1ns; |
`define |
|
`include |
`include “global.v” 包含另一个文件,完整拷贝过来; |
`restall |
把所有设置的编译引导恢复到缺省状态; |
|
|
#<num>; |
#10; //延迟10个时间单位 |
语句
说明
initial
块语句:只执行一次,always循环执行;不可综合;
作用:
产生激励信号;
检查输出波形;
赋初值;
forever
//产生周期信号:
intial begin
clk = 0;
forever
#10 clk = ~clk; //时钟信号
end
语句 |
说明 |
initial |
块语句:只执行一次,always循环执行;不可综合; |
作用: 产生激励信号; 检查输出波形; 赋初值; |
|
forever |
//产生周期信号: intial begin clk = 0; forever #10 clk = ~clk; //时钟信号 end |
1.6.4 系统任务和函数
条目
说明
$<标识符>
表示Verilg的系统任务和函数
$time
当前的仿真时间
$display
显示信号值变化:只执行一次,打印当前时刻;
$display($time, “b% %b %b”, rst,clk,dout);
$monitor
监视信号值变化:所有过程时刻;
$monitor($time, “b% %b %b”, rst,clk,dout);
$stop
暂停仿真
$finish
结束仿真,释放电脑资源;
1.7.1 组合逻辑电路
条目
说明
assign
assign add_cnt = flag==1; //用于简单的组合逻辑电路;
always
always @(*)begin//统一采用“*”为敏感列表;
(=,if,case)语句;//只能使用“=”赋值
end
1.7.2 时序逻辑电路
条目 |
说明 |
$<标识符> |
表示Verilg的系统任务和函数 |
$time |
当前的仿真时间 |
$display |
显示信号值变化:只执行一次,打印当前时刻; $display($time, “b% %b %b”, rst,clk,dout); |
$monitor |
监视信号值变化:所有过程时刻; $monitor($time, “b% %b %b”, rst,clk,dout); |
$stop |
暂停仿真 |
$finish |
结束仿真,释放电脑资源; |
条目 |
说明 |
assign |
assign add_cnt = flag==1; //用于简单的组合逻辑电路; |
always |
always @(*)begin//统一采用“*”为敏感列表; (=,if,case)语句;//只能使用“=”赋值 end |
计数器模板1
3段式模板 |
模板1 |
|
1 |
计数段 |
always @( posedge cllk or negedge rst_n) begin if (!rst_n) cnt <= 0; //初值规定为0 else if (add_cnt)begin//【位置1】 if(end_cnt) cnt <= 0; else cnt <= cnt + 1; end end |
2 |
加1条件 |
assingadd_cnt = d==1; //d==1: 什么时候开始数脉冲 |
3 |
结束条件 |
assing end_cnt = add_cnt&& cnt == X-1; // X:数多少个脉冲 |
计数器模板2
3段式模板 |
模板1 |
|
1 |
计数段 |
always @( posedge cllk or negedge rst_n) begin if (!rst_n) cnt <= 0; //初值规定为0 else if (add_cnt) begin//【位置1】 if(end_cnt) cnt <= 0; else cnt <= cnt + 1; end else cnt <= 0; //不连续,需要清0时,使用模板2; end |
2 |
加1条件 |
assingadd_cnt = d==1; //d==1: 什么时候开始数脉冲 |
3 |
结束条件 |
assing end_cnt = add_cnt&& cnt == X-1; // X:数多少个脉冲 |
模板4段式状态机
段号
代码
1
// 初始化,次态赋值给现态,明确当前状态;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
state_c <= S00;//初始状态
else
state_c <= state_n;
end
2
always @( * ) begin //组合逻辑,描述状态转换目标
case(state_c)
S00: begin
if(s00_s20_start) // 条件名 S00->S20
state_n = S20;
else
state_n = state_c; // 方便拷贝
end
S20: begin
if(s20_s21_start)
state_n = S21;
else
state_n = state_c;
end
S21: begin
if(s21_s00_start)
state_n = S00;
else
state_n = state_c;
end
default: begin
state_n = S00;
end
endcase
end
3
//具体的转换条件内容
assign s00_s20_start = state_c==S00&& (条件);
assign s20_s21_start = state_c==S20&& (条件);
assign s21_s20_start = state_c==S21&& (条件);
4
根据转态设计输出:
1个 always 设计1个输出信号;
1.7.3 Testbench
框架
段号 |
代码 |
1 |
// 初始化,次态赋值给现态,明确当前状态; always @(posedge clk or negedge rst_n) begin if(!rst_n) state_c <= S00;//初始状态 else state_c <= state_n; end |
2 |
always @( * ) begin //组合逻辑,描述状态转换目标 case(state_c) S00: begin if(s00_s20_start) // 条件名 S00->S20 state_n = S20; else state_n = state_c; // 方便拷贝 end S20: begin if(s20_s21_start) state_n = S21; else state_n = state_c; end S21: begin if(s21_s00_start) state_n = S00; else state_n = state_c; end default: begin state_n = S00; end endcase end |
3 |
//具体的转换条件内容 assign s00_s20_start = state_c==S00&& (条件); assign s20_s21_start = state_c==S20&& (条件); assign s21_s20_start = state_c==S21&& (条件); |
4 |
根据转态设计输出: 1个 always 设计1个输出信号; |
条目 |
内容 |
模块名 |
`timescale 1 ns/1 ns module testbench_name(); |
信号定义 |
reg clk ; //时钟 reg rst_n; //复位 reg[3:0] din0 ; //uut的输入信号 ,定义为reg型,在initial中 reg din1 ; wire dout0;//uut的输出信号, 定义为wire型 wire[4:0] dout1; parameter CYCLE = 20; //参数定义,方便修改; parameter RST_TIME = 3 ; |
待测模块例化 |
module_name uut( //统一采用名字关联 .clk ( clk ), .rst_n ( rst_n ), .din0 ( din0 ), .din1 ( din1 ), .dout0 ( dout0 ), .dout1 ( dout1 ) ); |
激励产生 |
//复位,时钟 ,等 |
显示输出结果 |
$display //类似printf; |
复位
复位
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
仿真时钟
仿真时钟
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
复位 |
initial begin rst_n = 1; #2; rst_n = 0; #(CYCLE*RST_TIME); rst_n = 1; end |
仿真时钟 |
initial begin clk = 0; forever #(CYCLE/2) clk=~clk; end |
激励信号
激励信号
initial begin
#1;//方便观测
din1 = 0; //赋初值
#(10*CYCLE);
//开始赋值
end
以上就是本人总结的Verilog语法相关知识点,当然明德扬还有很多比较简便的模板给我们使用,感兴趣的朋友可以进入明德扬论坛(http://www.fpgabbs.cn/)进行更多FPGA 或者语法相关讨论!
激励信号 |
initial begin #1;//方便观测 din1 = 0; //赋初值 #(10*CYCLE); //开始赋值 end |