Vivado使⽤技巧(28):⽀持的Verilog语法
复杂的电路设计通常使⽤⾃顶向下的设计⽅法,设计过程中的不同阶段需要不同的设计规格。⽐如架构设计阶段,需要模块框图或算法状态机(ASM)图表这⽅⾯的设计说明。⼀个框图或算法的实现与寄存器(reg)和连线(wire)息息相关。Verilog便具有将ASM图表和电路框图⽤计算机语⾔表达的能⼒,本⽂将讲述Vivado综合⽀持的Verilog硬件描述语⾔。
Verilog提供了⾏为化和结构化两⽅⾯的语⾔结构,描述设计对象时可以选择⾼层次或低层次的抽象等级。使⽤Verilog设计硬件时,可以将其视作并⾏处理和⾯向对象编程。Vivado综合⽀持IEEE 1364标准。Vivado综合对Verilog的⽀持可以⽤最有效的⽅式描述整体电路和各个模块。综合会为每个模块选择最佳的综合流程,将⾼层次的⾏为级或低层次的结构级转换为门级⽹表。
本⽂将介绍Vivado综合⽀持的所有Verilog语法。
1.可变部分选择
除了⽤两个明确的值限定选择边界外(如assign out = data[8:2]),还可以使⽤变量从向量中选择⼀组bit。设置⼀个起始点和截取的宽度,起始点可以动态变化。⽰例如下:
reg [3:0] data;
reg [3:0] lect;
wire [7:0] byte = data[lect +: 8]; //+、-表⽰从起始点开始增加或减少
2.结构化Verilog
Verilog可以进⾏多个代码块设计,并按⼀定的设计层次组合起来。下⾯给出于此相关的重要概念:
组件(Component):结构化设计中的⼀个基本块;
申明(Declaration):组件与外部交流的信息;
主体(Body):组件内部的⾏为或结构;
端⼝(Port):组件的I/O;
信号(Signal):组件与组件之间的连线;
⼀个组件⽤常见的模块(module)来表⽰。组件之间的连接由实例化(instantiation)声明实现。实例化声明规定⼀个组件在另外⼀个组件或电路中的实例,赋予标识符,并⽤关系列表设定信号与端⼝之间的联系。
除了⾃⼰设计的组件外,结构化Verilog还⽀持实例化预定义的原语:逻辑门、寄存器、Xilinx特定的原语(如CLKDLL、BUFG)。这些原语都定义在Xilinx Verilog库⽂件unisim_comp.v中。逻辑门原语包括AND、OR、XOR、NAND、NOR、NOT。实例化这些逻辑门来搭建更⼤的逻辑电路,⽰例如下:
//实现2输⼊或⾮逻辑功能
怎么挽回老公的心呢module build_xor
(
input a, b,
output c
);
wire a_not, b_not;
//每个实例必须有不同的实例化名称
not a_inv (a_not, a);
not b_inv (b_not, b);
and a1 (x, a_not, b);
and a2 (y, b_not, a);
or out (c, x, y);
endmodule
3.Verilog参数
参数化代码提⾼了可读性和代码紧凑型、容易维护和再使⽤。⼀个Verilog参数(parameter)就是⼀个常数(不⽀持字符串),且实例化参数化模块时可以改写参数值。下⾯给出⽰例:
//Verilog参数控制实例化块寄存器的宽度
module myreg #(parameter SIZE = 1)
(
input clk, clken,
订做一个天堂input [SIZE-1:0]d,
青鸟的天空
output reg [SIZE-1:0]q
);
怎么连接蓝牙耳机
always @(podge clk)
if (clken) q <= d;
著名诗歌endmodule
//顶层模块
module test #(parameter SIZE = 8)
(北京市房屋租赁合同
input clk, clken,
input [SIZE-1:0] di,
output [SIZE-1:0] do
);
myreg #SIZE inst_reg (clk, clken, di, do);
endmodule
4.Verilog使⽤限制
在Vivado综合中⽤到的Verilog语法有如下3点限制:
⼤⼩写敏感:Verilog是⼀种⼤⼩写敏感的语⾔,但在Vivado中,只有实例和信号名称会区分⼤⼩写。如果两个module名称只有⼤⼩写不同,综合时会报错。尽管如此,也不推荐仅⽤⼤⼩写区分两个不同的对象,在混合语⾔⼯程中可能会引起意料之外的问题。
阻塞和⾮阻塞赋值:不要混合使⽤阻塞和⾮阻塞赋值。尽管综合时可能不会报错,但在仿真时会出现错误。下⾯给出两个错误例⼦:
//同⼀信号不要混⽤阻塞和⾮阻塞赋值
always @(in1)
if (in2) out1 = in1;
el out1 <= in2;
//同⼀信号的不同bit不要混⽤
if (in2) begin
out1[0] = 1'b0;
out1[1] <= in1;
end
el begin
out1[0] = in2;
out1[1] <= 1'b1;
end
整数处理:某些情况下,Vivado综合器处理整数时与其它综合⼯具⽅法不同,因此必须使⽤特定的代码编写⽅式。在Ca语句或拼接语句中,使⽤未定义⼤⼩的整数都会导致⽆法预料的结果。下⾯给出例⼦:
//ca语句
reg [2:0] condition1;
always @(condition1) begin
ca(condition1)
4 : data_out = 2; //⽣成错误结果
3'd4 : data_out = 2; //正常⼯作
endca
end
//拼接语句
reg [31:0] temp;
assign temp = 4'b1111 % 2; //未确定位宽的运算⽤临时信号存储
assign dout = {12/3,temp,din}; //12/3运算位宽不确定,结果错误
5.Verilog构造和系统任务
Vivado综合⽀持的Verilog构造与系统任务包括:
整数、实数、assign(有限制)、deassign(有限制)、repeat语法(重复值必须是常数)、for语法(范围必须是静态的)、
disable(不能⽤于for循环和repeat循环)、module定义、defparam、实例数组、`default_nettype、`define、`ifdef、`ifndef、
`elsif、`include、`file、`line、$fclo、$fgets、$fopen、$fscanf、$readmemb、$readmemh、$signed、$unsigned、
$floor(仅⽤于参数)、$ceil(仅⽤于参数)。
Vivado综合不⽀持和会忽视的的Verilog构造和系统任务包括:
字符串、⽹络类型(tri0、tri1、trireg)、驱动强度、实数和实时寄存器、命名事件、事件(@)、延迟(#)、force、relea、forever语法、wait、并⾏块、设定块、macromodule定义、层次结构名称、`celldefine、`endcelldefine、`retall、`timescale、
`unconnected_drive、`nounconnected_drive、`ulib、$display、$fdisplay、$finish、$fwrite、$monitor、$random、$stop、$strobe、$time、$write、$clog2(仅SystemVerilog⽀持)、$rtoi、$itor、all others。
介绍其中⼏个⾮常常⽤的系统任务:
$signed和$unsigned可以强制规定输⼊数据为带符号数或⽆符号数,并作为返回值,不⽤管之前的符号。
$readmemb和$readmemh可以⽤于初始化块存储器,两者分别⽤2进制和16进制表⽰。如“$readmemb(“ram.data”, ram, 0,
7)”;。
6.Verilog原语
Vivado⽀持上⽂列出的Verilog门级原语,但不⽀持上拉下拉、驱动强度和延迟、原语矩阵这些类型的门级原语。也不⽀持如下转换级原语:cmos、nmos、pmos、rcmos、rnmos、rpmos、rtran、rtranif0、rtranif1、tran、tranif0、tranif1。
实例化门级原语的⽰例如下:
gate_type instance_name (output, inputs); //语法模板
and U1 (out, in1, in2);
bufif1 U2 (triout, data, trienable);
7.⾏为级Verilog
⾏为级Verilog中的变量都申明为整数,数据类型可以是reg(程序块中赋值)、wire(连续赋值)和integer(会被转换为寄存器类型)。所有变量的默认位宽为1bit,称作标量(scalar);定义的N bits位宽变量称作向量(Vector)。reg和wire可以定义为带符号数signed或⽆符号数unsigned。变量的每个bit可以是如下值:1(逻辑1)、0(逻辑0)、x(未知逻辑值)、z(⾼阻)。
reg[3:0]arb_priority;
wire[31:0]arb_request;
wire signed[8:0]arb_signed;
寄存器在定义时可以初始化,初始值是⼀个常数或参数,不能是函数或任务的调⽤。在全局复位或上电时,Vivado综合会将初始化值作为寄存器的输出(作为寄存器的INIT属性值)。⽽且,该初始值与本地复位是相互独⽴的。
//定义时初始化寄存器
reg arb_onebit = 1'b0;
reg [3:0] arb_priority = 4'b1011;
//本地置位/复位
always @(podge clk)
if (rst) arb_onebit <= 1'b0;
Verilog⽀持定义wire和reg的数组,⽀持⼀位数组和⼆维数组,但每次从数组中选择的元素不能超过⼀个,数组也不能作为任务或函数的传递参数。数组的定义⽰例如下:
//有32个元素的数组,每个元素4bits位宽
reg [3:0] mem_array [31:0];
//包含64个8bits位宽元素的数组
wire [7:0] mem_array [63:0];
//包含256*16个8bits位宽wire元素的⼆维数组
wire [63:0] array2 [0:255][0:15];
//包含256*8个64bits位宽reg元素的⼆维数组
reg [63:0] array2 [255:0][7:0];
Vivado⽀持的所有表达式列在下表中:
符号表达式
{}拼接运算符
{{}}复制运算符
+, -, , /, %, *加、减、乘、除、求余、求幂
>, <, >=, <=关系运算
!逻辑取反
&&逻辑与
||逻辑或
==, !=逻辑相等,逻辑不等
===条件相等
!==条件不等
~按位取反
&按位与
|按位或
^按位异或
~^, ^~按位等价(异或⾮)
~&与⾮运算
蛛丝马迹~|或⾮运算
<<, >>左移,右移
<<<,>>>带符号左移,带符号右移
:条件表达式
or, ‘,’事件或(如⽤于敏感列表)
形容美梦的成语
符号表达式
其中“===”和“!==”在综合时与“==”和“!=”功能相同,没有任何差别。但在仿真中,可以⽤来判断变量是否与’x’和’z’是否相等。下表给出常⽤操作符的运算结果,以供查阅。
initial和always是两个程序块,每个块内部组织了⼀些语法声明,⽤begin和end表⽰范围。块内部的语法声明按顺序执⾏。综合时只会处理always块,会忽略initial块。
8.模块module
Verilog中描述组件(component)的⽅法便是模块(module),模块必须申明与实例化。模块申明包括模块名称、电路I/O端⼝列表、定义功能的主体,并以endmodule结束。
每个电路I/O端⼝要有名称、端⼝模式(input、output、inout),如果端⼝是数组类型还要有范围信息。下⾯给出两种模块申明⽅法的⽰例:
//⽅法1,⽼版本Verilog
module example (A, B, O);
input A, B;
output O;
assign O = A & B;
endmodule
//⽅法2,推荐⽤法
module example
(
input A, B,
output O
);
assign O = A & B;
endmodule