CO | P1_Verilog
Verilog语法
$signed()
这里写一下关于 $signed() 的理解。
$signed() 的真正功能是决定数据如何进行补位。一个表达式(特别注意三目运算符)中如果存在一个无符号数,那么整个表达式都会被当作无符号数。
signedness
self-determined expression:指一个位宽可以由该表达式本身独立决定的 expressioncontext-determined expression:指一个位宽由其本身以及其所属的表达式共同决定的 expression(例如一个阻塞赋值语句右侧表达式的位宽同时受左右两侧位宽的影响)
Verilog 在计算表达式前,会先计算表达式的 signedness。计算方式如下:
- 仅由操作数决定,不由左侧决定(如
assign D = exp,exp符号与D无关。这一点区别于位宽,位宽由左右两侧所有表达式的最大位宽决定) - 小数是有符号的,显式声明了进制的数无符号,除非用修饰符 s 声明了其有符号(如
'd12无符号,'sd12有符号 - 位选_多位选择_位拼接的结果无符号(如
b[2]、b[3:4]、{b}均无符号,这也是隔壁 bit extender 有同学用三目运算符结果 WA 了的原因) - 比较表达式的结果无符号(要么是
0,要么是1) - 由实数强转成整型的表达式有符号
- 一个 self-determined expression 的符号性仅取决与其操作数
- 对于 context-determined expression,只有所有操作数均为有符号数时表达式才有符号
在计算表达式时,先由以上规则得出最外层表达式的符号性,再向表达式里的操作数递归传递符号性。
$signed() 函数的机制是计算传入的表达式,返回一个与原表达式的值和位宽完全相同的值,并将其符号性设为有符号。该函数可以屏蔽外部表达式的符号性传递。
另外就是算术右移运算符(>>>)的有操作数不影响结果的符号,所以没必要加上 $signed()。
一些例子
下面是一些例子:
assign C = (ALUOp==3'b101) ? $signed(A)>>>B : $signed(A+B);正确assign C = (ALUOp==3'b101) ? $signed(A)>>>B : A+B;错误assign C = (ALUOp==3'b101) ? $signed(A)>>>B : 0;正确(0被看作有符号数)assign C = (ALUOp==3'b101) ? $signed(A)>>>B : 32'b0;错误(显式声明了进制,被当成无符号数)
特别的,还有:
assign C = (ALUOp==3'b101) ? $signed(signed(A)>>>B) : $signed(A+B);正确
因为外层推导出是无符号,但是外面的 $signed 阻止了 unsignedness 向内层转移。
位扩展
$signed() 用法太玄妙了,不如直接用位扩展替代。
{{16{imm[15]}}, imm} // 将 [15:0] 的 imm 扩展到 32 位 |
常用运算符
Verilog HDL 中有相当多的运算符都与 C 语言基本相同,如:
- 基本运算符:
+,-,*,/,%等 - 位运算符:
&,|,~,^,>>,<<等 - 逻辑运算符:
&&,||,!等 - 关系运算符:
>,<,>=,<=等 - 条件运算符:
? :
这些运算的运算规则与 C 语言相同,只是在操作数中出现了不定值 x 和高阻值 z 的话最终结果可能也是带 x 或 z 的。另外 Verilog 中没有自增、自减运算符。下面主要介绍其他与 C 不同的部分。
-
逻辑右移运算符
>>与算术右移运算符>>>它们的区别主要在于前者在最高位补 0,而后者在最高位补符号位。
-
相等比较运算符
==与===和!=与!====和!=可能由于不定值x和高阻值z的出现导致结果为不定值x,而===和!==的结果一定是确定的 0 或 1(x与z也参与比较)。 -
阻塞赋值
=和非阻塞赋值<=不同于
assign语句,这两种赋值方式被称为过程赋值,通常出现在initial和always块中,为reg型变量赋值。这种赋值类似 C 语言中的赋值,不同于assign语句,赋值仅会在一个时刻执行。由于 Verilog 描述硬件的特性,Verilog程序内会有大量的并行,因而产生了这两种赋值方式。这两种赋值方式的详细区别我们会在之后的小节内介绍,这里暂时只需记住一点:为了写出正确、可综合的程序,在描述时序逻辑时要使用非阻塞式赋值<=。 -
位拼接运算符
{}这个运算符可以将几个信号的某些位拼接起来,例如
{a, b[3:0], w, 3'b101};;可以简化重复的表达式,如{4{w}}等价于{w,w,w,w};还可以嵌套,{b, {3{a, b}}}等价于{b, {a, b, a, b, a, b}},也就等价于{b, a, b, a, b, a, b}。 -
缩减运算符
运算符
&(与)、|(或)、^(异或)等作为单目运算符是对操作数的每一位汇总运算,如对于reg[31:0] B;中的B来说,&B代表将B的每一位与起来得到的结果。
宏定义
- ``define 标识符(宏名) 字符串(宏内容)`
- 引用宏名时也必须在宏名前加上符号 `
|
integer 型
- 同
int型,默认为有符号数
parameter 型
-
常量,用于在模块实例化时传递参数
-
parameter 标识符 = 表达式;如:parameter width = 8; -
在模块内部使用,parameter可以被重新赋值
wire
线网
nets型数据
- 一般使用
assign语句对wire型数据进行驱动 - 要按照组合逻辑的规则进行操作。如,对于
wire型变量a,assign a = a + 1是不合法的 - 一般在使用
wire型数据前应先声明它。但如果在模块实例的端口信号列表中使用了一个未声明的变量,则会将其默认定义为 1 位的wire变量
reg
寄存器数据类型
reg型变量不能使用assign赋值- 访问存储器型数据,可以用类似于位选择操作的方式。如
mem[2]就是访问mem中的第 3 个元素 - 信号定义好之后,不仅决定了位宽还决定了方向,在使用中只能正向接,不能反向接
reg型和always关键字配合,建模组合逻辑
eg.
module ext( |
数字字面量
<位宽>'<进制><值>,如10'd100- 二进制(b 或 B)、八进制(o 或 O)、十六进制(h 或 H)、十进制(d 或 D)
- 默认位宽:32位;默认进制:十进制;前导0可以省略;
- 值部分可以用下划线分开提高可读性,如
16'b1010_1011_1111_1010、
两个特殊的值:
x和z
x为不定值,当某一二进制位的值不能确定时出现,变量的默认初始值为x。
z为高阻态,代表没有连接到有效输入上。
数据拼接与拆分
- 拼接
reg [15:0] regA; |
- 拆分
wire[15:0] A; |
有符号数$signed()
wire、reg等数据类型默认是无符号的- 用 **
$signed()**使之成为有符号数 - Verilog 会自动地做数据类型匹配,将符号数向无符号数转化

阻塞赋值 = 和非阻塞赋值 <=
- 当前一句阻塞赋值完成后(即
=左边的变化为右边的值后),下一条阻塞赋值语句才会被继续执行 - 处在一个
always块中的非阻塞赋值是在块结束时同时并发执行的,在描述时序逻辑时要使用非阻塞式赋值<=
逻辑右移运算符 >> 与算术右移运算符 >>>
- 它们的区别主要在于前者在最高位补 0,而后者在最高位补符号位。
相等比较运算符 == 与 === 和 != 与 !==
==和!=可能由于不定值x和高阻值z的出现导致结果为不定值x===和!==的结果一定是确定的 0 或 1(x与z也参与比较)。
缩减运算符
- 运算符
&(与)、|(或)、^(异或)对操作数的每一位进行相应运算 - 如对于
reg[31:0] B;中的B来说,&B代表将B的每一位与起来得到的结果。
组合逻辑建模
assign
assign语句不能在always和initial块中使用assign语句经常与三目运算符配合使用建模组合逻辑- 在一个时钟周期里,对一个
wire型变量,只能assign一次
法一:
用assign语句和三目运算符配合,建模组合逻辑
module ALU( |
法二:
reg型和always 关键字配合,建模组合逻辑
module ext( |
时序逻辑建模
always
always之后紧跟@(...),表示当括号中的条件满足时,将会执行always之后的语句
always @(posedge clk) // 表示在 clk 上升沿触发后面的语句块 |
posedge,上升沿触发
negedge,下降沿触发每个条件使用逗号
,或or隔开,只要有其中一个条件被触发,always之后的语句都会被执行
always之后紧跟@ *或@(*),则表示对其后紧跟的语句或语句块内所有信号的变化敏感。这种用法主要用于与reg型数据和阻塞赋值配合,建模组合逻辑。
同步复位与异步复位
同步复位:
always@(posedge clk)begin |
异步复位:
always@(posedge clk or posedge reset)begin |
initial 块
- 对
reg型变量的取值进行初始化
reg a; |
if 语句
- 只能出现在
always块内
always @ * begin |
case 语句
- 和C略有不同
always @(posedge clk) begin |
for 语句
integer和reg类型的变量均可作为循环变量
module test( |
时间控制语句
#3; // 延迟 3 个时间单位 |



