文化网站建设方案,深圳十大建筑工程公司排行榜,维护网页,自适应企业架构文章目录 1.1 第一个verilog设计1.2 Verilog 简介1.3 Verilog环境搭建1.4 Verilog 设计方法设计方法设计流程 2.1 Verilog 基础语法格式注释标识符与关键字 2.2 Verilog 数值表示数值种类整数数值表示方法实数表示方法十进制#xff1a;科学计数法#xff1a; 字符串表示方法… 文章目录 1.1 第一个verilog设计1.2 Verilog 简介1.3 Verilog环境搭建1.4 Verilog 设计方法设计方法设计流程 2.1 Verilog 基础语法格式注释标识符与关键字 2.2 Verilog 数值表示数值种类整数数值表示方法实数表示方法十进制科学计数法 字符串表示方法 2.3 Verilog 数据类型线网wire寄存器reg向量整数实数时间寄存器变量整数integer实数real时间time 数组存储器参数字符串 2.4 Verilog 表达式表达式操作数操作符算术操作符关系操作符等价操作符逻辑操作符按位操作符归约操作符移位操作符拼接操作符条件操作符 2.5 Verilog 编译指令define, undefincludetimescaledefault_nettyperesetallcelldefine, endcelldefineunconnected_drive, nounconnected_drive 最近需要从原型验证切到EDA去做做于是先温习一下Verilog。 在开始之前先来一个Verilog设计。
1.1 第一个verilog设计
4 位宽 10 进制计数器
module counter10(//端口定义input rstn, //复位端低有效input clk, //输入时钟output [3:0] cnt, //计数输出output cout); //溢出位reg [3:0] cnt_temp ; //计数器寄存器always(posedge clk or negedge rstn) beginif(! rstn)begin //复位时计时归0cnt_temp 4b0 ;endelse if (cnt_temp4d9) begin //计时10个cycle时计时归0cnt_temp 4b000;endelse begin //计时加1cnt_temp cnt_temp 1b1 ; endendassign cout (cnt_temp4d9) ; //输出周期位assign cnt cnt_temp ; //输出实时计时器endmodule1.2 Verilog 简介
不展开占用篇幅了。
感兴趣—Verilog 简介
1.3 Verilog环境搭建
因为只是想学习一下Verilog的基本语法因此先不涉及。
感兴趣—Verilog环境搭建
1.4 Verilog 设计方法
设计方法
核心点自顶向下 于是就诞生了单元模块和集成 设计流程 2.1 Verilog 基础语法
格式
区分大小写格式自由可以在一行内编写也可跨多行编写。每个语句必须以分号为结束符。空白符换行、制表、空格都没有实际的意义在编译阶段可忽略。 关键点还是要注意分行提升代码的可读性。和软件一样的。 wire [1:0] results ;assign results (a 1b0) ? 2b01 (b1b0) ? 2b10 2b11 ;wire [1:0] results ;
assign results (a 1b0) ? 2b01 (b1b0) ? 2b10 2b11 ;很明显下面的好看些。
注释
用 // 进行单行注释用 /* 与 / 进行跨行注释用 / 与 */ 进行跨行注释
标识符与关键字 标识符的第一个字符必须是字母或者下划线不能以数字或者美元符开始。 标识符是区分大小写的。 关键字是 Verilog 中预留的用于定义语言结构的特殊标识符。 Verilog 中关键字全部为小写。
reg [3:0] counter ; //reg 为关键字 counter 为标识符
input clk; //input 为关键字clk 为标识符
input CLK; //CLK 与 clk是 2 个不同的标识符2.2 Verilog 数值表示
数值种类 四种基本的值 硬件电路中的电平逻辑 整数数值表示方法
数字声明时合法的基数格式有 4 中
十进制(d 或 D)十六进制(h 或 H)二进制b 或 B八进制o 或 O。
数值可指明位宽也可不指明位宽。
4b1011 // 4bit 数值
32h3022_c0de // 32bit 的数值_ 是为了增强代码的可读性。 不指明位宽:
一般直接写数字时默认为十进制表示例如下面的 3 种写法是等效的
counter d100 ; //一般会根据编译器自动分频位宽常见的为32bit
counter 100 ;
counter 32h64 ;负数表示
-6d15
-15-15 在 5 位二进制中的形式为 5’b10001,在 6 位二进制中的形式为 6’b11_0001。
需要注意的是减号放在基数和数字之间是非法的例如下面的表示方法是错误的
4d-2 //非法说明实数表示方法
实数表示方法主要有两种方式
十进制
30.123
6.0
3.0
0.001科学计数法
1.2e4 //大小为12000
1_0001e4 //大小为100010000
1E-3 //大小为0.001字符串表示方法
字符串是由双引号包起来的字符队列。
字符串不能多行书写即字符串中不能包含回车符。
reg [0: 14*8-1] str ;
initial beginstr www.runoob.com;
end2.3 Verilog 数据类型
线网wire寄存器reg
线网wire
wire 类型表示硬件单元之间的物理连线由其连接的器件输出端连续驱动。如果没有驱动元件连接到 wire 型变量缺省值一般为 “Z”。高阻态举例如下
wire interrupt ;
wire flag1, flag2 ;
wire gnd 1b0 ;线网型还有其他数据类型包括 wandworwritriandtriortrireg 等不常用。 寄存器reg
寄存器reg用来表示存储单元它会保持数据原有的值直到被改写。
reg clk_temp;
reg flag1, flag2 ;例如在 always 块中寄存器可能被综合成边沿触发器在组合逻辑中可能被综合成 wire 型变量。寄存器不需要驱动源也不一定需要时钟信号。在仿真时寄存器的值可在任意时刻通过赋值操作进行改写。例如 这就是为什么逻辑设计仿真的重要性。 reg rstn ;
initial beginrstn 1b0 ;#100 ;rstn 1b1 ;
end向量
当位宽大于 1 时wire 或 reg 即可声明为向量的形式。例如
reg [3:0] counter ; //声明4bit位宽的寄存器counter
wire [32-1:0] gpio_data; //声明32bit位宽的线型变量gpio_data
wire [8:2] addr ; //声明7bit位宽的线型变量addr位宽范围为8:2
reg [0:31] data ; //声明32bit位宽的寄存器变量data, 最高有效位为0可以指定某一位或若干相邻位
wire [9:0] data_low data[0:9] ;
addr_temp[3:2] addr[8:7] 1b1 ;Verilog 支持可变的向量域选择
reg [31:0] data1 ;
reg [7:0] byte1 [3:0];
integer j ;
always* beginfor (j0; j3;jj1) beginbyte1[j] data1[(j1)*8-1 : j*8]; //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]end
endVerillog 还支持指定 bit 位后固定位宽的向量域选择访问。 //下面 2 种赋值是等效的
A data1[31-: 8] ;
A data1[31:24] ;//下面 2 种赋值是等效的
B data1[0 : 8] ;
B data1[0:7] ;对信号重新进行组合成新的向量时需要借助大括号 wire [31:0] temp1, temp2 ;
assign temp1 {byte1[0][7:0], data1[31:8]}; //数据拼接
assign temp2 {32{1b0}}; //赋值32位的数值0整数实数时间寄存器变量 整数实数时间等数据类型实际也属于寄存器类型。 整数integer
整数类型用关键字 integer 来声明。声明时不用指明位宽位宽和编译器有关一般为32 bit。
reg 型变量为无符号数而 integer 型变量为有符号数。
reg [31:0] data1 ;
reg [3:0] byte1 [7:0]; //数组变量后续介绍
integer j ; //整型变量用来辅助生成数字电路
always* beginfor (j0; j3;jj1) beginbyte1[j] data1[(j1)*8-1 : j*8]; //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]end
endinteger 信号 j 作为辅助信号将 data1 的数据依次赋值给数组 byte1。综合后实际电路里并没有 j 这个信号j 只是辅助生成相应的硬件电路。 实数real
实数用关键字 real 来声明可用十进制或科学计数法来表示。
real data1 ;
integer temp ;
initial begindata1 2e3 ;data1 3.75 ;
endinitial begintemp data1 ; //temp 值的大小为3
end时间time
Verilog 使用特殊的时间寄存器 time 型变量对仿真时间进行保存。
其宽度一般为 64 bit通过调用系统函数 $time 获取当前仿真时间。
time current_time ;
initial begin#100 ;current_time $time ; //current_time 的大小为 100
end数组
在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组。
数组维数没有限制。
线网数组也可以用于连接实例模块的端口。
数组中的每个元素都可以作为一个标量或者向量以同样的方式来使用形如数组名[下标]。
对于多维数组来讲用户需要说明其每一维的索引。
例如
integer flag [7:0] ; //8个整数组成的数组
reg [3:0] counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0] addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组下面显示了对数组元素的赋值操作
flag [1] 32d0 ; //将flag数组中第二个元素赋值为32bit的0值
counter[3] 4hF ; //将数组counter中第4个元素的值赋值为4bit 十六进制数F等效于counter[3][3:0] 4hF即可省略宽度;
assign addr_bus[0] 8b0 ; //将数组addr_bus中第一个元素的值赋值为0
assign data_bit[0][1] 1b1; //将数组data_bit的第1行第2列的元素赋值为1这里不能省略第二个访问标号即 assign data_bit[0] 1b1; 是非法的。
data_4d[0][0][0][0][15:0] 15d3 ; //将数组data_4d中标号为[0][0][0][0]的寄存器单元的15~0bit赋值为3虽然数组与向量的访问方式在一定程度上类似但不要将向量和数组混淆。
向量是一个单独的元件位宽为 n
数组由多个元件组成其中每个元件的位宽为 n 或 1。
它们在结构的定义上就有所区别。
存储器
存储器变量就是一种寄存器数组可用来描述 RAM 或 ROM 的行为。例如
reg membit[0:255] ; //256bit的1bit存储器
reg [7:0] mem[0:1023] ; //1Kbyte存储器位宽8bit
mem[511] 8b0 ; //令第512个8bit的存储单元值为0参数
参数用来表示常量用关键字 parameter 声明只能赋值一次。例如
parameter data_width 10d32 ;
parameter i1, j2, k3 ;
parameter mem_size data_width * 10 ;但是通过实例化的方式可以更改参数在模块中的值。 局部参数用 localparam 来声明其作用和用法与 parameter 相同区别在于它的值不能被改变。所以当参数只在本模块中调用时可用 localparam 来说明。 字符串
字符串保存在 reg 类型的变量中每个字符占用一个字节8bit。因此寄存器变量的宽度应该足够大以保证不会溢出。
字符串不能多行书写即字符串中不能包含回车符。如果寄存器变量的宽度大于字符串的大小则使用 0 来填充左边的空余位如果寄存器变量的宽度小于字符串大小则会截去字符串左边多余的数据。
例如为存储字符串 “run.runoob.com”, 需要 14*8bit 的存储单元
reg [0: 14*8-1] str ;
initial beginstr run.runoob.com;
end有一些特殊字符在显示字符串中有特殊意义例如换行符制表符等。如果需要在字符串中显示这些特殊的字符则需要在前面加前缀转义字符 \ 。例如下表所示 其实在 SystemVerilog主要用于 Verilog 仿真的编程语言语言中已经可以直接用关键字 string 来表示字符串变量类型。
本来可以直接学习SystemVerilog但是有位前辈给我说建议先学学Verilog以后对比学习更有好处。
2.4 Verilog 表达式 操作符和很多编码语言类似这里只详细罗列两种独特的按位与归约。 表达式
操作数
操作符
算术操作符
关系操作符
等价操作符
逻辑操作符
按位操作符 按位操作符包括取反与或|异或^同或^。 按位操作符对 2 个操作数的每 1bit 数据进行按位操作。 如果 2 个操作数位宽不相等则用 0 向左扩展补充较短的操作数。 取反操作符只有一个操作数它对操作数的每 1bit 数据进行取反操作。 A 4b0101 ;
B 4b1001 ;
C 4bx010 ;~A //4b1010
A B //4b0001
A | B //4b1101
A^B //4b1100
A ~^ B //4b0011
B | C //4b1011
BC //4bx000归约操作符
归约操作符包括归约与归约与非归约或|归约或非|归约异或归约同或~。
归约操作符只有一个操作数它对这个向量操作数逐位进行操作最终产生一个 1bit 结果。
逻辑操作符、按位操作符和归约操作符都使用相同的符号表示因此有时候容易混淆。区分这些操作符的关键是分清操作数的数目和计算结果的规则。
A 4b1010 ;
A ; //结果为 1 0 1 0 1b0可用来判断变量A是否全1
~|A ; //结果为 ~(1 | 0 | 1 | 0) 1b0, 可用来判断变量A是否为全0
^A ; //结果为 1 ^ 0 ^ 1 ^ 0 1b0
移位操作符
拼接操作符
拼接操作符用大括号 {} 来表示用于将多个操作数向量拼接成新的操作数向量信号间用逗号隔开。
拼接符操作数必须指定位宽常数的话也需要指定位宽。例如
A 4b1010 ;
B 1b1 ;
Y1 {B, A[3:2], A[0], 4h3 }; //结果为Y1b1100_0011
Y2 {4{B}, 3d4}; //结果为 Y27b111_1100
Y3 {32{1b0}}; //结果为 Y332h0常用作寄存器初始化时匹配位宽的赋初值条件操作符
2.5 Verilog 编译指令
以反引号 开始的某些标识符是 Verilog 系统编译指令。
编译指令为 Verilog 代码的撰写、编译、调试等提供了极大的便利。
下面介绍下完整的 8 种编译指令其中前 4 种使用频率较高。
define, undef
在编译阶段define 用于文本替换类似于 C 语言中的 #define。
一旦 define 指令被编译其在整个编译过程中都会有效。例如在一个文件中定义
define DATA_DW 32则在另一个文件中也可以直接使用 DATA_DW。
define S $stop;
//用S来代替系统函数$stop; (包括分号)
define WORD_DEF reg [31:0]
//可以用WORD_DEF来声明32bit寄存器变量undef 用来取消之前的宏定义例如
define DATA_DW 32
……
reg [DATA_DW-1:0] data_in ;
……
undef DATA_DWifdef, ifndef, elsif, else, endif这些属于条件编译指令。例如下面的例子中如果定义了 MCU51则使用第一种参数说明如果没有定义 MCU、定义了 WINDOW则使用第二种参数说明如果 2 个都没有定义则使用第三种参数说明。
ifdef MCU51parameter DATA_DW 8 ;
elsif WINDOWparameter DATA_DW 64 ;
elseparameter DATA_DW 32 ;
endifelsif, else 编译指令对于 ifdef 指令是可选的即可以只有 ifdef 和 endif 组成一次条件编译指令块。
当然也可用 ifndef 来设置条件编译表示如果没有相关的宏定义则执行相关语句。
下面例子中如果定义了 WINDOW则使用第二种参数说明。如果没有定义 WINDOW则使用第一种参数说明。 ifndef WINDOWparameter DATA_DW 32 ; elseparameter DATA_DW 64 ;endifinclude
使用 include 可以在编译时将一个 Verilog 文件内嵌到另一个 Verilog 文件中作用类似于 C 语言中的 #include 结构。
该指令通常用于将全局或公用的头文件包含在设计文件里。
文件路径既可以使用相对路径也可以使用绝对路径。
include ../../param.v
include header.v
timescale
在 Verilog 模型中时延有具体的单位时间表述并用 timescale 编译指令将时间单位与实际时间相关联。
该指令用于定义时延、仿真的单位和精度格式为
timescale time_unit / time_precisiontime_unit 表示时间单位time_precision 表示时间精度它们均是由数字以及单位 s秒ms毫秒us微妙ns纳秒ps皮秒和 fs飞秒组成。时间精度可以和时间单位一样但是时间精度大小不能超过时间单位大小例如下面例子中输出端 Z 会延迟 5.21ns 输出 AB 的结果。 timescale 1ns/100ps //时间单位为1ns精度为100ps合法
//timescale 100ps/1ns //不合法
module AndFunc(Z, A, B);output Z;input A, B ;assign #5.207 Z A B
endmodule在编译过程中timescale 指令会影响后面所有模块中的时延值直至遇到另一个 timescale 指令或 resetall 指令。
由于在 Verilog 中没有默认的 timescale如果没有指定 timescaleVerilog 模块就有会继承前面编译模块的 timescale 参数。有可能导致设计出错。
如果一个设计中的多个模块都带有 timescale 时模拟器总是定位在所有模块的最小时延精度上并且所有时延都相应地换算为最小时延精度时延单位并不受影响。例如:
timescale 10ns/1ns
module test;reg A, B ;wire OUTZ ;initial beginA 1;B 0;# 1.28 B 1;# 3.1 A 0;endAndFunc u_and(OUTZ, A, B) ;
endmodule在模块 AndFunc 中5.207 对应 5.21ns。 在模块 test 中1.28 对应 13ns3.1 对应 31ns。
但是当仿真 test 时由于 AndFunc 中的最小精度为 100ps因此 test 中的时延精度将进行重新调整。
13ns 将对应 130100ps31ns 将对应 310100ps。仿真时时延精度也会使用 100ps。仿真时间单位大小没有影响。
如果有并行子模块子模块间的 timescale 并不会相互影响。
例如在模块 test 中再例化一个子模块 OrFunc。仿真 test 时OrFunc 中的 #5.207 延时依然对应 52ns。
//子模块
timescale 10ns/1ns //时间单位为1ns精度为100ps合法
module OrFunc(Z, A, B);output Z;input A, B ;assign #5.207 Z A | B
endmodule//顶层模块
timescale 10ns/1ns
module test;reg A, B ;wire OUTZ ;wire OUTX ;initial beginA 1;B 0;# 1.28 B 1;# 3.1 A 0;endAndFunc u_and(OUTZ, A, B) ;OrFunc u_and(OUTX, A, B) ;endmodule此例中仿真 test 时OrFunc 中的 #5.207 延时依然对应 52ns。
timescale 的时间精度设置是会影响仿真时间的。时间精度越小仿真时占用内存越多实际使用的仿真时间就越长。
所以如果没有必要应尽量将时间精度设置的大一些。
default_nettype
该指令用于为隐式的线网变量指定为线网类型即将没有被声明的连线定义为线网类型。
default_nettype wand 该实例定义的缺省的线网为线与类型。因此如果在此指令后面的任何模块中的连线没有说明那么该线网被假定为线与类型。
default_nettype none该实例定义后将不再自动产生 wire 型变量。
例如下面第一种写法编译时不会报 Error第二种写法编译将不会通过。
//Z1 无定义就使用系统默认Z1为wire型变量有 Warning 无 Error
module test_and(input A,input B,output Z);assign Z1 A B ;
endmodule//Z1无定义就使用由于编译指令的存在系统会报Error从而检查出书写错误
default_nettype none
module test_and(input A,input B,output Z);assign Z1 A B ;
endmoduleresetall
该编译器指令将所有的编译指令重新设置为缺省值。
resetall 可以使得缺省连线类型为线网类型。
当 resetall 加到模块最后时可以将当前的 timescale 取消防止进一步传递只保证当前的 timescale 在局部有效避免 timescale 的错误继承。
celldefine, endcelldefine
这两个程序指令用于将模块标记为单元模块他们包含模块的定义。
例如一些与、或、非门一些 PLL 单元PAD 模型以及一些 Analog IP 等。
celldefine
module (input clk,input rst,output clk_pll,output flag);……
endmodule
endcelldefineunconnected_drive, nounconnected_drive
在模块实例化中出现在这两个编译指令间的任何未连接的输入端口为正偏电路状态或者为反偏电路状态。 正偏电路状态指的是在PN结上施加正向电压即P区接正电位N区接负电位。在这种状态下PN结处于正向偏置电流可以通过PN结流动使得PN结处于导通状态。在电子器件中如二极管或三极管当它们的PN结处于正偏状态时它们可以允许电流通过。 反偏电路状态则是指在PN结上施加反向电压即P区接负电位N区接正电位。在这种状态下PN结处于反向偏置电流几乎被完全截止PN结处于不导通状态。这种状态下电子器件如二极管或三极管是关闭的不允许电流通过。 正偏电路状态使得PN结导通允许电流通过而反偏电路状态则使得PN结截止阻止电流通过。 unconnected_drive pull1
. . ./ *在这两个程序指令间的所有未连接的输入端口为正偏电路状态连接到高电平 * /
nounconnected_driveunconnected_drive pull0
. . ./ *在这两个程序指令间的所有未连接的输入端口为反偏电路状态连接到低电平 * /
nounconnected_drive 参考资料【菜鸟教程】