Verilog学习

Verilog学习

基于《HDL数字系统设计与实践》一书的Verilog学习

verilog HDL 层次化设计

模块和端口

  1. 模块=模块名定义+端口描述+内部功能逻辑
  2. 逻辑功能描述:变量声明、数据流描述语句、门级实例化描述语句、行为描述语句、任务与函数。
  3. 关键词:module定义模块名字;inputoutput指定端口方向;endmodule结束模块描述。
  4. 模块定义格式:
    1
    2
    3
    4
    module 模块名
    端口定义
    ......
    unmodule
  5. 端口定义:有两种格式
    • 普通风格
      1
      2
      3
      4
      module 模块名
      input [位宽-1: 0] 端口名1,端口名2  
      output [位宽-1: 0] 端口3
      inout [位宽-1: 0]端口4
    • ANSI C风格
      1
      2
      3
      4
      5
      module 模块名
      ( input [位宽-1: 0] 端口名1,端口名2  
      output [位宽-1: 0] 端口3
      inout [位宽-1: 0]端口4
      );
  6. 实例化:不允许嵌套,模块之间只能通过实例化

层次化设计思想

  • 自顶向下
    类似于根据一张图纸造物
  • 自底向上
    类似于根据材料造物,可以是已知也可以是未知的

Testbench概念

用于测试设计的电路的功能是否正常

verilog HDL基本语法

词法约定

  1. 空白符:空格\b,制表\t和换行
  2. 注释:单行//,多行*/.../*(不允许嵌套)
  3. 操作符:单目操作符~,双目操作符+,三目操作符?
  4. 标识符:
    • 第一个字符是字母或下划线
    • 区分大小写
    • 转义\
    • 空白符结束
  5. 关键字:均为小写

数据类型

– 线网:除trireg所有网线类型不能存储数据值
– 变量:抽象的数据存储单元

  1. 逻辑值与常量
  • 基本值:0/1真假,x未知,z高阻(不分大小写)
  • 整数:二进制、十进制、十六进制、八进制
    格式:[位宽]'[进制][数值]
    负数:加负号
  • 实数:十进制和科学计数法(可用e或E表示)
  1. 逻辑强度:supply最强,highz最弱
  • 线网类型:wiretri最常见
    格式:wire [7:0] datain, dataoutt(*两个8位的wire数据)【这是举例】
  • 变量类型:reg类型、integer型、real型、time
  • 向量:位宽大于1
  • 数组:数组中的每一个元素可以是标量也可以是向量
  • 参数:defparam语句(同样不可嵌套)
  1. 表达式
  • 操作数
  • 算术操作符:单双目操作数。单目+ -表正负,双目* / + -运算符号
  • 逻辑操作符:逻辑与&&,逻辑或||,逻辑非!
  • 关系操作符:> < >= <=
  • 相等操作符:逻辑相等==,逻辑不等!=,逻辑全等===,逻辑非全等!==
  • 按位操作符:反~,与&,或|,异或^,同或^~, ~^
  • 缩减操作符:缩减与&,缩减与非-&,缩减或|,缩减或非~|,缩减异或^,缩减同或~^, ^~
  • 位移操作:右移>>,左移<<,算术左移<<<,算术右移>>>
  • 拼接操作符:格式{操作1,操作2,操作3,...,操作n}
  • 条件操作符:根据条件表达式的值从两个表达式中选择一个表达式作为输出结果,格式条件表达式? 真表达式 : 假表达式

Verilog HDL行为描述

Verilog HDL基本描述形式

  1. 数据流描述方法
    1
    assing [延迟] wire类型 = 表达式
  2. 行为描述方式
    两种语句:initial和always
    1
    2
    3
    4
    5
    6
    7
    always @(事件控制列表) begin
    ···
    end

    intial begin
    ···
    end
  3. 层次化描述方式
    1
    模块名 实例名

结构化过程语句

initial和always语句不能相互嵌套使用

  1. initial语句
    从仿真的0时刻开始,只执行一次。
    1
    2
    3
    4
    5
    6
    7
    8
    reg a, b, c; //initial中只有一条赋值语句
    initial
    a = 1'b0;
    //若含多条赋值语句,需要begin end
    initial begin
    b = 1'b0;
    c = 1'b0;
    end
  2. always语句
    从仿真的0时刻开始,会重新执行命令
    1
    2
    3
    4
    reg a;
    initial a = 0;
    always #50 a =-a;
    //从0开始,每隔50个时间单位的反复操作

顺序块和并行块

  1. 顺序块:按书写顺讯依次执行
  2. 并行块:fork是同时开始
  3. 块语句的其他特点
    • 嵌套块
    • 命名块

过程赋值语句

  1. 阻塞赋值语句
    =作为赋值符,按顺序执行
    1
    2
    3
    4
    5
    6
    7
    reg a, b;
    initial begin
    a = 1'b0;
    b = 1'b0;
    #10 a = 1'b1; //阻塞赋值语句对a赋新值,a变为1后才继续执行后面的命令
    b = a;
    end
  2. 非阻塞赋值语句
    <=作为赋值符,不会阻塞同一个块语句中的其他语句的执行
    1
    2
    3
    4
    5
    6
    7
    reg a, b;
    initial begin
    a = 1'b0;
    b = 1'b0;
    #10 a <= 1'b1; //用非阻塞赋值语句赋值,赋值还未完成先完成后面的语句
    b <= a; //由于a的赋值未完成,所以b的值还是0
    end

条件语句

有三种情况,if表达式,if-else表达式,if-else if表达式。
注意,这里容易弄混,一定要搞清楚。

多路分支语句

当分支特别多时,if else语句非常不好用,就可以使用case语句
case语句的关键词:case default endcase 格式如下

1
2
3
4
5
6
case(表达式)
分支表达式1: 语句1
分支表达式2: 语句2
···
default: 默认语句
endcase

条件语句和多路分支语句的比较

if...else语句有优先级,而case语句没有,是并行的关系。

循环语句

  1. while循环
    1
    2
    while(条件表达式)
    语句:
    当表示式为真时,则循环执行里面的雨具;如果为假,中止循环并跳出while。如果while中表达式的值为x或z时,当作假处理。
  2. for循环
    1
    for(循环变量初值;循环结束条件;循环变量增值)
    在for中,C语言有i++,而verilog没有,改成i=i+1
  3. repeat循环
    1
    repeat(循环次数表达式)
  4. forever循环

时序控制

  1. 延迟控制
    分为常规和内嵌,两者区别为:常规是整个语句执行后,在推迟的时间后,赋值语句开始计算;内嵌是开始执行时刻就立即计算表达式右边的值,此值会一直保持至延迟结束。
  • 常规延迟
    # [延迟值] 语句
  • 内嵌延迟
    语句 #[延迟值]
  1. 事件控制
    发生某个事件(变量、线网信号或表达式的值发生变化)之后,整个逻辑发生变化
  • 边沿敏感事件控制
    用边缘敏感符号@,格式为
    1
    2
    @ 事件
    语句;
    这里有个关键字:posedge(上升沿跳变)/negedge(下降沿跳变) 记住即可
    如果出现了多个敏感事件,则用or或者,即可
  • 电平敏感事件控制
    以信号值变化的边沿为标志的,即一定要达到指定信号的某个值的变化边沿才会触发执行,关键字为wait
    1
    wait(事件) 语句;
    当事件为真时,后面的语句才会执行
  • 时序控制语句在综合代码中的应用
    在进行电路综合时,延迟控制语句会被综合工具自动忽略,当作无延迟处理

组合逻辑建模

数字电路建模方式

电路与代码的一一对应关系

组合逻辑的门级描述

与门(and)、与非门(nand)、或门(or)、或非门(nor)、异或门(xor)、同或门(xnor)、缓冲器(buf)、非门(not)、三台缓冲器控制信号低电平有效(bufif0)、三台缓冲器控制信号高电平有效(bufif1)、三态非门控制信号低电平有效(notif0)、三态非门控制信号高电平有效(notif1)

组合逻辑的数据流描述

  1. 连续赋值语句
    assign[延迟]wire型变量=表达式;

  2. 数据流描述
    利用数据流描述可以很方便的描述一个加法器等,用符号就可以了,比如+ *

组合逻辑的行为描述

组合逻辑建模实例

  1. 比较器
  2. 译码器和编码器

时序逻辑电路

时序逻辑建模概述

时序逻辑电路指的是在verilog HDL所描述的电路中,包含一个或多个存储单元。这些存储单元可以是边沿触发的寄存器,或者是电平触发的锁存器。由于引入了存储单元,时序逻辑电路具有了“记忆”功能,可以记录当前时刻之前的输入激励情况及电路状态。

寄存器和锁存器设计

  1. 寄存器实例
    always后面的敏感列表中加入边沿敏感的信号,即可设计出一个简单的寄存器。posedge是在时钟的上升沿触发并采集数据端口的值;negedge则在下降沿触发。
1
2
3
4
5
6
7
8
9
10
11
module dff
(
input i_clk,
input i_din,
output reg o_dout
);

always@(posedge i_clk) //在always语句的敏感列表@()中加入边沿敏感的时钟信号i_clk
o_dout <= i_din;

endmodule
  1. 锁存器的设计实例
    用verilog描述一个的锁存器,该锁存器在控制信号i_en为高电平时开启,为低电平时锁存器当前值
1
2
3
4
5
6
7
8
9
10
11
12
module latch
(
input i_en,
input i_din,
output reg o_dout
);

always@(i_den or i_en) //敏感列表中没有边沿触发的信号
if(i_en)
o_dout <= i_din;

endmodule

寄存器和锁存器的推断

  1. 寄存器的推断
  • 寄存器是一种时序元件,用于存储数据,并且在时钟信号的上升沿或下降沿触发时更新数据。
  • 寄存器在时钟边沿触发时,将其输入数据传递到输出,具有确定的时序行为。
  • 在Verilog中,通常使用触发器(Flip-Flops)来实现寄存器。
1
2
3
4
5
6
7
8
9
10
11
12
//举例说明
module DFF (
input wire D, // 数据输入
input wire CLK, // 时钟信号
output wire Q // 数据输出
);

always @(posedge CLK) begin
Q <= D;
end

endmodule
  1. 锁存器的推断
  • 锁存器是一种组合逻辑元件,它不需要时钟信号,而是根据输入信号的变化实时更新输出。
  • 锁存器不是时序元件,通常应该避免在数字电路中使用锁存器,因为它们可能会导致不稳定的行为和时序问题。
  • 锁存器可能会导致冒险条件,因此在设计中应该谨慎使用。
1
2
3
4
5
6
7
8
9
10
//举例说明
module SRLatch (
input wire S, // 设置输入
input wire R, // 复位输入
output wire Q // 数据输出
);

assign Q = S & ~R;

endmodule

存储器的设计与建模

  1. ROM建模
    ROM是只读存储器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//变量z以数组的方式描述了一个ROM
//由于使用了inital语句,因此该代码只能适用于仿真,不能综合
module rom_sim
(
input [2:0] i_sel,
output [3:0] o_dat
);

reg [3:0] rom [0:7]; //通过数组声明存储器变量

inital begin //利用inital语句为ROM赋值
rom[0] = 4'b1001;
rom[1] = 4'b1011;
rom[2] = 4'b0010;
rom[3] = 4'b0011;
rom[4] = 4'b1110;
rom[5] = 4'b0000;
rom[6] = 4'b0000;
rom[7] = 4'b0000;
end

assign o_dat = rom[i_sel];
endmodule
  1. RAM建模
    一个电平变化触发的16位宽RAM模型通常是基于时序逻辑的,使用触发器来存储数据。以下是一个简单的电平变化触发的16位宽RAM模型示例,使用2个16位触发器作为存储元件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module LevelSensitiveRAM16 (
input wire [3:0] address, // 4位地址输入
input wire [15:0] data_in, // 16位数据输入
input wire write_enable, // 写使能信号
input wire read_enable, // 读使能信号
output wire [15:0] data_out // 16位数据输出
);

reg [15:0] memory [0:15]; // 16个16位触发器,模拟RAM的存储单元

always @(posedge write_enable or posedge read_enable) begin
if (write_enable) begin
// 写操作:将输入数据写入存储器指定地址
memory[address] <= data_in;
end
if (read_enable) begin
// 读操作:从存储器指定地址读取数据
data_out <= memory[address];
end
end

endmodule

同步有限状态机

同步有限状态机(Synchronous Finite State Machine,SFSM)是一种在特定时钟信号下运行的状态机,其中状态的转换和输出的更新都与时钟信号同步。SFSM 包括状态寄存器、组合逻辑块以及时钟信号。

以下是一个简单的 2 状态同步有限状态机的示例,其状态转换和输出更新是同步的:

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
31
32
33
34
module SynchronousFSM (
input wire clk, // 时钟信号
input wire rst, // 复位信号
output wire [1:0] state, // 2位状态输出
output wire output // 输出信号
);

reg [1:0] current_state; // 当前状态寄存器

always @(posedge clk or posedge rst) begin
if (rst) begin
// 复位操作
current_state <= 2'b00; // 将状态初始化为00
end else begin
// 状态转换逻辑
case (current_state)
2'b00: current_state <= 2'b01; // 从00转换到01
2'b01: current_state <= 2'b10; // 从01转换到10
2'b10: current_state <= 2'b00; // 从10转换到00
endcase
end
end

assign state = current_state; // 输出当前状态

always @(posedge clk) begin
// 输出逻辑
case (current_state)
2'b00: output <= 1'b0; // 当前状态为00时输出0
default: output <= 1'b1; // 其他状态时输出1
endcase
end

endmodule

时序逻辑建模实例

  1. 计数器
  2. 串并/并串转换器
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
31
32
33
34
module p2s(clk,en,rsy,pin,sout);
input clk,en,rst;
input [7:0] pin;
output sout;
output end; #并转串传输结束

reg[2:0] cnt;
reg[7:0] data;

always@(posedge clk or posedge rst)
if(rst)
sout <= 1'b0;
cnt <= 3'b0;
end
else if(en) begin
cnt <= cnt+1'b1;
sout <= data[0];
end

always@(posedge clk or posedge rst)
if(rst)
data <= 8'b0;
else
if(cnt==3'b0)
data <= pin;
else
data <= {1'b0,data[7:1]};

always@(posedge clk or posedge rst)
if(rst)
end <= 1'b0;
else
end <= (cnt==3'b111);
endmodule
  1. 时钟分频电路

练习题

5.4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module 8bit(
input wire clk,
input wire rst,
input wire [7:0] data_in,
output wire [7:0] data_out
);

reg [7:0] reg_data;

always @(nesedge clk or posedge rst)begin
if (rst)
reg_data <= 8'b0;
else
reg_data <= date_in;
end
end

assign data_out = reg_data;

endmodule

5.6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module 32_RAM(clk,wr,addr,rdata,wdata);
input clk,wr;
input [31:0] wdata;
input [14:0] addr;
output [31:0] rdata;

reg [31:0] ram[32767:0]

always@(posedge clk)
if(wr)
ramp[addr] <= wdata;
else

assign rdata =ram [addr];
endmodule

5.7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module 32_RAM(clk,wr,addr,data);
input clk,wr;
inout [31:0] wdata;
inout [14:0] addr;

reg [31:0] ram[32767:0]

always@(posedge clk)
if(wr)
ramp[addr] <= wdata;
else
data ram[addr]
assign rdata =ram [addr];
endmodule

5.11

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
module fsm(clk,rst_n,sel.hready,write,burst,resp_ok)
input clk,rst_n,sel,hready,write,burst,resp_ok;
parameter Reset=3'b000;
parmeter Idle=3'b001;
parmeter Addr=3'b010;
parmeter Write=3'b011;
parmeter Read=3'b100;
parmeter BurstWrite=3'b101;
parmeter BurstRead=3'b110;
parmeter Resp=3'b111;

reg [2:0] next_state.current_state;
always@(posedge clk or negedge rst_n)
if(!rst_n)
current_state <= Reset;
else
current_state <= next_state;

always@(rst_n or sel or hready or wirte orburst or resp_ok)
case(current_state)
Reset: if(rst_n) next_state =Idle;
Idle: if(sel&&hready) next_state = Addr;
Addr: if(write&&burst) next_state = Burstread;
else if(!write&&burst) next_state = burstread;
else if(!write&&burst) next_state = Read;
else next_state = Write;
......

endmodule

行为级仿真模型建模

组合逻辑和时序逻辑建模可以通过门级描述数据流描述行为描述来实现,用数据流或行为级语法进行可综合的电路描述,被称为RTL描述,即寄存器传输级描述。而利用门级语法进行描述的,通常称为结构级描述或层次化描述。

概述

verilog提供的时序控制语句主要有3种:延迟控制语句,事件控制语句和条件等待语句。

仿真时间和时序控制

事件控制语句指利用语法@()进行描述,@后面的括号里包含需要的语句

仿真模型建模实例

  1. 时钟发生器

  2. 简单的仿真环境

  3. 从文件读取激励

  4. 输出结果监控
    当调用$monitor任务时,参数列表中指定的变量都将被仿真器控制。

  5. 总线功能模型
    总线功能模型(BFM)通常是某个总线主设备的接口电路模型
    SoC芯片指在单个芯片上集成一个完整的数字电路系统

各层次verilog HDL描述形式与电路建模

基本的数字单路单元模块

各抽象层次的verilog HDL描述形式

verilog HDL的描述形式可概括分为:
门级描述(或称层次化描述):and nl(out, opa, opb);
数据流描述:assign out=opa&opb;
行为描述:

1
2
3
always@(opa or opb)begin
out=opa&opb;
end
  1. 利用各层次描述进行组合逻辑建模
    数据流转换为行为描述的步骤
    • 将数据流描述中连续赋值语句左边的变量定义为 reg 类型;
    • 利用 always 模块描述组合逻辑,将连续赋值语句等号右边的所有信号都加到 always语句后面的变量敏感列表中,且所有信号都是电平敏感的;
    • 在 always 模块中利用阻塞赋值语句对左边变量进行赋值,赋值语句等号右边的表达式与连续赋值语句右手边的表达相同。
  2. 利用各层次描述进行时序逻辑建模
  3. 利用各层次描述进行行为仿真逻辑建模

任务与函数

任务语句说明

语法定义:

1
2
3
4
task 任务名;
端口声明和变量定义;
一个或多个过程语句;
endtask

任务的调用语法:任务名 (参数1, 参数2, 参数3, ...);

$time 系统函数将返回当前仿真时间

任务和函数的联系和区别

  1. 函数不能包含时序控制语句,如@()、#10 等;
  2. 在函数中不能调用任务,而任务可以调用其他任务和函数;
  3. 函数必须包含至少一个端口;
  4. 函数必须返回一个值,而任务不能返回值。

$monitor任务

$monitor任务可以用来监控并打印任何指定的变量或表达式

编译预处理

编译指令实现的编程预处理功能

‘define, ‘undef

‘define指令用于定义文本替换;
‘undef指令则是取消前面定义的宏

‘ifdef, ‘else, ‘elsif, ‘endif, ‘ifndef

工程师会根据某些条件选择性地编译某部分代码,同时选择性地忽略掉一些代码,而实现这样功能的编程指令就是条件编译指令。

verilog HDL设计与综合中的陷阱

阻塞语句和非阻塞语句

  1. 阻塞语句
    阻塞语句用操作符号 = 进行连接,起基本语法格式为
    寄存器变量(reg) = 表达式/变量

    阻塞语句的含义:

    • 在多个阻塞语句顺序出现,出现的语句会完全阻塞后面的动作,直到前面的语句被执行结束,即执行的顺序性;
    • 在赋值钱不能插入任何动作
  2. 非阻塞语句
    非阻塞语句用操作符号 <= 进行连接,起基本语法格式为
    寄存器变量(reg) <= 表达式/变量

规则

  1. 当描述时序逻辑时,用非阻塞语句;
  2. 当描述组合逻辑时,用阻塞语句;
  3. 当在一个always模块中同时描述组合逻辑和时序逻辑时用非阻塞语句;
  4. 在同一个always模块中不要混合使用阻塞和非阻塞语句。

锁存器的产生与危害

无意识锁存器产生的主要原因有两个:

  • 在设计组合逻辑中使用不完整的条件判断语句,即有if没有else,或是在设计组合逻辑中使用不完整的case语句;
  • 设计中使用到了组合逻辑反馈等异步逻辑。

组合逻辑反馈

规则:

  1. 当描述组合逻辑时,禁止使用组合逻辑反馈;
  2. 当需要描述反馈环路时,必须使用边沿触发。

复位电路设计问题与改进

  1. 同步复位电路
    同步复位的好处:

    • 保证整个系统是一个完全同步的系统;
    • 复位信号只在时钟的有效边沿处才能复位寄存器,在一定程度上,滤除了复位信号上的毛刺;
    • 在某些设计中,复位信号由一系列条件产生,这时同步复位是一种有效的方法,因为同步复位能够滤除组合逻辑在时钟信号之间的毛刺;

    同步复位的坏处:

    • 同步复位需要时钟参与,复位信号有效期间如果没有工作时钟,则不能复位;
    • 同步复位增加了组合逻辑的时延,降低了设计的速度。
  2. 异步复位电路
    异步复位的好处:

    • 直接利用内部寄存器的硬复位引脚来复位寄存器;
    • 复位信号不再参与到组合逻辑电路中,不影响逻辑的速度;
    • 不需要时钟参与。

    异步复位的坏处:

    • 复位信号上的毛刺很容易导致系统的异常复位;
    • 异步复位带来的最大问题就是异步,无论是寄存器复位还是复位的释放都是异步的。异步复位阶段一般没有问题,问题出在复位信号的释放阶段。如果复位信号正好在时钟沿附近释放,寄存器的输出将出现亚稳态。
作者

Hau uhang

发布于

2023-09-19

更新于

2025-04-06

许可协议

评论