FPGA学习笔记3-verilog HDL
2012-01-19 14:48
337 查看
Verilog HDL基础
不是软件编程语言,是一种可以硬件仿真的硬件描述语言
常用术语
HDL-Hardware Description Language
RTL-Register Transfer Level
行为建模-由输入输出关系描述的组件
结构化建模-由底层互联组成和原语描述的组件
综合-将HDL译成电路,然后对表征电路进行优化
模块-是verilog HDL中基本设计单元
行为建模
只有电路功能,没有结构
没有专门的硬件目标
由于综合以及仿真目的
可面向任意技术的通用代码
结构化建模
电路功能和结构
要求明确的硬件实现
处于综合以及仿真目的
可以是通用或者器件专用,也可以同时兼用
RTL综合
语句-译成-优化
模块结构
Verilog基本模型结构
moudle moudle_name(port_list);
端口声明
数据类型声明
电路功能
时序规范
end moudle
case敏感
所有关键字为小写
空白用于提高可读性
分好是声明结束符
单行注释://
多好注释:/* */
时序规范用于仿真
端口
端口列表
-端口名称列表,例如:moudle mult_acc(out,ina,inb,clk,clr)
端口类型
-input(输入端口)
-output(输出端口)
-inout(双向端口)
端口声明
-<port_type><port_name>
-例如:input[7:0] ina,inb;
input clk,clr;
output[15:0] out;
数据类型
网数据类型(NET)-表示进程之间的物理连接
寄存器类型-表示暂时存储数据的变量,并不一定表示硬件中寄存器
NET数据类型
wire-表示一个节点或者连接
tri-表示一个三态节点
supply0,逻辑0,
supply1,逻辑1
寄存器数据类型
寄存器可以是:整数,实数,时间,实时
只能在进程声明,任务或者功能中赋值reg类型变量
不能是逻辑门输出,或者是assign语句的输出
输入输出规则表
赋值-数字
sized或者Unsized
<size>'<base format><number>
size例子:3'b010 = 3位宽二进制数字,前缀3表示数字位宽
unsize例子:123 = 默认的32位宽十进制数字
PS.默认-没有指定的<base format>,默认为decimal,没有指定的<size>,默认为32位宽数字
基本格式
-十进制 ('d或者'D)16'd255 = 16位宽十进制数字
-十六进制 ('h或者'H)8'h9a = 8位十六进制数字
-二进制 ('b或者'B)'b1010 = 32位宽二进制数字
-八进制 ('o或者'O)'o21 = 32位宽八进制数字
数字
负数-在<size>前加上负号表示
-合法:-8'd3 = 8位负数,存储为2'的补码3
-非法:4'd-2 = Error!
特殊数字字符
-'_'(下划线):用于可读性
例子:32'h21_65_bc_fe = 32位16进制数字
-'x'或者'X'(未知值)
例子:12'h12x = 12位16进制数字:LSB位置
-'z'或者'Z'(高阻抗值)
例子:1'bz = 一位高阻抗数字
算子
1.把矢量整体作为一个数值
2.如果任一操作数为Z或者X,那么结果未知。例:ain + din = unknow
3.如果结果和操作数大小相同,则进位丢失
4.负数以2的补码格式存储,但是当用在表达式中时,解释为无符号值
位算子
1.对操作数的每一位进行运算
2.结果是最大操作数的大小
3.如果位宽不同,以零向左扩展
缩位算子
1.将矢量减为单比特
2.X或者Z被认为未知,但是结果可能是确定值,例:&din = 1'b0
关系算子
1.用于对比数值
2.返回一位标量值,布尔真(1)/假(0)
3.如果任一操作数是Z或者X,那么结果未知
相等算子
1.用于对比数值
2.返回一位标量值,布尔真(1)/假(0)
3.如果任一操作数是Z或者X,那么结果未知
4.条件相等和不等包括X和Z
PS:相等只支持已知的数值,不支持z,x,条件相等支持所有的数值,1,0,x,z
逻辑算子
1.返回一位标量值,布尔真(1)/假(0)
2.如果任一操作数是Z或者X,那么结果未知
移位算子
1.矢量向左或者向右移动一定比特
2.填充零
3.移出比特丢失
其他算子
算子优先级
算子默认优先级
+,-,!,~(unary)
+,-(Binary)
<<,>>
<,>,<=,>=
==,!=
&
^,^~ or ~^
|
&&
||
?:(ternary)
()可以优于默认优先级
赋值语句
连续赋值声明
使用算子,对组合逻辑进行建模
wire adder_out = mult_out + out
等价于
wire adder_out
assign adder_out = mult_out + out
assign #5 adder_out = mult_out + out 延迟5个tip
1.左手侧(LHS)必须是net数据类型
2.保持主动:当一个右手侧(RHS)操作数变化时,评估表达式,立即更新LHSnet
3.RHS可以是net,寄存器或者函数调用
4.延迟值可以赋值给模型逻辑门延迟
进程赋值模块
inital模块-用于初始化仿真的行为声明(被综合器忽略)
always模块-使用行为声明,用于描述电路功能
1.每个always和inital模块代表不同的进程
2.进程并行运行,在仿真时间0开始
3.而进程中的声明顺序执行
4.always和inital模块不能嵌套
inital模块
由行为声明构成
用于初始化,监视,波形和其他进程,在仿真中只能执行一次
-一个inital模块在时间0启动,在仿真中只执行一次,然后不再执行
-inital模块中的行为声明顺序执行
-因此,声明的顺序并不重要
综合不支持(例如,被综合器忽略)
always模块
由行为声明构成
如果有多个always模块,同时执行每个模块
用于对数字电路中不断重复的进程进行建模
-一个always模块在时间0启动,以循环的仿真连续执行行为声明
-初始模块中的行为声明顺序执行
-因此,声明的顺序并不重要
-如果有多个声明,必须使用关键词begin和end组合在一起
两类进程赋值
阻塞赋值(=):按顺序模块中指定的次序执行
非阻塞赋值(<=):不对顺序模块后的声明进行阻塞赋值,支持对赋值的调度,时间步长的最后进行
驻留在进程模块中
左手侧,更新reg,integer,real,time或者realtime变量值
PS:实际上感觉非阻塞赋值即是不考虑赋值顺序中其上存在延迟赋值,即所有的赋值延迟参考点为赋值段开始的时候,
而阻塞赋值则是为上一个赋值结束为下一个阻塞起始参考点
使用进程模块的电路类型
组合电路-对组合逻辑中使用的所有输入敏感
例子:always @(a or b or sel) 敏感事件列表包括组合逻辑使用的所有输入
时钟电路-对时钟以及控制信号敏感
例子:always @(posedge clk or negedge clr) 敏感事件列表不包含输入,只有时钟以及控制信号
行为声明
-IF-ELSE声明
对条件按照从上到下的顺序进行评估
优先级
-CASE声明
立即对条件进行评估
没有优先级
循环声明
用于重复计算
PS:
必须用于always或者initial模块中
这些行为声明也可以用在时钟进程中
IF-ELSE声明(逐个检查条件)
if (<conitional 1>)
sequence of statement(s)
else
if (<conitional 2>)
sequence of statement(s)
.
.
else
sequence of statement(s)
CASE声明(立即检查条件,不过条件不可有交叠,效率比if高)
case (expression)
<condition 1>:
sequence of statement(s)
<condition 2>:
sequence of statement(s)
.
.
sequence of statement(s)
defalut:
sequence of statement(s)
endcase
两种其他形式的CASE声明
casez
-认为case条件下的所有'z'值不重要,不是逻辑值
-所有'z'值也可以表示为'?'
casex
-认为case条件下所有'x'和'z'值不重要,不是逻辑值
永远和重复循环
forever循环-不断执行
例:
inital
begin
clk = 0;
forever #25 clk = ~clk;
end
repeat循环-执行一定次数
例:
if(rotate == 1)
repeat(8)
begin
tmp = data[15];
data = {data<<1,temp};
end
while循环-如果表达式为真,则执行
例:(从0到100计数,101退出循环)
inital
begin
count = 0;
while(count<101)
begin(count<101)
$display("Count =%d",count);
count = count + 1;
end
end
for循环-循环开始时立即执行一次,如果表达式为真,则继续执行
例:
integer i; //declare the index for the FOR LOOP
always @(inp or cnt)
begin
result[7:4] = 0;
result[3:0] = inp;
if(cnt == 1)
begin
for(i=4;i<=7;i=i+1)
begin
result[i] = result[i-4];
end
result[3:0]=0;
end
end
Verilog HDL函数和任务
-函数和任务是子程序
-用于可重复代码
-增加模块可读性
-函数
--根据输入返回一个值
--产生组合逻辑
--用在表达式中:assign mult_out = mult(ina,inb);
-任务
--与其他语言的程序相似
--可以使组合或者寄存
--以声明形式调用任务:stm_out(nxt,first,sel,filter);
函数定义例:
function [15:0]mult;
//函数名字,也是返回变量
input[7:0] a,b;
reg[15:0] r;
integer i;
begin
if(a[0]==1)
r=b;
else
r=0;
for(i=1;i<=7;i=i+1)
begin
if(a[i]==1)
r=r+(b<<i);
end
mult=r;
end
endfunction
任务实例:
task add; //task definition
input a,b; //two input argument ports
output c; //one output argument port
begin
c=a+b;
end
endtask
函数和任务的不同
PS:实际上感觉函数就是有返回值的,任务就是没有返回值的函数,应该说是没有返回赋值。
函数调用时表达式为
assign mult_out = mult(ina,inb);
ina,inb为输入量,而函数定义中的r作为输出量,返回给mult,然后赋值给mult_out,每次能够只能返回一个变量
任务调用表达式为
add(1,0,p); p声明为reg
这里按照数值出现顺序进行传递,1,0为input,而p作为output输出,这里直接返回给p,没有通过内部中继,可以一次性返回多个变量
函数可以调用另外一个函数,但是不可以调用另外一个任务
而任务可以调用另外一个任务,也可以调用函数
不是软件编程语言,是一种可以硬件仿真的硬件描述语言
常用术语
HDL-Hardware Description Language
RTL-Register Transfer Level
行为建模-由输入输出关系描述的组件
结构化建模-由底层互联组成和原语描述的组件
综合-将HDL译成电路,然后对表征电路进行优化
模块-是verilog HDL中基本设计单元
行为建模
只有电路功能,没有结构
没有专门的硬件目标
由于综合以及仿真目的
可面向任意技术的通用代码
结构化建模
电路功能和结构
要求明确的硬件实现
处于综合以及仿真目的
可以是通用或者器件专用,也可以同时兼用
RTL综合
语句-译成-优化
模块结构
Verilog基本模型结构
moudle moudle_name(port_list);
端口声明
数据类型声明
电路功能
时序规范
end moudle
case敏感
所有关键字为小写
空白用于提高可读性
分好是声明结束符
单行注释://
多好注释:/* */
时序规范用于仿真
端口
端口列表
-端口名称列表,例如:moudle mult_acc(out,ina,inb,clk,clr)
端口类型
-input(输入端口)
-output(输出端口)
-inout(双向端口)
端口声明
-<port_type><port_name>
-例如:input[7:0] ina,inb;
input clk,clr;
output[15:0] out;
数据类型
网数据类型(NET)-表示进程之间的物理连接
寄存器类型-表示暂时存储数据的变量,并不一定表示硬件中寄存器
NET数据类型
wire-表示一个节点或者连接
tri-表示一个三态节点
supply0,逻辑0,
supply1,逻辑1
寄存器数据类型
寄存器可以是:整数,实数,时间,实时
只能在进程声明,任务或者功能中赋值reg类型变量
不能是逻辑门输出,或者是assign语句的输出
输入输出规则表
variable_type | input | output | inout |
net | YES | YES | YES |
register | NO | YES | NO |
sized或者Unsized
<size>'<base format><number>
size例子:3'b010 = 3位宽二进制数字,前缀3表示数字位宽
unsize例子:123 = 默认的32位宽十进制数字
PS.默认-没有指定的<base format>,默认为decimal,没有指定的<size>,默认为32位宽数字
基本格式
-十进制 ('d或者'D)16'd255 = 16位宽十进制数字
-十六进制 ('h或者'H)8'h9a = 8位十六进制数字
-二进制 ('b或者'B)'b1010 = 32位宽二进制数字
-八进制 ('o或者'O)'o21 = 32位宽八进制数字
数字
负数-在<size>前加上负号表示
-合法:-8'd3 = 8位负数,存储为2'的补码3
-非法:4'd-2 = Error!
特殊数字字符
-'_'(下划线):用于可读性
例子:32'h21_65_bc_fe = 32位16进制数字
-'x'或者'X'(未知值)
例子:12'h12x = 12位16进制数字:LSB位置
-'z'或者'Z'(高阻抗值)
例子:1'bz = 一位高阻抗数字
算子
算子符号 | 执行的计算 | 例子(ain=5,bin=10,cin=2'b01,din=2'b0Z) |
+ | 加法 | bin + cin = 11 |
- | 减法,取负 | bin - cin = 9 , -bin = -10 |
* | 乘法 | ain * bin = 50 |
/ | 除法 | bin / ain = 2 |
% | 取模 | bin % ain = 0,PS:取模支持的比较有限 |
2.如果任一操作数为Z或者X,那么结果未知。例:ain + din = unknow
3.如果结果和操作数大小相同,则进位丢失
4.负数以2的补码格式存储,但是当用在表达式中时,解释为无符号值
位算子
算子符号 | 执行的计算 | 例子(ain=3'b101,bin=3'b110,cin=3'b01X) |
~ | 每位取反 | ~ain = 3'b010 |
& | 每位and | ain&bin = 3'b100 , bin&cin = 3'b010 |
| | 每位or | ain|bin = 3'b111 |
^ | 每位Xor | ain^bin = 3'b011 |
^~或者~^ | 每位Xnor | ain^~bin = 3'b100 |
2.结果是最大操作数的大小
3.如果位宽不同,以零向左扩展
缩位算子
算子符号 | 执行的计算 | 例子(ain=5'b10101,bin=4'b0011,cin=3'bZ00,din=3'bX011) |
& | and所有位 | &ain = 1'b0 , &din = 1'b0 |
~& | nand所有位 | ~&ain = 1'b1 |
| | or所有位 | |ain = 1'b1 , |cin = 1'bX |
~| | nor所有位 | ~|ain = 1'b0 |
^ | Xor所有位 | ^ain = 1'b1 |
~^或者^~ | Xnor所有位 | ~^ain = 1'b0 |
2.X或者Z被认为未知,但是结果可能是确定值,例:&din = 1'b0
关系算子
算子符号 | 执行的计算 | 例子(ain=3'b010,bin=3'b100,cin=3'b111,din=3'b01z,ein=3'b01x) |
> | 大于 | ain>bin = 1'b0 |
< | 小于 | ain<bin = 1'b1 |
>= | 大于等于 | ain>=din = 1'bX |
<= | 小于等于 | ain<=ein = 1'bX |
2.返回一位标量值,布尔真(1)/假(0)
3.如果任一操作数是Z或者X,那么结果未知
相等算子
算子符号 | 执行的计算 | 例子(ain=3'b010,bin=3'b100,cin=3'b111,din=3'b01z,ein=3'b01x) |
== | 相等 | ain==cin = 1'b0 |
!= | 不等 | ein!=ein = 1'bX |
=== | 条件相等 | ein===ein = 1'b1 |
!== | 条件不等 | ein!==din = 1'b1 |
2.返回一位标量值,布尔真(1)/假(0)
3.如果任一操作数是Z或者X,那么结果未知
4.条件相等和不等包括X和Z
PS:相等只支持已知的数值,不支持z,x,条件相等支持所有的数值,1,0,x,z
逻辑算子
算子符号 | 执行的计算 | 例子(ain=3'b101,bin=3'b000) |
! | 非真 | !ain = 1'b0 |
&& | 所有表达式真 | ain&&bin = 1'b0 |
|| | 一个或所有都真 | ain||bin = 1'b1 |
2.如果任一操作数是Z或者X,那么结果未知
移位算子
算子符号 | 执行的计算 | 例子(ain=1'b1010,bin=4'b10X0) |
>> | 右移 | bin>>1 = 4'b010X |
<< | 左移 | ain<<2 = 4'b1000 |
2.填充零
3.移出比特丢失
其他算子
算子符号 | 执行的计算 | 例子 |
?: | 条件 | (condition)?true_val:false_val; sig_out = (sel==2'b01)?A:B; |
{} | 连接 | ain=3'b010,bin=4'b1100; {ain,bin} = 7'b0101100; |
{{}} | 复制 | {3{2'b10}} = 6'b101010 |
算子默认优先级
+,-,!,~(unary)
+,-(Binary)
<<,>>
<,>,<=,>=
==,!=
&
^,^~ or ~^
|
&&
||
?:(ternary)
()可以优于默认优先级
赋值语句
连续赋值声明
使用算子,对组合逻辑进行建模
wire adder_out = mult_out + out
等价于
wire adder_out
assign adder_out = mult_out + out
assign #5 adder_out = mult_out + out 延迟5个tip
1.左手侧(LHS)必须是net数据类型
2.保持主动:当一个右手侧(RHS)操作数变化时,评估表达式,立即更新LHSnet
3.RHS可以是net,寄存器或者函数调用
4.延迟值可以赋值给模型逻辑门延迟
进程赋值模块
inital模块-用于初始化仿真的行为声明(被综合器忽略)
always模块-使用行为声明,用于描述电路功能
1.每个always和inital模块代表不同的进程
2.进程并行运行,在仿真时间0开始
3.而进程中的声明顺序执行
4.always和inital模块不能嵌套
inital模块
由行为声明构成
用于初始化,监视,波形和其他进程,在仿真中只能执行一次
-一个inital模块在时间0启动,在仿真中只执行一次,然后不再执行
-inital模块中的行为声明顺序执行
-因此,声明的顺序并不重要
综合不支持(例如,被综合器忽略)
always模块
由行为声明构成
如果有多个always模块,同时执行每个模块
用于对数字电路中不断重复的进程进行建模
-一个always模块在时间0启动,以循环的仿真连续执行行为声明
-初始模块中的行为声明顺序执行
-因此,声明的顺序并不重要
-如果有多个声明,必须使用关键词begin和end组合在一起
两类进程赋值
阻塞赋值(=):按顺序模块中指定的次序执行
非阻塞赋值(<=):不对顺序模块后的声明进行阻塞赋值,支持对赋值的调度,时间步长的最后进行
驻留在进程模块中
左手侧,更新reg,integer,real,time或者realtime变量值
PS:实际上感觉非阻塞赋值即是不考虑赋值顺序中其上存在延迟赋值,即所有的赋值延迟参考点为赋值段开始的时候,
而阻塞赋值则是为上一个赋值结束为下一个阻塞起始参考点
使用进程模块的电路类型
组合电路-对组合逻辑中使用的所有输入敏感
例子:always @(a or b or sel) 敏感事件列表包括组合逻辑使用的所有输入
时钟电路-对时钟以及控制信号敏感
例子:always @(posedge clk or negedge clr) 敏感事件列表不包含输入,只有时钟以及控制信号
行为声明
-IF-ELSE声明
对条件按照从上到下的顺序进行评估
优先级
-CASE声明
立即对条件进行评估
没有优先级
循环声明
用于重复计算
PS:
必须用于always或者initial模块中
这些行为声明也可以用在时钟进程中
IF-ELSE声明(逐个检查条件)
if (<conitional 1>)
sequence of statement(s)
else
if (<conitional 2>)
sequence of statement(s)
.
.
else
sequence of statement(s)
CASE声明(立即检查条件,不过条件不可有交叠,效率比if高)
case (expression)
<condition 1>:
sequence of statement(s)
<condition 2>:
sequence of statement(s)
.
.
sequence of statement(s)
defalut:
sequence of statement(s)
endcase
两种其他形式的CASE声明
casez
-认为case条件下的所有'z'值不重要,不是逻辑值
-所有'z'值也可以表示为'?'
casex
-认为case条件下所有'x'和'z'值不重要,不是逻辑值
永远和重复循环
forever循环-不断执行
例:
inital
begin
clk = 0;
forever #25 clk = ~clk;
end
repeat循环-执行一定次数
例:
if(rotate == 1)
repeat(8)
begin
tmp = data[15];
data = {data<<1,temp};
end
while循环-如果表达式为真,则执行
例:(从0到100计数,101退出循环)
inital
begin
count = 0;
while(count<101)
begin(count<101)
$display("Count =%d",count);
count = count + 1;
end
end
for循环-循环开始时立即执行一次,如果表达式为真,则继续执行
例:
integer i; //declare the index for the FOR LOOP
always @(inp or cnt)
begin
result[7:4] = 0;
result[3:0] = inp;
if(cnt == 1)
begin
for(i=4;i<=7;i=i+1)
begin
result[i] = result[i-4];
end
result[3:0]=0;
end
end
Verilog HDL函数和任务
-函数和任务是子程序
-用于可重复代码
-增加模块可读性
-函数
--根据输入返回一个值
--产生组合逻辑
--用在表达式中:assign mult_out = mult(ina,inb);
-任务
--与其他语言的程序相似
--可以使组合或者寄存
--以声明形式调用任务:stm_out(nxt,first,sel,filter);
函数定义例:
function [15:0]mult;
//函数名字,也是返回变量
input[7:0] a,b;
reg[15:0] r;
integer i;
begin
if(a[0]==1)
r=b;
else
r=0;
for(i=1;i<=7;i=i+1)
begin
if(a[i]==1)
r=r+(b<<i);
end
mult=r;
end
endfunction
任务实例:
task add; //task definition
input a,b; //two input argument ports
output c; //one output argument port
begin
c=a+b;
end
endtask
函数和任务的不同
函数 | 任务 |
可以使能其他函数,但不是其他任务 | 可以使能其他任务和函数 |
不能包括任何延迟,时间或者时序控制声明 | 可以含有延迟,事件或者时序控制声明 |
至少要有一个输入变量 | 可以由零或者更多的输入,输出或者inout变量 |
总能返回一个数值 | 返回零或者更多的数值 |
不能有output或者inout变量 |
函数调用时表达式为
assign mult_out = mult(ina,inb);
ina,inb为输入量,而函数定义中的r作为输出量,返回给mult,然后赋值给mult_out,每次能够只能返回一个变量
任务调用表达式为
add(1,0,p); p声明为reg
这里按照数值出现顺序进行传递,1,0为input,而p作为output输出,这里直接返回给p,没有通过内部中继,可以一次性返回多个变量
函数可以调用另外一个函数,但是不可以调用另外一个任务
而任务可以调用另外一个任务,也可以调用函数
相关文章推荐
- xilinx fpga学习笔记5:Xst综合属性
- xilinx fpga学习笔记7:时序约束原理
- FPGA学习笔记---COEDIC算法
- FPGA学习笔记4-VHDL
- Xilinx FPGA 学习笔记——时钟资源
- Xilinx FPGA 学习笔记——原语 BUFIO 的理解
- FPGA学习笔记6-Quartus II中的TCL脚本
- FPGA学习笔记(转)
- Xilinx FPGA 学习笔记——原语 BUFIO 的理解
- FPGA学习笔记之LPM RAM使用
- FPGA从零开始-Verilog语法学习笔记(一)
- xilinx fpga学习笔记4
- 学习笔记——FPGA逻辑基础 from altera
- FPGA学习笔记——点亮LED
- FPGA学习笔记6-Quartus II中的TCL脚本(翻译)
- FPGA学习笔记之先定义还是先使用?
- 小梅哥fpga学习笔记之NIOS II CPU复位异常的原因及解决方案
- FPGA 学习笔记(十一) VGA驱动的实现
- FPGA 学习笔记 (十) PLL核的定制