verilog
原文链接:https://blog.csdn.net/jac_chao/article/details/123744724
标识符
- 用途: 用于定义常数、变量、信号、端口、子模块或参数名称。
- 组成:字母、数字、$、下划线任意组合而成
- 注意事项:
- 区分大小写
- 第一个字符是字母或者下划线
逻辑值
- 0: 低电平
- 1:高电平
- x: 表示状态未知
- z:表示高阻状态
注意:Z,X不区分大小写。(例如0z1x和0Z1X表示同一个数据)
逻辑运算
(1) 逻辑运算符:&&(与)、==(相等)、||(或)、!=(不等)
- 如 m&&n : 判断m和n是否全为真(非0即为真),真则输出1’b1,否则输出1’b0 (4’b1010&4’b0101 = 1’b1)
- 最后输出结果只有1bit
(2) 按位运算符: &、|、~、^、~&、~^、~|
- 如 m&n : 是把m的每一位与n的每一位按位做与运算 (4’b1010&4’b0101 = 4’b0000)
- 输出结果与m/n的bit数相同
(3) 归约运算符: &、|、~、^、&、~^、~|
- 只有一个参量参与运算时( &为一元运算符),表示规约与,即向量内部进行与运算
1 | &a [3:0] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf) |
- 即(&4’b0101 = 0&1&0&1 = 1’b0 )
- 最后输出结果只有1bit
常量
与C语言类似,常量主要有:整数型、实数型和字符串型三种
(1)整数
用十进制表示常量
- 直接写18表示位宽为32bit的十进制数18;
- -15表示十进制的-15,用二进制补码表示至少需要5bit,即1_0001,最高一位为符号位;如果用6bit表示,则为11_0001,同样最高一位为符号位。
用基数表示法表示(写法清晰,更推荐)
基本格式
1
[换算为二进制后位宽的总长度][’][数值进制符号][与数值进制符号对应的数值]
- 8’hab表示8bit的十六进制数,换算成二进制是1010_1011;
- 8’d171表示8bit的十进制数,换算成二进制是1010_1011;
- 8’o253表示8bit的八进制数,换算成二进制是1010_1011;
- 8’b1010_1011表示8bit的二进制数,二进制就是1010_1011。
注意事项:
- 当表示二进制时,最好每4位写一个下划线以增强可读性:如8’b1000_1100 与8’b10001100 是一样的
- 基数表示法中遇到x时:十六进制表示4个x,八进制中表示3个x
- 当位宽大于二进制位数时左边自动补0,小于二进制数时2从左边截断!
(2) 字符串
每个字符由1个8位的ASCII码值表示,即需要1byte存储空间
如:“Hello world” 字符串由11个ASCII符号构成,需要11byte存储空间
变量
- 线网型(wire):表示电路间的物理连接;
- 寄存器型(reg):Verilog中一个抽象的数据存储单元
注意事项:
- 凡是在always或initial语句中被赋值的变量(赋值号左边的变量),不论表达的是组合逻辑还是时序逻辑,都一定是reg型变量;
- 凡是在assign语句中被赋值的变量,一定是wire型变量。
参数
和C的静态常量类似
(1) 参数是一种常量,通常出现在module内部,常被用于定义状态、数据位宽等
1 | parameter STATE = 1'b0; |
(2) 只作用于声明的那个文件,且可以被灵活改变!
(3) 局部参数localparam,只在本模块中使用
1 | localparam STATE= 1'b1’; |
(4) 参数的名称一般为大写,以区分其他变量
注意:
- 实例化时,参数可修改
1 | parameter IDLE = 3’b001; |
向量
vector(向量),是一组信号的集合,可视为位宽超过1bit 的 wire 信号。
(1) 定义方式
1 | 格式:input/output wire/reg [upper:lower] vector_name |
- [upper:lower] 定义位宽,如 [7:0] 表示位宽为8 bit ,即upper=7,lower=0
- vector_name可以一次写多个向量
(2)向量片选
- a[3:0] 取向量a的0~4位数据
- b[n] 取向量b的第n位数据
- c[-1:-2] 取向量c的最低2位数据
- c[0:3] 取向量c的最高4位数据
多路选择器应用:实现一个 256 选 1 选择器,sel 信号作为选择信号,当 sel = 0 时选择 in[3:0],sel = 1 时选择 in[7:4],以此类推。
1 | module top_module ( |
- 片选信号sel输入为n位二进制数,当参与运算、充当索引时会自动转换成十进制数
- 该题所选取的信号片段为: in[sel4+3: sel4] ,但这不符合Verilog的片选语法规则故应写成:
in[sel4 +: 4] 表示索引从sel4开始的高4bit信号
in[sel4+3 -: 4] 表示索引从sel4+3开始的低4bit信号 - 或是直接选出需要的每一位,再用{ }拼接成新向量:
{in[sel4+3], in[sel4+2], in[sel4+1], in[sel4+0]}
赋值语句
阻塞赋值
1
2
3
4
5
6
7
8
9//阻塞赋值
// 组合块
always @(*) begin
out1 = a ;
a = b ;
out2 = a ;
end
//组合always块中用阻塞式赋值
//执行顺序:按照begin_end语句块中的顺序依次执行,上述输出结果为:out1 = a ,out2 = b
非阻塞赋值
1
2
3
4
5
6
7
8
9//非阻塞赋值(<=)
// 时序块
always @(posedge clk) begin
out1 <= a ;
a <= b ;
out2 <= a ;
end
//时序always块中用非阻塞赋值
//执行顺序:begin_end中所有语句并行执行,上述输出结果为:out1 = a ,out2 = a
三目运算符
1 | condition ? if_true : if_false |
当条件为真,表达式值为if_true ,否则表达式值为if_false。
以选择器为例:
1 | assign out = (sel == 1'b1) ? in1 : in2; |
分支语句
if
1
2
3
4
5
6
7if(<条件表达式 1>)
语句或语句块 1;
else if(<条件表达式 2>)
语句或语句块 2;
………
else
语句或语句块 n;case
1
2
3
4
5
6
7
8
9case(<控制表达式>)
<分支语句 1> : 语句块 1;
<分支语句 2> : 语句块 2;
<分支语句 3> : 语句块 3;
………
<分支语句 n> : 语句块 n;
default : 语句块 n+1;
endcase
for循环
1 | integer i; |
运算符
关系运算符
>、<、>=、<=
- 运算结果为真返回 1
- 运算结果为假返回 0
- 若某个操作数值不定(x),则返回值为 x
拼接运算符
用一对花括号加逗号组成“{ , }”拼接运算符,逗号隔开的数据按顺序拼接成新数据!
1
2
3
4wire [1:0] a;
wire [3:0] b;
wire [5:0] c;
wire [11:0] d = {a, b, c}移位运算符
移位运算符用于将左边操作数左移或右移指定的位数!移位后空闲位用0填充。
左移运算符: <<
如: 4‘b1101 << 3 结果为:4‘b1000
右移算法符: >>
如: 4‘b1101 >> 3 结果为:4‘b0001
移位运算符其他用途:左移一位可以看成是乘以 2,右移一位可以看成是除以 2。
移位运算符代替乘除法可以节省资源!
模块
(1) 介绍(和main类似)
1 | module top_module( |
(2) 模块实例化
1 | mux2_1 mux2_1_inst |
- 按mod_a定义的端口顺序实例化: mod_a instance1 (a, b, out);
- 按mod_a端口名实例化: mod_a instance2 (.in1(a), .in2(b), .out(out)); (推荐此种写法)
逻辑块
always
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 2.1always逻辑块
// always块可构建 组合逻辑块 和 时序逻辑块,复杂的逻辑操作都需要处于该逻辑块中,如if、case、for等
// 2.1.1组合逻辑块
// always逻辑块中任意信号变化时立即触发,执行begin - end之间的语句
// begin - end用于将多条语句组成一个代码块,只有一条语句时可省略
module top_module();
always @(*) begin
//....
end
endmodule
// 2.1.2 时序逻辑电路
// clk 信号的上升沿触发
// posedge: 上升沿
// negedge: 下降沿
module top_module();
always @(posedge clk) begin
// ....
end
endmodulegenerate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 2.2 generate逻辑块
/*
generate主要结合for循环使用,主要用途有:
对向量中的多个位进行重复操作
对同一个模块进行多次重复实例化(主要用途)
*/
// 2.2.1 操作向量
module top_module(input [7:0] in, output [7:0] out);
genvar i; // genvar i; 也可以定义在generate内部
generate
for(i=0; i<8; i++) begin: bit
assign out[i]=^in[8-1:i];
end
endgenerate
endmodule
// 模块重复多次实例化
module top_module(
input a,
input b,
output out
);
genvar i;
generate
for(i=0; i<8; i++) begin: gen_mod_a // gen_mod_a 为每个begin_end的结构的名称
mod_a instance2 (.in1(a), .in2(b), .out(out));
end
endgenerate
endmodule
initial块
1
2
3
4
5
6
7
8
9
10// initial块
// initial块可以理解为一个初始化块,在initial的起始位置的语句在0时刻即开始执行,之后如果遇到延时,则延时之后执行接下来的语句。
// 初始块是不可综合的,因此不能将其转化为带有数字元素的硬件原理图。因此初始块除了在仿真中使用外,并没有太大的作用。
initial
begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
#50
sys_rst_n = 1'b1;
end
系统函数
`timescale 1ns/1ns //时间尺度预编译指令 时间单位/时间精度
仿真中使用“#数字”表示延时相应时间单位的时间,例#10表示延时10个单位的时间,即10ns。
1
2
3always #10 in_1 <= {$random} % 2;
always #10 in_2 <= {$random} % 2;
always #10 sel <= {$random} % 2;时间单位不能比时间精度小
$display //打印信息,自动换行
$display(“%b+%b=%d”,a, b, c);//格式“%b+%b=%d” 格式控制,未指定时默认十进制
%h或%H //以十六进制的形式输出
%d或%D //以十进制的形式输出
%o或%O //以八进制的形式输出
%b或%B //以二进制的形式输出
$write //打印信息
$write(“%b+%b=%dn”,a, b, c); //“%b+%b=%dn” 格式控制,未指定时默认十进制
%h或%H //以十六进制的形式输出
%d或%D //以十进制的形式输出
%o或%O //以八进制的形式输出
%b或%B //以二进制的形式输出
\n //换行
$strobe //打印信息,自动换行,最后执行
$strobe(“%b+%b=%d”,a,b,c); //“%b+%b=%d” 格式控制,未指定时默认十进制
%h或%H //以十六进制的形式输出
%d或%D //以十进制的形式输出
%o或%O //以八进制的形式输出
%b或%B //以二进制的形式输出
$monitor //监测变量
$monitor(“%b+%b=%d”,a,b,c); //“%b+%b=%d” 格式控制,未指定时默认十进制
%h或%H //以十六进制的形式输出
%d或%D //以十进制的形式输出
%o或%O //以八进制的形式输出
%b或%B //以二进制的形式输出
1
2
3
4
5
6
7
8
9
10always #10 in_1 <= {$random} % 2;
always #10 in_2 <= {$random} % 2;
always #10 sel <= {$random} % 2;
initial
begin
$timeformat(-9,0,"ns",6);
$monitor("@time %t:in_1=$b in_2=%b sel=%b out=%b",$time,in_1,in_2,sel,out);
end仿真结果(部分)
1
2
3
4
5
6
7
8
9
10
11# @time 0ns:in_1=$b in_2=0 sel=0 out=00
# @time 10ns:in_1=$b in_2=0 sel=1 out=10
# @time 20ns:in_1=$b in_2=1 sel=1 out=11
# @time 30ns:in_1=$b in_2=1 sel=0 out=11
# @time 60ns:in_1=$b in_2=0 sel=1 out=01
# @time 70ns:in_1=$b in_2=1 sel=1 out=01
# @time 80ns:in_1=$b in_2=1 sel=0 out=00
# @time 90ns:in_1=$b in_2=0 sel=1 out=01
# @time 100ns:in_1=$b in_2=1 sel=1 out=11
# @time 110ns:in_1=$b in_2=1 sel=0 out=00
# @time 120ns:in_1=$b in_2=0 sel=0 out=10
$stop //暂停仿真
$finish //结束仿真
$time //时间函数
$random //随机函数
$readmemb //读文件函数




