1.1 概述
条目 |
说明 |
分类 |
1>> 面向设计的语句; // 可综合。 2>> 面向测试的语句; //testbench ,不可综合。 |
特点 |
设计语句 assign , always ,模块例化,都对应实际电路,并行执行。 |
构造 |
1.2 模块 Module
条目 |
说明 |
模块名(端口列表) |
整个电路的外特性,抽象为黑盒子; |
端口方向 |
input , output ; inout ; |
端口类型 |
wire , reg ; 端口类型是 wire 时可以省略。 例: input a ; // 端口方向为输入,类型默认为 wire ; |
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 型数据 |
1.3.2 常量
类型 |
格式 |
说明 |
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 运算符
1.4.1 概述
运算符 |
说明 |
算术运算符 |
+ ( 加 ) , - (减), * (乘), / (除), % (取模); |
每个运算符在电路中都是个模块,如加法器,减法器; !注意:除法,除 2^n ,是移位运算, 浮点运算就复杂了,因此浮点运算要专用除法器; |
|
关系运算符 |
>, <, >=, <= , == (相等), ! = (不相等); |
逻辑运算符 |
&& (逻辑与) . || (逻辑或) , ! (逻辑非); 条件判断语句中,为避免歧义,逻辑运算符二边推荐为 1bit ; |
位运算符 |
& (与), | (或), ~ (非) , ^ (异或) ; ~^ (同或); |
移位运算符 |
<< (左移), >> (右移); |
归约操作 |
& , ~& , | , ~| , ^, ~^;//unary reduction ; |
条件运算符 |
?: |
拼接运算符 |
{} //{3{a[0]}}: 代表 3 根同样的 a[0] 线, {a[0],a[0],a[0]} |
1.5 设计语句
1.5.1 assign (连续赋值)
实例 |
说明 |
assigny = ~ b ; assign out = a==1 && c==1 ; assign f = sel ? a : b ; |
>> 实现可以用布尔函数描述的组合逻辑电路; |
>>“=” 后面可以是任何布尔函数; >> 并行执行; |
|
典型错误 1 : assigna = b + a; |
避免出现反馈电路:变为了不可知时序逻辑电路; |
1.5.2 always (过程块)
a、赋值
赋值方式 |
说明 |
= ,阻塞赋值 |
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 块内,阻塞赋值:并行执行; |
b、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 成对使用; |
c、case 语句
条目 |
说明 |
格式 |
case( 表达式 ) 常量表达式 1:begin 语句; end 常量表达式 2:begin 语句; end 常量表达式 3:begin 语句; end default : 语句; endcase |
特点 |
分支语句,各个分支条件相同;并行执行判断; |
注意 |
default 语句不可省略; |
d、代码 & 硬件
条目 |
说明 |
映射 |
赋值语句 -> 逻辑函数; // 加法器,减法器等; |
边沿型条件分支 -> D 触发器; |
|
条件分支 -> 多路选择器; |
|
示例 |
1.5.3 模块例化
a、作用
系统设计时,建议遵循以下设计原则:
b、常见的典型错误如下所示:
1.5.4 全加器
全加器顶层: w1 , w2 , w3 :模块之间连线; |
半加强: 2 种描述方法,如下: |
描述方式
描述方式 |
说明 |
位置关联 |
AND u1(a, b, and_out); |
名字关联 |
AND u1(.a(a), .b(b), .o ( and_out ) ); // 推荐使用 |
1.6 测试语句
1.6.1 结构
Testbench |
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 语句
语句 |
说明 |
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.7.1 组合逻辑电路
条目 |
说明 |
assign |
assign add_cnt = flag==1; // 用于简单的组合逻辑电路; |
always |
always @(*)begin// 统一采用“ *” 为敏感列表; ( =,if,case )语句; // 只能使用“ =” 赋值 end |
1.7.2 时序逻辑电路
a、计数器模板 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: 数多少个脉冲 |
b、计数器模板 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: 数多少个脉冲 |
c、 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
a、框架
条目 |
内容 |
模块名 |
`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 ; |
b、复位
复位 |
initial begin rst_n = 1; #2; rst_n = 0; #(CYCLE*RST_TIME); rst_n = 1; end |
c、仿真时钟
仿真时钟 |
initial begin clk = 0; forever #(CYCLE/2) clk=~clk; end |
d、激励信号
激励信号 |
initial begin #1;// 方便观测 din1 = 0; // 赋初值 #(10*CYCLE); // 开始赋值 end |
以上就是总结的 Verilog 语法相关知识点,转自明德扬论坛