FPGA知识点
Verilog基础语法
基础知识
逻辑值
-
逻辑0:表示低电平,也就是对应电路GND
-
逻辑1:表示高电平,也就是对应电路VCC
-
逻辑X:表示未知,有可能是高电平也有可能是低电平
-
逻辑Z:表示高阻态,外部没有激励信号,是一个悬空状态
数字进制格式
Verilog数字进制格式包括二进制(b),八进制(o),十进制(d)和十六进制(h)。
一般常用的为二进制、十进制和十六进制。
二进制表示如下:
4b’0101表示4位二进制数字0101
十进制表示如下:
4’d2表示4位十进制数字2(二进制0010)
十六进制B表示如下:
4’ha表示4位十六进制数字a(二进制1010)
可以增加下划线来增加可读性
16’b1001_1010_1010=16’h9AA9
标识符
标识符(identifier)用于定义模块名、端口名、信号名等。
标识符可以是任意一组字母、数字、$符号和**_(下划线)符号**的组合;
但标识符的第一个字符必须是字母或者下划线;
标识符是区分大小写的。
标识符推荐写法:
-
不建议大小写混合使用
-
普通内部信号建议全部小写
-
信号命名最好体现信号的含义,简洁、清晰、易懂;
以下是一些推荐的写法:
-
用有意义的有效名字如sum、cpu、addr等
-
用下划线区分词,如cpu_addr
-
采用一些前缀或者后缀,比如时钟采用clk前缀:clk_50,clk_cpu
数据类型
在Verilog语言中,主要有三大类数据类型:
寄存器数据类型、线网数据类型和参数数据类型
真正在数字电路中起作用的数据类型应该是寄存器数据类型和线网数据类型
寄存器类型
寄存器表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值
寄存器数据类型的关键字是reg,reg类型数据的默认初始值为不定值X
//reg define
.reg [31:0] delay_cnt; //延时计数(表示这是一个32位寄存器,高位在前,低位在后)
Reg key_reg; //默认位宽为1
reg类型的数据只能在always语句和initial语句中被赋值。
-
如果该过程语句描述的是时序逻辑,即always语句带有时钟信号,则该寄存器变量对应为触发器;
-
如果该过程语句描述的是组合逻辑,即always语句不带有时钟信号,则该寄存器变量对应为硬件连线;
线网类型
线网数据类型表示结构实体(例如门)之间的物理连线。
线网类型的变量不能储存值,它的值是由驱动它的元件所决定的。
驱动线网类型变量的元件有门、连接赋值语句、assign等。
如果没有驱动元件连接到线网类型的变量上,则该变量就是高阻的,即其值为z。
线网数据类型包括wire型和tri型,其中最常用的就是wire类型
//wire define
wire key_flag;
参数类型
参数其实就是一个常量,在Verilog HDL中用parameter定义常量。
我们可以一次定义多个参数,参数与参数之间需要用逗号隔开。
每个参数定义的右边必须是一个常数表达式。
//parameter define 4.3'RGB LCD
parameter H_SYNC = 11'd41; //行同步
parameter H_BACK = 11'd2; //行显示后沿
parameter H_DISP = 11'd480 //行有效数据
parameter H_FRONT = 11'd2 //行显示前沿
parameter H_TOTAL = 11'd525 //行扫描周期
参数型数据常用于定义状态机的状态、数据位宽和延迟大小等。
采用标识符来代表一个常量可以提高程序的可读性和可维护性。
在模块调用时,可通过参数传递来改变被调用模块中已定义的参数。
运算符
Verilog中的操作运算符按照功能可分为以下类型:
-
算术运算符
符号 使用方法 说明 + a+b a加上b - a-b a减去b * a*b a乘以b / a/b a除以b % a%b a模除b -
关系运算符
符号 使用方法 说明 > a>b a大于b < a<b a小于b <= a>=b a大于等于b >= a<=b a小于等于b == a==b a等于b != a!=b a不等于b -
逻辑运算符
符号 使用方法 说明 ! !a a的非,a如果为0,那么a的非是1 && a&&b a与上b,如果a和b都为1,a&&b结果才为1,表示真 || a||b a或上b,如果a或者b有一个为1,a ||b结果为1,表示真 -
条件运算符
符号 使用方法 说明 ?: a?b;c 如果a为真,就选择b,否则选择c -
位运算符
符号 使用方法 说明 ~ ~a 将a的每个位进行取反 & a&b 将a的每个位与b相同的位进行相与 | a|b 将a的每个位与b的相同位进行相或 ^ a^b 将a的每个位与b的相同位进行异或 如果a和b位宽不一致,系统会在较短的位宽的数据前面补0,补到二者位宽一致,再进行位运算
-
移位运算符
符号 使用方法 说明 << a<<b 将a左移b位 >> a>>b 将a右移b位 两种移位运算都用0来填充移出的空位。
左移时,位宽增加;右移时,位宽不变。
4'b1001 << 2 = 6'b100100 4'b1001 >> 1 = 4'b0100
-
拼接运算符
符号 使用方法 说明 {} {a,b} 将a和b拼接起来,作为一个新信号 c = {a,b[3:0]}; c[11:0] = {a[7:0],b[3:0]};
运算符优先级
Verilog程序框架
Verilog注释
verilog中有两种注释方式
一、以//开头的语句,它表示以//开头到本行结束都属于注释语句
二、以/*开头,以*/结束,在两个符号之间的语句都是注释语句,因此可以扩展到多行。
Verilog关键字
常用关键字
Verilog程序框架
Verilog的基本设计单元是”模块“(block)
一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能。
module block (a,b,c,d);//定义模块
input a,b;//输入接口
output c,d;//输出接口
assign c = a | b;//描述模块的功能
assign d = a & b;
endmodule
每个verilog 程序包括4个主要的部分:
端口定义、IO说明、内部信号声明、功能定义。
module flow_led(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
output reg [3:0] led //4个led灯 四位 reg表示输出端口是一个寄存器类型的端口
);
//reg define
reg [23:0] counter;//寄存器型 24位 计数器
//main code
//计数器对系统时钟计数,计时0.2秒
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
counter <= 24'd0;
else if(counter < 24'd1000_0000)
counter <= counter + 1'b1;
else
counter <= 24'd0;
end
//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态、
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <=4'b0001;
else if(counter == 24'd1000_0000)
led[3:0] <={led[2:0],led[3]};
else
led <= led;
end//这两个always模块在执行时是同时执行的,而在里面是顺序执行的
endmodule
功能定义有三种方法:
-
assign语句
描述组合逻辑
-
always语句
描述组合/时序逻辑
-
实例化元件
如:
and#2 u1(q,a,b)
上述三种逻辑功能是并行的
注意:
在always块中,逻辑是顺序执行的。而多个always块之间是并行的。
模块的调用
在模块调用时,信号通过模块端口在模块之间传递。
module seg_led_static_top (
input sys_clk, //系统时钟
input sys_rst_n, //系统复位信号(低电平有效)
output [5:0] sel, //数码管位选
output [7:0] seg_led //数码管段选
);
//parameter define
parameter TIME_SHOW = 25'd2500_0000;//数码管变化的时间间隔0.5s
// wire define
wire add_flag;//数码管的通知信号
//main code
//每隔0.5s产生一个时钟周期的脉冲信号
time_count #(
.MAX_NUM (TIME_SHOW) //这里是参数,MAX_NUM 给参数传递一个新值TIME_SHOW
) u_time_count(
.clk (sys_clk),
.rst_n (sys_rst_n),
.flag (add_flag)//输出信号连接到上层的wire类型的变量里面,输出段必须连接到一个wire类型的变量,否则会报错,并且位宽必须保持一致
);
module time_count(
input clk,
input rst_n,
output reg flag
);
//parameter define
parameter MAX_NUM = 5000_0000;
//reg define
reg [24:0] cnt;
//另外一种端口连接方式:
//每隔0.5s产生一个时钟周期的脉冲信号
time_count #(
.MAX_NUM (TIME_SHOW)
) u_time_count(
sys_clk,
sys_rst_n,
add_flag
);
Verilog高级知识点
结构语句
initial和always语句
initial(初始化)语句它在模块中只执行一次。它常用于测试文件地编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。
always语句一直在不断地重复活动。
但只有和一定的时间控制结合在一起才有作用。
initial begin
sys_clk <=1'b0;
sys_rsr_n <=1'b0;
touch_key <=1'b0;
#20 sys_rst_n <=1'b1; //#20: 延时20个单位时间,20ns
#10 touch_key <=1'b1;//#10:相对于上一个sys_rst_n延时的20ns再延时10ns把电位调高
#30 touch_key <=1'b0;
#110 touch_key <=1'b1;
#30 touch_key <=1'b0;
end
//产生50Mhz的时钟,周期为20ns
always #10 sys_clk <= ~sys_clk;//延时10ns取反
always的时间控制可以是沿触发,也可以是电平触发;
可以是单个信号也可以是多个信号,多个信号中间要用关键字or连接。
always @(posedge sys_clk or negedge sys_rst_n) begin //posedge上升沿、negedge下降沿
//按顺序执行
if(!sys_rst_n)
counter <= 24'd0;
else if(counter < 24'd1000_0000)
counter <= counter + 1'b1;
else
counter <= 24'd0;
end
沿触发的always块常常描述时序逻辑行为。
由关键词or连接的多个事件名或信号名组成的列表称为“敏感列表”。
电平触发的always块常描述组合逻辑行为。
always @(a or b or c or d or e or f or g or h or p or m) begin
out1 = a ? (b+c) : (d+e);
out2 = f ? (g+h) : (p+m);
end
如果组合逻辑块语句的输入变量很多,那么编写敏感列表会很繁琐并且容易出错,
always @( * ) begin //@( * )表示后面语句块中所有输入变量的变化都是敏感的。
out1 = a ? (b+c) : (d+e);
out2 = f ? (g+h) : (p+m);
end
根据逻辑功能的不同特点,可以将数字电路分成两大类:
组合逻辑电路和时序逻辑电路。
- 组合逻辑电路中,任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。
- 时序逻辑电路中,任意时刻的输出不仅取决于当时的输入的信号,而且还取决于电路原来的状态。或者说还与以前的输入有关,因此时序逻辑必须具备记忆功能。
赋值语句
Verilog HDL语言中,信号有两种赋值方式
-
阻塞赋值(blocking),如a = b
串行执行
阻塞赋值可以认为只有一个步骤的操作:即计算RHS并更新LHS。
所谓阻塞的概念是指,在同一个always块中,后面的赋值语句是在前一句赋值语句结束后才开始赋值的。
always @(posedge clk or negedge rst_n) begin if (!ret_n)begin a = 1; b = 2; c = 3; end else begin a = 0; b = a; c = b; end end
-
非阻塞赋值(Non_Blocking),如b <= a;
并行执行
非阻塞赋值的操作过程可以看作两个步骤:
- 赋值开始的时候,计算RHS;
- 赋值结束的时候,更新LHS。
所谓非阻塞的概念是指,在计算非阻塞赋值的RHS以及更新LHS期间,运行其他的非阻塞赋值语句同时计算RHS和更新LHS。
always @(posedge clk or negedge rst_n) begin if (!ret_n)begin a <= 1; b <= 2; c <= 3; end else begin a <= 0;//右侧同时计算结果但不赋值,此时0还没赋值给a,a还是1 b <= a;// a = 1 c <= b;// b = 2 end end
非阻塞赋值只能用于对寄存器类型的变量进行赋值,因此只能用在initial块和always块等过程块中。
在描述组合逻辑的always块中用阻塞赋值=,综合成组合逻辑的电路结构;
这种电路结构只与输入电平的变化有关系。
在描述时序逻辑的always块中用非阻塞赋值<=,综合成时序逻辑的电路结构;
这种电路结构往往与触发沿有关系,只有在触发沿时才可能发生赋值的变化。
注意:在同一个always块中不要即用非阻塞赋值又用阻塞赋值
不允许在多个always块中对同一个变量进行赋值!
条件语句
if -else语句
// (1)
if(a>b)
cout= data_1
//(2)
if(a>b)
out = data_1;
else
out = data_2;
//(3)
if(表达式)
语句
else if(表达式)
语句
else if(表达式)
语句
else
语句
条件语句必须在过程块中使用
- 过程块语句是指由initial和always语句引导的块语句
注意事项:
-
允许一定形式的简写,如:
if(a)
等同于if(a==1)
if(!a)
等同于if(a!=1)
-
if语句对表达式的值进行判断,若为0,x,z则按假处理;若为1,按真处理
-
if和else后面的操作语句可以用begin和end包含多个语句。
-
允许if语句的嵌套。
case语句(多分支选择语句)
-
分支表达式的值互不相同;
-
所有表达式的位宽必须相等;
不能用‘bx来代替n’bx
-
casez
比较时,不考虑表达式中的高阻值z
-
casex
比较时不考虑高阻值z和不定值x
状态机
状态机的概念
状态机(State Machine)
- 有限状态机(Finite State Machine,简称FSM)
- 在有限个状态之间按一定规律转换的时序电路。
状态机模型
状态寄存器由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳变沿。
状态是否改变、如何改变,取决于组合逻辑F的输出,F是当前状态和输入信号的函数。
状态机的输出是由输出组合逻辑G提供的,G也是当前状态和输入信号的函数。
状态机设计
四段论
-
状态空间定义
//define state space parameter SLEEP = 2'b00; parameter STUDY = 2'b01; parameter EAT = 2'b10; parameter AMUSE = 2'b11; //internal veriable reg [1:0] current_state; reg [1:0] next_state; //独热码 //define state space; parameter SLEEP = 4'b1000; parameter STUDY = 4'b0100; parameter EAT = 4'b0010; parameter AMUSE = 4'b0001; //internal variable reg [3:0] current_state; reg [3:0] next_state;
独热码:每个状态只有一个寄存器置位,译码逻辑简单。
-
状态跳转
//transition always @(posedge clk or negedge rst_n)begin//敏感列表:时钟信号以及复位信号边沿的组合 if(!rst_n) current_state <= SLEEP; else current_state <= next_state;//时序逻辑里使用非阻塞赋值 end
-
下个状态判断(组合逻辑)
//next state decision always @(current_state or input_signals)begin//敏感信号表:所有的右边表达式中的变量以及if、case条件中的变量 case (current_state)//case的状态没给全也会产生锁存器,多余的状态用default SLEEP:begin if(clock_alarm) next_state = STUDY;//使用阻塞赋值 else next_state = SLEEP; end STUDY: begin if(lunch_time) next_state = EAT; else //if /else 要配对,避免latch(锁存器)的产生,只在组合逻辑中产生 next_state = STUDY; end EAT: ...; AMUSE: ...; default: ...; endcase end
-
各个状态下的动作
//action wire read_book; assign read_book = (current_state == STUDY) ? 1'b1: 1'b0; always @(current_state)begin if(current_state == STUDY) read_book = 1; else read_book = 0; end
文章来源:https://www.toymoban.com/news/detail-772972.html
文章来源地址https://www.toymoban.com/news/detail-772972.html
到了这里,关于FPGA基础知识点的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!