[蔡觉平老师主讲] Verilog HDL数字集成电路设计原理与应用

一、基本概念

1.1 硬件描述语言

硬件描述语言,Hardware Description Language (HDL)。利用HDL,可以根据电路结构的特点,采用层次化的设计结构,将抽象的逻辑功能用电路的方式进行实现。之后通过EDA(电子设计自动化)工具,可以将HDL程序综合成网表,通过自动布线工具把网表转换为具体电路布线结构,用于专用集成电路(Application Specific Integrated Circuit,ASIC)和现场可编程门阵列(Field Programmable Gate Array,FPGA)的实现。

Verilog HDL和VHDL是两种常用的硬件描述语言。

1.2 可重用性与IP核

模块的可重用性对于硬件电路的开发效率的提高至关重要,因此提出了集成电路的软核、硬核和固核的概念。

软核(Soft Core) 一般是指经过功能验证、5000门以上的可综合Verilog HDL或VHDL模型,软核与设计方法和电路采用的工艺无关。

固核(Firm Core)通常是指在FPGA器件上,经过综合验证、大于5000门的电路网表文件。

硬核(Hard Core)通常是指在ASIC器件上,经过验证,正确的、大于5000门的电路结构版图掩模。

三者的联系与区别:软核、固核、硬核是目前数字集成电路功能单元模块在不同层级使用的三种形式,可以简单理解为 软核 > 固核 > 硬核。软核采用可读性较高的可综合HDL实现,其可维护性和可重用性程度高,使用更加灵活和便捷。固核和硬核是针对不同芯片平台的功能能单元,性能稳定,不宜修改。

IP核:IP核是具有知识产权核的集成电路芯核的总称,是经过反复验证的、具有特定功能的红模块,且该模块与芯片制造工艺无关,可以移植到不同的半导体工艺中。

如下图所示,在SoC芯片的设计生产过程中,芯片的生产厂家只需根据设计需要购入相应功能的IP核,再将这些IP核按照设计要求进行组合,即可完成所需特定功能的设计。

在这里插入图片描述

1.3 组合逻辑和时序逻辑

1、组合逻辑:

组合逻辑的特点是任意时刻的输出仅取决于该时刻的输入,与电路原本的状态无关,逻辑中不牵涉跳变沿信号的处理,组合逻辑的verilog描述方式有两种:

(1)always@(电平敏感信号列表)

always模块的敏感信号列表为所有输入信号;

组合逻辑中的赋值建议使用阻塞赋值”=“;

always 模块中的信号必须定义为reg 型,但最终的综合得到的组合逻辑电路中并没有寄存器。

(2)assign描述的连续赋值语句

2、时序逻辑:

时序逻辑的特点为任意时刻的输出不仅取决于该时刻的输入,而且还和电路原来的状态有关。时序逻辑电路里面有存储元件,不管其输入如何变化,仅当时钟发生跳变时,输出才可能会变化。

(1)always@(边沿敏感型信号列表)

时序逻辑的敏感信号列表为时钟信号(以及复位信号)和部分输入信号;

时序逻辑中的赋值建议使用非阻塞赋值“<=”。

二、Verilog HDL基础知识

此处所有的内容都可见本人另一篇博客(更加明了),[Verilog学习]一、Verilog语言快速入门

Verilog HDL语法来源于C语言基本语法,空白符、注释符、标识符、转移标识符、关键字等就不再赘述。

2.1 数据类型

在Verilog中数据类型一共有19种,分别是wire,tri,tri1,wand,triand,trireg,trior,wor,reg,large,small,scalared,medium,vectored,integer,time,real,parameter型。可以分为两大类:物理数据类型(连线型和寄存器型)和抽象数据类型(主要有整型,时间型,实型和参数型)。

  1. 物理数据类型中最主要用到的就是wire(连线型)和reg(寄存器型),它们的区别如下:
  • wire连线表示逻辑单元的物理连接,可以对应为电路中的物理信号连接,这种变量类型不能保持电荷。连线型变量必须要有驱动源,一种是连接到一个门或者模块的输出端;另一种使用assign连续赋值语句对其赋值。若没有驱动源,则保持高阻态z

  • reg寄存器型变量是数据存储单元的抽象类型,其对应的硬件电路元件具有状态保持作用,能够存储数据,如触发器、锁存器等。

    reg型数据和wire型的区别在于,reg型数据能够保持最后一次的赋值,而wire型数据需要有持续的驱动。

  1. 抽象数据类型则是对纯数字的抽象描述,不能够与实际的硬件电路相映射,即不可综合。

2.2 运算符

详细介绍见本人的另外一篇博客中1.2.2小节,此处不再赘述

[Verilog学习]一、Verilog语言快速入门

2.3 模块

模块(module)是Verilog中的基本单元,它代表一个基本的功能块,用于描述某个设计的功能或结构以及与其他模块通信的外部端口。模块正是Verilog中层次化设计的体现。

详细介绍见本人另外一篇博客,此处不再赘述

[Verilog学习]一、Verilog语言快速入门

三、Verilog HDL程序设计语句和描述方式

3.1 数据流建模

数据流建模只有一种描述方式,即通过连续赋值语句进行逻辑描述,其最基本的语句是由assign引导的。数据流建模可以描述所有的组合逻辑电路

语法格式:

<net_declaration><range><name>;
assign #<delay><name> =  Assignment expression;
  • net_declaration,连线型变量类型,缺省为wire

  • range,变量位宽,缺省为1bit

  • delay,延时,语法格式为:#(delay1, delay2, delay3)

    “delay1”称为上升延时,是连线型变量转移到”1“状态的延时值

    “delay2”称为下降延时,是连线型变量转移到”0“状态的延时值

    “delay3”称为关闭延时,是连线型变量转移到”Z“状态的延时值

// eg.
module example1_assignment(a,b,m,n,c,y)
    input[3:0]a,b,m,n;
    output[3:0]c,y;
    
    assign y = m|n;
    assign #(3,2,4)c = a&b;
endmodule

(1)赋值目标只能是连线型(wire)

(2)在连续赋值中,只要赋值语句右边表达式任何一个变量有变化,表达式立即被计算,计算的结果立即赋值给左边的信号

(3)连续赋值语句不能出现在过程块(行为级建模会提到,initial/always)中

(4)多个连续赋值语句之间是并行关系,与位置顺序无关

(5)连续赋值语句中的延时具有硬件电路中惯性延时的特性,任何小于其延时量的信号变化脉冲都将被滤除,不会出现在输出端口

下面通过两段Verilog代码,重点解释一下并行关系

  • 案例一:输入信号为a和b,输出信号为c和d,c和d之间没有关系
module ParallelAssignmentExample(  
    input a, b,  
    output c, d  
);  
  
    assign c = a & b;  // 语句1:c 是 a 和 b 的逻辑与结果  
    assign d = a | b;  // 语句2:d 是 a 和 b 的逻辑或结果  
  
endmodule

在这个例子中,cd的值都是并行计算的,不依赖于它们在代码中的顺序。无论assign c = a & b;assign d = a | b;这两行语句的顺序如何,它们都会在同一时钟周期内对输入ab进行操作,并将结果分别赋值给cd

如果输入ab的值发生变化,那么cd的值也会立即更新,而不需要等待前一个赋值语句完成。

  • 案例二:输入信号为a和b,输出信号为c和d,且c是d的输入信号之一
module DependencyExample(  
    input a, b,  
    output c, d  
);  
  
    assign c = a & b;  // 语句1:c 是 a 和 b 的逻辑与结果  
    assign d = c | !b; // 语句2:d 是 c 和 b 的非 的逻辑或结果  
  
endmodule

在这个例子中,cab的逻辑与结果,而dc(即ab的逻辑与结果)和b的非的逻辑或结果。尽管cd的一个输入,但由于这两个赋值语句都是并行执行的,因此c的值会立即反映到d的计算中。这就是说,如果右边的表达式中包含了另一个连续赋值语句的输出,那么该输出值会立即用于计算

3.2 行为级建模

Verilog行为描述语句及其可综合性(能否生成实际的电路)如下

类别语句可综合性
过程语句initial
always
语句块串行语句块 begin-end
并行语句块 fork-join
赋值语句过程连续赋值语句 assign
过程赋值语句 = 、<=
条件语句if-else
case,casez,casex
循环语句forever
repeat
while
for
编译向导语句`define
`include

3.2.1 过程语句

1、initial过程语句

语法格式如下:

initial
    begin
        语句1;
        语句2;
        ...
        语句n;
    end

initial过程块在进行仿真时从模拟0时刻开始执行,在仿真过程中仅执行一次,在执行完一次之后该initial过程块就被挂起,不再执行。如果一个模块中存在多个initial过程块,每个initial过程块都是从0时刻开始并行执行的。

initial过程语句通常用于仿真模块中对激励向量的描述。

2、always过程语句(可综合)

语法格式如下:

always@(<敏感事件列表>)	//边沿敏感型、电平敏感型
    语句块;

(1)采用always过程语句对组合电路进行描述时,需要把全部的输入信号列入敏感事件列表

(2)采用always过程语句对时序电路进行描述时,需要把时间信号和部分输入信号列入敏感事件列表

3.2.2 过程赋值语句

过程块中的赋值语句称为过程赋值语句。过程赋值是在initial语句或always语句内的赋值,只能对reg寄存器数据类型的变量赋值。

  • 阻塞赋值语句 =,不再赘述
  • 非阻塞赋值语句 <=, 不再赘述

3.2.3 过程连续赋值语句

过程连续赋值是在always和initial过程语句块中对连线型和寄存器型变量进行赋值操作,这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。

与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会立即引起过程连续赋值语句的重新执行。

过程连续赋值语句的关键词有四个:

(1)赋值、重新赋值语句(assign、deassign),只能用于reg寄存器型变量赋值,不可用于wire连线型变量赋值。

assign <寄存器型变量> = <赋值表达式>;

deassign <寄存器型变量>;

(2)强制、释放语句(force、release)

force <寄存器或连线型变量> = <赋值表达式>;

release <寄存器或连线型变量>;

下面是用assign、deassign改写的D触发器逻辑代码

//D触发器
module dff_normal(
    input       rstn,
    input       clk,
    input       D,
    output reg  Q
 );

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin   //Q = 0 after reset effective
            Q <= 1'b0 ;
        end
        else begin
            Q <= D ;       //Q = D at posedge of clock
        end
    end

endmodule
//使用assign和deassign改写之后的D触发器
module dff_assign(
    input       rstn,
    input       clk,
    input       D,
    output reg  Q
 );
 
    always @(posedge clk) begin
        Q <= D ;       //Q = D at posedge of clock
    end
 
    always @(negedge rstn) begin
        if(!rstn) begin
            assign Q = 1'b0 ; //change Q value when reset effective
        end
        else begin        //cancel the Q value overlay,
            deassign Q ;  //and Q remains 0-value until the coming of clock posedge
        end
    end
 
endmodule

3.2.4 条件分支语句

1、if语句

与C语言类似,此处不再赘述,需要注意的是if语句必须在过程块内使用

2、case语句(可综合)

语法格式如下

case(控制表达式)1: 语句块1;2: 语句块2;
    ...
    值n: 语句块n;
    default: 语句块n+1;
endcase

(1)case语句对控制表达式和后面的值进行比较时,必须是一种全等比较,必须保证两者的对应位全等。

(2)在使用case语句时,应包含所有的状态,如果未包含完全,那么必须写出default,否则将会产生锁存器,这在同步时序电路设计中时不允许的。

本来觉得没有必要补充casez语句的使用,但是在HDLbits上看到了一个有意思的题目,因此来补充一下(后续)

3.2.5 循环语句(很少使用)

(1)forever所引导的循环语句表示永久循环;

forever 语句或语句块(循环体)

(2)repeat所引导的循环语句表示执行固定次数的循环;

repeat(循环次数表达式) 语句或语句块(循环体)

(3)while所引导的循环语句表示条件循环;

while(条件表达式) 语句或语句块(循环体)

(4)for引导的循环语句也表示条件循环

for(循环变量赋初值; 循环结束条件; 循环变量增值) 语句块(循环体)

循环语句也可以用于综合电路的设计,当循环语句进行计算和赋值的描述时,即当循环语句中的变量是数学值而非物理信号量,可以综合得到逻辑电路。

3.3 结构化建模

主要有模块级建模、门级建模和开关级建模三种,开关级建模是Verilog HDL与VHDL之间的本质区别。这里主要介绍一下模块级建模。

1、模块级建模就是通过调用由用户自己描述产生的module模块来对硬件电路结构进行说明,并设计出电路。若当前模块不再被其它模块所调用,那么这个模块就是顶层模块。

模块调用的基本语法格式:模块名<参数值列表> 实例名(端口名列表);

其中”模块名“是module定义中给定的模块名,它指明了被调用的是哪一个模块,简单来说就是被调用的模块原来叫什么名字;

”参数值列表“是可选项,通常会使用参数重定义语句defparam修改参数值,后续会进行介绍;

”实例名“是模块被调用到当前模块的标志,用来索引层次化建模中被调用的模块位置,简单来说就是被调用的模块在顶层模块中叫什么名字;

”端口名列表“是被调用模块实例各端口(被调用模块的信号名称)相连的外部信号(顶层模块的信号名称),一般使用端口名对应方式,还有一种是端口位置对应方式。

module and_2(a,b,c)						//2输入与门模块
    input a,b;
    output c;
    
    parameter MAX_show = 520;			//参数定义
    
    assign c = a&b;
endmodule

module logic(in1,in2,q)					//顶层模块
    input in1,in2;
    output q;
    
    and_2 U1(.a(in1), .b(in2), .c(q));	//端口名对应方式
    defparam U1.MAX_show = 521;			//使用参数重定义语句修改参数值
endmodule

2、门级建模指的是用基本门级元件搭建硬件模型,但是实际使用较少,因为可以使用数据流建模的方式来进行替代。

3、开关级建模指的是用输入、输出为模拟信号的晶体管搭建硬件模型。

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值