赞
踩
从今天开始自学FPGA,自学过程中感受较深的是,FPGA的编程不止是写代码,代码实际是一个电路,这点是与单片机编程概念不同的。
代码的好坏直接影响的是一个硬件电路。
- 逻辑0:低电平,也就是电路中的GND
- 逻辑1:高电平,也就是电路中的VCC
- 逻辑X:未知状态,
- 逻辑Z:高阻态,外部没有激励信号,是一个悬空状态
b:表示二进制 o:表示八进制 d:表示十进制 h:表示十六进制
- numA=8'b10101010; //8'表示numA这个变量占8位,numA的值是二进制的10101010
-
- numB=4'd2;//numB的值是十进制的2
-
- numC=4'ha;//numC的值是十六进制的A
-
- numD=10;//numD占32bit,是十进制的10
说明:不指定位宽则默认是32位,不指定进制则默认是十进制
标示符:可用于模块名、端口号、信号名等(类似C的变量名)
- 寄存器数据类型 (reg)
- 线网数据类型(wire)
- 参数数据类型(parameter)
关键则:reg 初始值为:不定制X(逻辑X)
- module test(L);
- output LED;
- input num;
- reg[7:0] LED; //寄存器数据类型
- reg num; //寄存器数据类型
- always
- begin
- LED=8'b10101010;
- end
- endmodule
关键字:wire 表示结构实体之间的物理连线
线网类型的变量不能村初值,他是由驱动她都元件决定的
没有驱动元件连接的线网类型变量是:高阻值(逻辑Z)
wire key_flag;
关键字:parameter
他类似C中的 #define,宏定义
Verilog中的 算数运算符、关系运算符、逻辑运算符、条件运算符、位运算符、移位运算符包括其 运算符优先级 也与C语言相同。
Verilog特有的拼接运算符:{}
C={a,b[3:0]} //将变量a与变量b的低四位拼接起来,赋值给变量C
Verilog的基本设计单元是“模块”(类似C语言中的函数)
一个模块由两部分组成
一个模块的示例:
- module test(LED,a,b,c,d,e); //模块开始的关键字是:module 括号里的都是:端口定义
- output LED,c,d; //输出信号 input output 都是:IO说明
- input a,b; //输入信号
- reg[7:0] LED; //寄存器数据类型
- reg e; //寄存器数据类型
- wire f; //线网数据类型 f:内部信号
-
- //变量声明结束后,都是:功能定义
- assign c=a|b;
- assign d=a&b; //assign :产生wire信号语句的关键字 描述组合逻辑
-
- always begin //always:产生reg信号语句的关键字 描述组合、时序逻辑
- LED=8'b10101010;
- end
-
- endmodule //模块要用“endmodule”结束
与C语言不同的是,Verilog中多个<always>块都是“并行”的 ,也就是说,两个<always>块可以同时被执行。
单个<always>块是中的代码是顺序执行的。
assign相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。
assign的功能属于组合逻辑的范畴,应用范围可概括为以下三点:
- 持续赋值;
- 连线;
- 对wire型变量赋值,wire是线网,相当于实际的连接线,如果要用assign直接连接,就用wire型变量。wire型变量的值随时变化。其实以上三点是相通的。
要更好的把握assign的使用,Verilog中有几个要点需要深入理解和掌握:
- 在Verilog module中的所有过程块(如initial块和always块)、连续赋值语句(如assign语句)和实例引用都是并行的。在同一module中这三者出现的先后顺序没有关系。
- 只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于module的功能定义部分。
- 连续赋值assign语句独立于过程块,所以不能在always过程块中使用assign语句。
- module LED_Blink( //可以在端口定义时 指定IO
- input sys_clk, //系统时钟
- input sys_rst_n, //系统复位
- output reg[3:0] led;//4bit 代表四颗灯
- );
- reg [23:0] count; //计数 (这是一个内部信号声明)
- //计数器对系统时钟计数,0.2s
- /*赋值“=”用于阻塞式赋值(执行到该行时,等待赋值完成再执行下一条语句),仿真是initial中用=;
- “<=”用于非阻塞式赋值中(执行到改行时,不等待赋值完成,直接执行下一条语句),always中用<=
- */
- always @(posedge sys_clk or negedge sys_rst_n)begin//posedge:上升沿触发 negedge:下降沿触发
- if(!sys_rst_n)
- count<=24'd0; //对Count1赋23位十进制的0
- else if(count<24'd1000_0000)
- count<=count+1'b1;
- else
- count<=24'b0;
- end
-
- endmodule
赋值“=” 用于阻塞式赋值(执行到该行时,等待赋值完成再执行下一条语句),仿真是initial中用=;
赋值“<=”用于非阻塞式赋值中(执行到改行时,不等待赋值完成,直接执行下一条语句),always中用<=
在模块调用时,信号通过模块端口在模块之间传递。
- module seg_led_static_top( //上层模块
- input sys_clk, //系统时钟
- input sys_rst_n, //系统复位
- output [5:0] sel;
- output [7:0] sel_led;
- );
- parameter TIME_SHOW=25'd25000_000; //宏定义
- wire add_flag; //数码管变化通知
-
- //# 是延迟的意思,井号后面数字是延迟的数量
- //#1 a=1;#表延迟,延迟一个时间单位后执行a=1
-
- time_count #( //这里的#表示:用来将parameter变量传给调用实例。
- .MAX_NUM (TIME_SHOW)
- //上层模块传输给底层模块
- )
- u_time_count(
- .clk (sys_clk) // .表示:连接 将上层模块的sys_clk与下层模块clk连接在一起
- .rst_n (sys_rst_n)
- .flag (add_flag)
- )
-
- endmodule
-
-
-
- module time_count( // 底层模块
- input clk, //系统时钟
- input rst_n, //系统复位
- output reg flag;
- );
- parameter MAX_NUM=25'd50000_000; //宏定义
- reg [24:0] cnt;
- endmodule
# 是延迟的意思,井号后面数字是延迟的数量
例如代码 : #1 a=1; //延迟一个时间单位后执行a=1
#():这里的#表示:用来将parameter变量传给调用实例。
.是连接的意思:将上层模块的变量与下层模块变量连接在一起
initial 语句只执行一次
always 语句是一直不断重复的
- //initial 语句只执行一次
- //always 语句是一直不断重复的
- initial begin
- sys_clk=10;
- b=11;
- c=12;
- #20 d=13;
- end
- //赋值“=”用于阻塞式赋值(执行到该行时,等待赋值完成再执行下一条语句),仿真是initial中用=;
-
- always #10 sys_clk<=~sys_clk; //chans 50Mhz的时钟,周期为20ns
- //“<=”用于非阻塞式赋值中(执行到改行时,不等待赋值完成,直接执行下一条语句)always中用<=
-
always的时间就控制可以是沿触发,也可以是电平触发。
- always @(posedge sys_clk or negedge sys_rst_n)begin//posedge:上升沿触发 negedge:下降沿触发
- if(!sys_rst_n)
- count<=24'd0; //对Count1赋23位十进制的0
- else if(count<24'd1000_0000)
- count<=count+1'b1;
- else
- count<=24'b0;
- end
or 连接的多个时间或者信号组成的列表称为:敏感列表
只有敏感列表里的条件满足时,才会执行此always模块
- always @(*)begin
- out1=a?(b+c):(d+e);
- end
- //@(*):模块中所有使用的输入变量都是敏感的
@(*):模块中所有使用的输入变量都是敏感的
假设 a=1,b=2,C=3 执行 a=0;b=a;c=b; 后 a b c的值都为0
假设 a=0,b=2,C=3 执行 a<=0;b<=a;c<=b; 后 a =0 b=1 c=2
描述组合逻辑的always语句用“阻塞赋值=”
描述时序逻辑的always语句用“非阻塞赋值<=”
注意:1.在同一个always模块中不同时使用 非阻塞赋值 和 阻塞赋值
2. 不允许在多个always块中一个变量进行赋值 ,因为Verilog是并行执行的
阻塞赋值和非阻塞赋值产生的实际电路区别:
当存在多条赋值语句时,阻塞赋值会产生多个级联的触发器.
if .........else if.......... 用法和C语言相同。
if......esle..........是带有优先级的,实际产生的电路如下:
case是没有优先级的 实际电路如下:
一般只用于仿真测试
while循环与C语言相同,但是Verilog中只用于仿真调试
与C语言相同,实际产生的是一个多层级的物理电路,for循环在Verilog中比较常用。
函数通常为一系列组合逻辑,有返回值
函数关键字:function
函数定义:
- //下面相当于函数声明
- function [15:0]mult;//函数名
- input[7:0] a,b;
- reg [15:0] r;
- integer i; //整数(integer),integer类型的变量为有符号数
- //下面相当于函数主体
- 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<<1;
- end
- mult=r; //返回值就是函数名
- end
- endfunction
函数调用:
-
- module
- mult_acc(
- input[7:0] ina,inb;
- )
- wire[15:0]mult_out;
-
- assign mult_out=mult(ina,inb);//函数调用
- endmodule
任务关键字:task
- module module_name
-
- task add_num; //任务定义
- input a,b;
- output c;
- begin
- c=a+b;
- end
- endtask
-
- reg num;
- add_num(1,2,num) //任务调用
-
- endmodule
函数 与任务的区别
function | task |
|
|
必须包含至少一个输入 | 可以没有输入、输出 |
不能使用输出/双向参数 (类似C的形参带出返回值),只能返回1个值,返回变量与函数名相同 | 使用输出参数、可以返回多个值 |
函数可以调用另一个函数,但是不能调用任务 | 任务可以调用另外的函数或者任务。 |
状态机:在有限个状态之间按一定规律转换的时序电路
状态机模型中由 两个组合逻辑 和 一个时序逻辑 组成
以下的设计步骤:
是一个定义的过程
推荐每一个状态只有寄存器的一个位置,这样编译逻辑简单,编译后的硬件电路简单
状态跳转是一个时序逻辑
再次强调:时序逻辑中使用 非阻塞赋值
根据输入状态判断输出状态,是一个组合逻辑
再次强调:组合逻辑中使用 阻塞赋值
latch:是一个锁存器,程序设计中要避免latch的产生,他会使我们电路输出的信号产生毛刺
(if else 没有配对 case中没有default 就会产生latch)
根据当前状态完成当前动作,是一个组合逻辑
下面是产生组合逻辑的两种方式:
always:可以表示时序逻辑 和 组合逻辑
mealy状态机 后面又加了一级寄存器来实现时序逻辑的输出,这么做的好处是:
举例有代码如下:
在一个clk时钟的上升沿到来时,always语句块里的代码同时被执行。
S_in1里的值是上一时刻S_in0的值,不会是这一时刻S_in的值。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。