基本学习计划
初识verilog
Verilog数据和赋值
Verilog预处理
Verilog建模以及实例
Verilog仿真以及实例
Verilog状态机
UDP
综合不可综合
其他高级功能
Verilog设计技巧
(初学者 如有错误请多批评)
-----------------------------------------------------------------------------------
这次主要关注verilog与c语言的数据类型和赋值的不同之处
来学verilog的设计思想和基本数据类型以及赋值方法
其实有很多新奇的地方 比如向量和数组(memory)以及拼接(合并) inital和always等需要学习的地方
数据类型如何选择?
数组和向量(总线)的认识。
拼接功能{}
阻塞和非阻塞有何区别?
Init块和always块有何区别?
为什么fpga程序执行分并行和顺序执行?
延时。
设计思想
自顶向下,分层次,模块化设计
Verilog HDL 是区分大小写的,即大小写不同的标识符是不同的。
另外Verilog HDL的书写格式是自由的,即一条语句可多行书写;一行可写多个语句。
白空(新行、制表符、空格)没有特殊意义。
如input A;input B;与
input A;
input B;
是一样的。
书写规范建议:
一个语句一行。
采用空四格的table 键进行缩进。
端口类型分为
Input:输入端口
Output:输出端口
inot双向端口
(模块的所有端口都必须进行输入输出的声明)
- 寄存器类型: reg 是具有存储能力的信号
在always 块中被赋值的信号,往往代表触发器,但不一定是触发器。
reg 是最常用的寄存器类型,寄存器类型通常用于对存储单元的描述,如D型触发器、ROM
等。存储器类型的信号当在某种触发机制下分配了一个值,在分配下一个值之时保留原值。但必
须注意的是,reg 类型的变量,不一定是存储单元,如在always 语句中进行描述的必须用reg 类型
的变量。
reg 类型定义语法如下:
reg [msb: lsb] reg1, reg2, . . . r e g N;
msb 和lsb 定义了范围,并且均为常数值表达式。范围定义是可选的;如果没有定义范围,缺
省值为1 位寄存器。例如:
reg [3:0] Sat; // S a t 为4 位寄存器。
reg Cnt; //1 位寄存器。
reg [1:32] Kisp, Pisp, Lisp ;
- 线网类型: wire信号需要前端驱动 自身没有存储能力
用 assign 关键词指定的组合逻辑的信号或连线
线网类型主要有wire 和tri 两种。线网类型用于对结构化器件之间的物理连线的建模。如器件的管脚,内部器件如与门的输出等。以上面的加法器为例,输入信号A,B是由外部器件所驱动,
异或门X1的输出S1是与异或门X2输入脚相连的物理连接线,它由异或门X1所驱动。
由于线网类型代表的是物理连接线,因此它不存贮逻辑值。必须由器件所驱动。通常由assign
进行赋值。如assign A = B ^ C;
当一个wire 类型的信号没有被驱动时,缺省值为Z(高阻)。
信号没有定义数据类型时,缺省为wire 类型。
wire [4:1] Box, Drt;
wire [5:1] Cfg;
wire [6:1] Peg;
wire [8:1] Adt;
线网和寄存器类型的变量可以声明为向量(Vector)(位宽大于 1),如果声明中没有指定位
宽,则默认为标量(位宽为 1)。举例如下
wire a; //标量线网变量,默认
reg [n-1:0]busA,busB; //声明 busA 和 busB 为 n 位宽寄存器变量,
reg [0:n-1]busC; //声明 busC 为 n 位宽的寄存器变量
wire [n-1:0]busA,busB; //声明 busA 和 busB 为 n 位宽信号,
wire [0:n-1]busC; //声明 busC 为 n 位宽的信号
向量通过[high#:low#]或者[low#:high#]说明,方括号中左边数总是代表向量的最高有效
位(Most Significant Bit, MSB),在上面的例子中,向量 busA 的最高有效位为第 n-1 位,向量
busC 的最高有效位为第 0 位。
数组
在 Verilog HDL 中允许声明 reg 以及 wire 类型向量以及标量的数组,对数组的维数没有
限制。 线网数组也可用于连接连接实例的端口, 数组中的每个元素可以作为变量或者向量使
用。举例如下
reg count[0:7] //由 8 个变量组成的数组;
reg bool[31:0] //由 32 个寄存器标量组成的数组,数组的每个元素为 1 位宽的寄存器类
//型的变量
reg [4:0]port_id[0:7] //由 8 个 5 位宽的向量组成的数组,数组的每个元素为 5 位宽变量
wire [7:0]w_array[5:0] //由 8 位寄存器向量组成数组,数组共有 6 个元素;
注意不要混淆数组和向量两个概念。下面的例子显示如何访问数组元素
count[5]=0 //把 count 数组的第 5 个元素清零;
w_array[2]=8’h01;
Integer 32位整型数据
可用十进制和基数表示法来表示
十进制数32
-15 十进制数-15
基数表示法注意补位和截断原则
[size ] 'base value
size 定义以位计的常量的位长;base 为o 或O(表示八进制),b 或B(表示二进制),d 或D
(表示十进制),h 或H (表示十六进制)之一;value 是基于base 的值的数字序列。值x 和z 以及
十六进制中的a 到f 不区分大小写。
5 'O37 5 位八进制数(二进制11111 )
4'D2 4 位十进制数 (二进制0011)
4 'B1x_01 4 位二进制数
7'Hx 7位x(扩展的x), 即xxxxxxx
4 'hZ 4 位z(扩展的z) , 即zzzz
4'd-4 非法:数值不能为负
8 'h 2A 在位长和字符之间,以及基数和数值之间允许出现空格
3' b 001 非法:` 和基数b 之间不允许出现空格
(2+3)'b10 非法:位长不能够为表达式
注意,x (或z )在十六进制值中代表4 位x(或z ),在八进制中代表3 位x(或z ),在二进制中代表1 位x (或z )。
如果定义的长度比为常量指定的长度长,通常在左边填0 补位。但是如果数最左边一位为x 或
z ,就相应地用x 或z 在左边补位。例如:
10'b10 左边添0 占位, 0000000010
10'bx0x1 左边添x 占位, x x x x x x x 0 x 1
如果长度定义得更小,那么最左边的位相应地被截断。例如:
3 ' b1001 _ 0011 与3'b011 相等
5'H0FFF 与5'H1F 相等
每次写verilog代码时都会考虑把一个变量是设置为wire类型还是reg类型,因此把网上找到的一些关于这方面的资料整理了一下,方便以后查找。
wire表示直通,即只要输入有变化,输出马上无条件地反映;reg表示一定要有触发,输出才会反映输入。
不指定就默认为1位wire类型。专门指定出wire类型,可能是多位或为使程序易读。
wire只能被assign连续赋值,reg只能在initial和always中赋值。
wire使用在连续赋值语句中,而reg使用在过程赋值语句中。
在连续赋值语句中,表达式右侧的计算结果可以立即更新表达式的左侧。
在理解上,相当于一个逻辑之后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线就对应于wire。
在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量可以声明成reg类型的。
根据触发条件的不同,过程赋值语句可以建模不同的硬件结构:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;
如果这个条件是某一信号的高电平或低电平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。
输入端口可以由wire/reg驱动,但输入端口只能是wire;输出端口可以使wire/reg类型,输出端口只能驱动wire;若输出端口在过程块中赋值则为reg型,若在过程块外赋值则为net型。用关键词inout声明一个双向端口, inout端口不能声明为reg类型,只能是wire类型。
默认信号是wire类型,reg类型要申明。这里所说的默认是指输出信号申明成output时为wire。如果是模块内部信号,必须申明成wire或者reg.
对于always语句而言,赋值要申明成reg。连续赋值assign的时候要用wire。
字符串型
字符串是双引号内的字符序列。字符串不能分成多行书写。例如:
"INTERNAL ERROR"
" REACHED->HERE "
用8 位ASCII 值表示的字符可看作是无符号整数。因此字符串是8 位ASCII 值的序列。为存储
字符串“INTERNAL ERROR ”,变量需要8 * 1 4 位。
r e g [1: 8*14] Message;
. . .
Message = "INTERNAL ERROR"
二,赋值与运算
赋值assign和always initial task function等 又有阻塞和非阻塞之分
运算要注意运算中的位宽和是否有符号
语句要注意执行过程和时延
另外学会巧用拼接功能{}
1) 算术操作符
2) 关系操作符
3) 相等操作符
4) 逻辑操作符
5) 按位操作符
6) 归约操作符
7) 移位操作符
8) 条件操作符
9) 连接和复制操作符
在常用的算术运算符主要是 :
加法(二元运算符):“+”;
减法 (二元运算符):“-”;
乘法(二元运算符):“*”;
非阻塞(non-blocking) 赋值语句 ( b<= a):
- 块内的赋值语句同时赋值;
- b 的值被赋成新值 a 的操作, 是与块内其他
赋值语句同时完成的;
- 建议在可综合风格的模块中使用不阻塞赋值。
阻塞(blocking) 赋值语句 ( b = a):
- 完成该赋值语句后才能做下一句的操作;
- b 的值立刻被赋成新值 a;
- 时序电路中硬件没有对应的电路,因而综合结果未知。
连接运算符
连接操作是将小表达式合并形成大表达式的操作。形式如下:
{expr1, expr2, . . .,exprN}
实例如下所示:
wire [7:0] Dbus;
assign Dbus [7:4] = {Dbus [0], Dbus [1], Dbus[2], Dbus[ 3 ]};
//以反转的顺序将低端4 位赋给高端4 位。
assign Dbus = {Dbus [3:0], Dbus [7:4]};
//高4 位与低4 位交换。
由于非定长常数的长度未知, 不允许连接非定长常数。例如,下列式子非法:
{Dbus,5} //不允许连接操作非定长常数。
在Verilog 模块中有三种方法可以生成逻辑电路:
- 用 assign 语句:
assign cs = ( a0 & ~a1 & ~a2 ) ;
- 用 元件的实例调用:
and2 and_inst ( q, a, b);
- 用 always 块:
always @ (posedge clk or posedge clr)
begin if (clr) q<= 0; else if (en) q<= d;
End
如在模块中逻辑功能由下面三个语句块组成 :
assign cs = ( a0 & ~a1 & ~a2 ) ; // -----1
and2 and_inst ( qout, a, b); // -----2
always @ (posedge clk or posedge clr) //-----3
begin if (clr) q<= 0; else if (en) q<= d;
end
三条语句是并行的,它们产生独立的逻辑电路;
而在 always 块中: begin 与 end 之间是顺序执行的。
数据流的描述是采用连续赋值语句(assign )语句来实现的。语法如下:
assign net_type = 表达式;
连续赋值语句用于组合逻辑的建模。 等式左边是wire 类型的变量。等式右边可以是常量、由
运算符如逻辑运算符、算术运算符参与的表达。如下几个实例:
wire [3:0] Z, Preset, Clear; //线网说明
assign Z = Preset & Clear; //连续赋值语句
wire Cout, C i n ;
wire [3:0] Sum, A, B;
. . .
assign {Cout, Sum} = A + B + Cin;
assign Mux = (S = = 3)? D : 'bz;
注意如下几个方面:
1、连续赋值语句的执行是:只要右边表达式任一个变量有变化,表达式立即被计算,计算的
结果立即赋给左边信号。
2、连续赋值语句之间是并行语句,因此与位置顺序无关。
语句块块提供将两条或更多条语句组合成语法结构上相当于一条语句的机制。这里主要讲
Verilog HDL 的 顺序语句块(begin . . . end) :语句块中的语句按给定次序顺序执行。
顺序语句块中的语句按顺序方式执行。每条语句中的时延值与其前面的语句执行的模拟时间
相关。一旦顺序语句块执行结束,跟随顺序语句块过程的下一条语句继续执行。顺序语句块的语
法如下:
begin
[ :block_id{declarations} ]
procedural_statement ( s )
end
initial 语句
initial 语句只执行一次,即在设计被开始模拟执行时开始(0时刻)。通常只用在对设计进行
仿真的测试文件中,用于对一些信号进行初始化和产生特定的信号波形。
在测试块中常用到fork…join块。用并行块能表示以同一个时间起点算起的多个事
件的运行,并行地执行复杂的过程结构,如循环或任务。举例说明如下:
module inline_tb;
reg [7:0] data_bus;
initial fork
data_bus= 8’b00;
#10 data_bus = 8’h45;
//这两个repeat开始执行时间不同,但能同时运行
#20 repeat (10) #10 data_bus = data_bus +1;
#25 repeat (5) # 20 data_bus = data_bus <<1;
#140 data_bua = 8’h0f;
join
endmodule
always 语句与initial 语句相反,是被重复执行,执行机制是通过对一个称为敏感变量表的事件
驱动来实现的,下面会具体讲到。always 语句可实现组合逻辑或时序逻辑的建模。
例[1]:
initial
Clk = 0 ;
always
#5 Clk = ~Clk;
因为always 语句是重复执行的,因此,Clk 是初始值为0 的,周期为10 的方波。
用户1861799 2015-11-17 22:08
用户1318772 2015-2-9 10:50
用户593939 2015-2-8 22:10
学习和参考了
用户593939 2015-2-8 22:09
学习和参考了
用户593939 2015-2-8 22:09
sunyzz 2015-1-29 21:09
风来 2015-1-29 19:41
用户421074 2013-9-13 14:24
学习中,资源很好。
用户312471 2013-9-5 22:05
用户1057926 2013-8-24 08:21