一、FPGA
-
PLD - 可编程逻辑器件
- CPLD - 复杂可编程逻辑器件【基于乘积项的与或逻辑阵列】
- FPGA - 现场可编程逻辑门阵列【基于查找表的CLB阵列】
- 两者虽然有差异,但也只是硬件层面,在编程上是没有区别的!
-
什么是FPGA?
- 一种可通过编程来修改其逻辑功能的数字集成电路(芯片)
-
与单片机的区别?
- 单片机并不改变电路的内部连接结构,只是根据要求实现的功能来编写运行的程序(指令)
二、HDL
-
数字系统设计流程?
- 逻辑设计(前端)-> 电路实现(后端) -> 系统验证
-
逻辑设计(前端)
- HDL(硬件描述语言)可以在不同的层次对数字电路的结构、功能和行为进行描述。
- 数字电路最基本的电路是门电路!
-
电路实现(后端)
- HDL描述的电路,首先通过综合工具将其转换为门级电路网表,然后将其余某种工艺的基本元件逐一对应起来,再通过布局布线工具转换为电路布线结构!
-
Verilog HDL和VHDL区别
- 在欧洲VHDL普及度比较广,在中国和美国Verilog HDL普及度比较广
- Verilog HDL相对于VHDL灵活性较好,上手相对容易。
-
Verilog和C的区别?(FPGA和单片机最大的区别?)
- Verilog是硬件描述语言,在编译下载到FPGA之后会生成电路。由于上电之后电路是并行的,所以Verilog也是并行的。(速度相对快)
- C语言是软件编程语言,编译下载到单片机后是存储器中的一组指令,而单片机处理软件指令需要取指、译码、执行。这个过程是串行执行的。(速度相对慢)
三、Verilog基本语法
-
常用的进制
- 二进制,4’b0101(4表示位宽)
- 十进制,4’d2(4’b0010)
- 十六进制,4’ha(4’b1010)
- 当没有明确给出位宽时,默认是32位位宽。若进制也没有的话,则默认是表示十进制。
- 给数值添加下划线,增加可读性。16’b1001_1010_1010_1001 = 16’h9AA9
-
标识符(类似变量名):定义模块名、端口名、信号名(变量或常量)
- 第一个字符必须是字母或者下划线
- 可以是任意一组字母、数字、$、_ 的组合
- 区分大小写
-
数据类型
扫描二维码关注公众号,回复: 11483811 查看本文章- 寄存器、线网、参数
- 真正在数字电路起作用的数据类型应该是寄存器和线网,而参数数据类型主要是给编译器识别用的。
-
寄存器数据类型
- 关键字:reg。默认初始值为不定值x。
- reg[31:0] delay_cnt; 定义了一个32位的位宽的寄存器
- reg key_reg; 定义了一个1位的位宽寄存器(没有给定位宽,就默认是1)
- reg类型只能在always语句和initial语句赋值
- 若always语句带有时钟信号(描述的是时序逻辑),则该寄存器变量对应为触发器
- 若always语句不带有时钟信号(描述的是组合逻辑),则该寄存器变量对应为硬件连线
- 关键字:reg。默认初始值为不定值x。
-
线网数据类型(当没有明确定义变量的类型,那么它就是线网类型)
- 表示结构实体(比如门)之间的物理连线
- 关键字:wire(常用)和tri(少用)
- 线网类型不能存储值,它的值有驱动它的元件所决定。
- 可以驱动线网类型的元件有:门、连续赋值语句、assign
- 若没有驱动元件,则该元件为高阻态z
- wire key_flag; 1位位宽的wire类型变量
-
参数数据类型
- 定义常量,类似C语言的define
- 关键字:parameter
- parameter H_SYNC = 11’d41; 定义H_SYNC 常量,注意相比C的define有等号也有分号。
- 可以一次定义多个参数,参数与参数之间用逗号隔开,但是不美观。
- 右边必须是常数
- 参数数据类型可以咏柳定义状态机的状态、数据位宽、延迟大小
-
运算符需要注意的
- 左移时,位宽增加;右移时,位宽不变;移位都是用0来补上空位
- 位拼接运算符
- {a,b},a和b拼接起来
- c[11:0] = {a[7:0], b[3:0] }
-
常用关键字
-
Verilog的基本设计单元是“模块”,同C语言的函数
- 模块由2部分组成:描述接口、描述逻辑功能
- 每个Verilog程序包括4个主要的部分:
- 端口定义、IO说明、内部信号声明、功能定义
-
Verilog里定义功能有三种方法:
- assign语句:描述组合逻辑
- always语句:描述时序/组合逻辑
- 例化实例元件:eg:and #2 u1(q,a,b); 例化一个与门,#2表示延时2(至于单位,要具体看情况)
- 以上三种逻辑功能是并行的!
- 注意
- 在always模块中的逻辑是顺序执行的
- 多个always模块间是并行的
-
例化一个模块时注意事项:
time_count #( .MAX_NUM (TIME_SHOW) ) u_time_count( .clk (sys_clk), .rst_n (sys_rst_n), .flag (add_flag) );
- 输出信号必须是一个wire类型的!
- #(xx)这个里面一般是进行parameter类型的变量赋值
u_time_count
里面的赋值是wire和reg类型的!
-
过程块:
- initial
- always
-
initial(里面按顺序执行)
- 在模块中只执行一次
- 常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值
-
always(里面按顺序执行)
- 一直在不断地重复活动,但是只有和一定的时间控制结合在一起才有作用。
always #10 sys_clk <= ~sys_clk
产生周期为20ns的时钟信号- always的时间控制可以是沿触发,也可以是电平触发;可以是单个信号,也可以是多个信号,多个信号之间要用关键字or连接。
- always后紧跟的过程块(顺序块)是否运行,要看它的触发条件是否满足。
always @(posedge sys_clk or negedge sys_rst_n)...
表示sys_clk上升沿或者sys_rst_n下降沿触发 - 由or连接的多个事件名或信号名组成的列表成为“敏感列表”
- 沿触发的always块常常用来描述时序逻辑行为
- 电平触发的always块常常用来描述组合逻辑
- 组合逻辑块语句的输入变量很多,如下
always @(a or b or c or d or e or ....)
- 这时我们可以直接使用
always @(*)
替代上面的写法
-
关于Verilog里面的begin end可以类比C语言的大花括号{}
-
延时
- 通常在一个.v文件开头定义延时单位和精度,比如
timescale 1ns / 1ps
- 那么#200 的意思就是延时200ns
- 那么#200.123的意思就是延时200.123ns,注意200.123ns = 200123ps,这个是由1ps决定的!
- 通常在一个.v文件开头定义延时单位和精度,比如
-
赋值语句
- 阻塞赋值:eg:
b = a
- 计算右边并更新左边
- 非阻塞赋值:eg:
b <= a
- 赋值开始的时候,计算右边
- 赋值结束的时候,更新左边
- 非阻塞只能用于对寄存器类型变量赋值
- 什么时候用阻塞赋值,什么时候用非阻塞呢?
- 描述组合逻辑电路always块用阻塞赋值
- 这种电路之与输入电平的变化有关
- 描述时序逻辑电路always块用非阻塞赋值
- 这种电路往往与触发沿有关(敏感列表中有posedge或者negedge)
- 注意
- 在一个always块中不要既用非阻塞又用阻塞
- 不允许在多个always块中对同一个变量赋值
- 描述组合逻辑电路always块用阻塞赋值
- 阻塞赋值:eg:
-
条件语句
- if
- 必须在过程块中使用。(initial和always中)
- 0、x(不定值)、z(高阻态)均为假。
- case
- if
-
状态机
-
检测顺序逻辑
-
在有限个状态之间按一定规律转换的时序电路,成为有限状态机(FSM)
-
Mealy【两个组合逻辑,一个时序逻辑】
-
Moore(特殊的Mealy)
-
状态机的设计 - 四段论
- 状态空间定义
-
注意:
- 位宽得一样
- 独热码(one hot code):每个状态只有一个寄存器置位,译码逻辑简单
- 状态跳转【时序逻辑】
-
注意是非阻塞赋值
- 下个状态判断【组合逻辑】
-
对应模型中的:
-
阻塞赋值
-
latch是指锁存器 - 电平触发存储器;触发器 - 边沿触发的存储器;
-
在编写Verilog时要注意,避免产生无谓的锁存器
- 锁存器只在组合逻辑中产生,锁存器会造成最后生成的电路毛刺较多;会影响我们对整个电路的时序分析
- 如果使用if,没有配套的else会产生锁存器;如果case语句没有写全,但有没有写default也会产生。
- 各个状态下的动作【组合逻辑】
-
对应模型中的
-
阻塞赋值
-
- 状态空间定义
-
-
三段式状态机(四段论的改进)