verilog 笔记
每日一言
A scar indicates that something is missing. You lost something important, and there’s a hole in your heart. Having that empty hole can lead to anger, hate, despair, or… it can make you reach for the sky. – Fuuko Kurasaki
fromAccel World
verilog语言程序的基本结构
verilog 标识符
verlog的标识符有以下三点要求:
- 第一个字符不能是数字
- 区分大小写,不能与Verilog的保留字相同
- 关键字必须小写
这里不是重点,简单了解即可,我们看点示例:
同时在创建verilog项目时,项目名,verilog文件名,和设计模块的名字,必须要相同,否则可能会因名字未定义而编译失败。
verilog 的注释语法与c语言的语法一样,同时每个语句的后面也要以”;” 结尾。
逻辑值
verilog是四值逻辑语言。有四种基本的逻辑值:
值 | 含义 |
---|---|
0 | 逻辑0、逻辑非、低电平 |
1 | 逻辑1、逻辑真、高电平 |
x/X | 不确定的逻辑状态 |
z/Z | 高阻态 |
接口描述
verilog 的每个模块都由两部分组成:
- 接口描述
- 功能描述
我们首先来学习如何描述一个模块的接口:
声明一个模块
声明一个模块的格式如下:
1 |
|
对于一个端口,首先我们需要描述这个端口是输入端(input)还是输出端(output),然后我们需要描述这个端口是寄存器类型还是线网类型。
I/O声明对于每个端口来说是必须的,但是第二种类型说明在端口为线网类型时可以省略。即不特殊声明的端口默认为线网类型。
示例如下:(一位全加器)
1 |
|
如果输入/输出有多个位,例如四位的全加器,输入a,b 各有四个位,可以如下声明:
1 |
|
但是加入一个芯片,有四组输入,每组输入为3位,可以如下声明:
1 |
|
I/O 端口定义:
数据类型定义
注意:输入和双向端口不能声明为reg型。
parameter 类型说明
parameter 定义的符号常量只能在定义模块内使用。定义之后,就可以将该数字以定义的变量表示。
示例:
1 |
|
数的表示
verilog 中表示一个数的方法是:<二进制位宽>' <进制><数字>;
进制常用的表示方法有:
- b (2进制)(z,x 等状态也当作一个二进制位)
- d (10进制)
- h (16进制)
- 如果直接输一个数字,默认会当作32位10进制数
示例如下:
运算符
区分逻辑操作、位操作与归约操作
逻辑操作
逻辑操作有两个操作数,只不过返回的结果只有1位,1’b1或1’b0 。只能用于进行逻辑判断。用法与c语言相同
位操作
位操作也有两个操作数,其结果是两个操作数进行按位运算,位数小的数高位补0。总体用法也与c类似。
归约运算
归约运算只有一个操作数,结果只有一位,是对操作数的每一位逐位进行运算,取最后的一次运算的结果为整个运算的结果。
例如 a = 4'b1011
1 |
|
过程为:
- 1 & 1 = 0 缩减为100
- 0 & 0 = 0 缩减为1 0
- 1 & 0 = 0 结果为0
位拼接运算
{ } ,将两个或多个信号的某些位拼接起来。但是不允许连接非定长常数。
例子:
1 |
|
用{ }连接时的线我们可以当作一个拥有多位宽的类型。将其中的线视为一个整体。
运算长度
- 自动调整位宽:运算表达式结果的长度由最长的操作数决定。
- 赋值结果的长度: 由赋值左端目标长度决定。
功能描述语句
连续赋值assign语句
句式:
1 |
|
使用assign赋值有以下要求:
- 赋值运算符只能使用’=’
- assign 后面只能跟一个表达式
- 避免出现反馈如 :
a = a+b
- inout型引脚只能用在assign中
- 赋值目标只能是wire型
- 多个assing 之间是并行关系
- assign只能逐句使用,只能完成简单的组合逻辑
- 不能对同一个信号进行多次assign赋值
过程赋值always语句块
always 后面是语句块,并且在always中的赋值语句必须是reg型。
在always 中的所有代码是顺序执行的。
句式:
1 |
|
敏感信号
always语句只在敏感信号变化的时候才会启动进程。
- 上升沿触发:
always @(posedge 信号名)
- 下降沿触发:
always @(negedge 信号名)
- 电平触发:
always @(信号名)
多个电平信号:always @(a or b or c)
或者always @(a,b,c)
- 所有信号均触发:
always @(*)
,组合逻辑。
特别注意: 敏感信号 always @(posedge LE or negedge LE)
这种写法是错误的,Verilog 并不支持直接同时检测 posedge
和 negedge
,只能选择其中一个。或者使用LE的电平信号来作为敏感信号。
aways中的两种赋值语句
- ‘=’ 赋值,阻塞赋值。会立即更新数值。用于组合逻辑。
- ‘<=’ 赋值,非阻塞赋值,延迟更新数,所有的赋值语句同时完成更新。用于时序逻辑。
注意:
- 不能在组合逻辑中使用 <= 赋值,<= 用于实现时序逻辑
- 在一个always中不要混用两种赋值语句
简单区分”=” 和 “<=”
1 |
|
= ,阻塞赋值,会立即覆盖原来的值,所以
- x = c&a
- y = c&d
1 |
|
<=,非阻塞赋值,会等执行到end语句后再统一赋值,d的最后一个值为 b,所以
- x = c&b
- y = c&b
注意:不能在多个always和assign中对同一个信号多次赋值。
转向控制语句
转向控制语句有 for,case,if 语句。 这三种语句都只能在always中使用。
if语句
句式:
1 |
|
- if 语句中最先出现的条件优先级最高,自上而下优先。
- 可以有零到多个else if,但是必须有一个else,以保持良好的编码风格。
- 如果在某个分支中对于某个变量赋值,那么在其他分支中,也需要对于该变量赋值。
case语句
根据某个表达式的值来选择执行体。无优先级。
句式:
1 |
|
- 分支条件必须在表达式范围内,且不能重合
- 执行时必须选中且只能选中一个分支
- 如果在某个分支中对于某个变量赋值,那么在其他分支中,也需要对于该变量赋值。
备注:
在 if 或 case 中,如果一个分支内有多条语句,那么需要将其中的语句封装在一个语句块中,即用 begin
表示语句块开始,用 end
表示语句块结束。
for语句
句式:
1 |
|
- 表达式1:设置循环变量初值,必须定义一个i 循环变量
- 表达式2:循环执行判断条件
- 表达式3:更新循环变量,只能用类似i = i +1 或 i= i - 1;
- 注意:初值,步长,循环条件中不能有变量