`
lovnet
  • 浏览: 6691267 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

FPGA学习笔记3-verilog HDL

 
阅读更多
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语句的输出

输入输出规则表

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:取模支持的比较有限

1.把矢量整体作为一个数值
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

1.对操作数的每一位进行运算
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

1.将矢量减为单比特
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

1.用于对比数值
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

1.用于对比数值
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

1.返回一位标量值,布尔真(1)/假(0)
2.如果任一操作数是Z或者X,那么结果未知

移位算子

算子符号 执行的计算 例子(ain=1'b1010,bin=4'b10X0)
>> 右移 bin>>1 = 4'b010X
<< 左移 ain<<2 = 4'b1000

1.矢量向左或者向右移动一定比特
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变量

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,没有通过内部中继,可以一次性返回多个变量
函数可以调用另外一个函数,但是不可以调用另外一个任务
而任务可以调用另外一个任务,也可以调用函数
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics