review josh-verilog-4, change hyperref styles

This commit is contained in:
2023-04-17 19:46:59 +08:00
parent 5f2488bfa9
commit e50c0bcfcc
6 changed files with 302 additions and 265 deletions
@@ -19,6 +19,7 @@ date: 2020-11-25 22:32:42
# 语法 # 语法
`b = rcosdesign(beta,span,sps)` `b = rcosdesign(beta,span,sps)`
`b = rcosdesign(beta,span,sps,shape)` `b = rcosdesign(beta,span,sps,shape)`
<!-- more --> <!-- more -->
@@ -23,7 +23,7 @@ abbrlink: 460433dd
description: 本文阐述了 HDL 语言/设计方法的基本概念。主要介绍了数字系统建模方法的演变,引入了 HDL 语言的基本概念,对 Verilog HDL 和 VHDL、Verilog HDL 和 C 的联系与区别进行了说明,在最后引入了 HDL 的设计和验证流程,并对各个流程进行了简单介绍。 description: 本文阐述了 HDL 语言/设计方法的基本概念。主要介绍了数字系统建模方法的演变,引入了 HDL 语言的基本概念,对 Verilog HDL 和 VHDL、Verilog HDL 和 C 的联系与区别进行了说明,在最后引入了 HDL 的设计和验证流程,并对各个流程进行了简单介绍。
--- ---
# 1. 设计方法的变迁<a id="01"></a> # 1. 设计方法的变迁<a id="toc.1"></a>
随着微电子设计技术与工艺的发展,数字集成电路从电子管、晶体管、中小规模集成电路、超大规模集成电路(Very Large Scale Integrated Circuit, VLSIC)逐步发展到今天的专用集成电路(Application Specific Integrated Circuit, ASIC),愈发数字化的产品如计算机、手机、数字电视等都运用了复杂的专用数字集成电路。数字逻辑器件从简单的逻辑门到复杂的 SoC,种类繁多,提供了对复杂系统的灵活支撑。 随着微电子设计技术与工艺的发展,数字集成电路从电子管、晶体管、中小规模集成电路、超大规模集成电路(Very Large Scale Integrated Circuit, VLSIC)逐步发展到今天的专用集成电路(Application Specific Integrated Circuit, ASIC),愈发数字化的产品如计算机、手机、数字电视等都运用了复杂的专用数字集成电路。数字逻辑器件从简单的逻辑门到复杂的 SoC,种类繁多,提供了对复杂系统的灵活支撑。
@@ -51,9 +51,9 @@ description: 本文阐述了 HDL 语言/设计方法的基本概念。主要介
目前最成熟的综合工具是 RTL 级综合工具,它能将 RTL 级描述翻译并优化为门级网表。综合工具的产生支撑了直接从 RTL 级描述硬件电路,这种从门级描述抽象到 RTL 级描述数字电路是设计方法的又一次伟大飞跃。 目前最成熟的综合工具是 RTL 级综合工具,它能将 RTL 级描述翻译并优化为门级网表。综合工具的产生支撑了直接从 RTL 级描述硬件电路,这种从门级描述抽象到 RTL 级描述数字电路是设计方法的又一次伟大飞跃。
从上面描述可以看到 HDL 语言是分层次的,关于上述 HDL 语言设计层次的含义在本文的[“HDL 的设计与验证流程”](#03)小节和 [《Part 3——描述方式和设计层次》的“设计层次”][]小节有详细论述。) 从上面描述可以看到 HDL 语言是分层次的,关于上述 HDL 语言设计层次的含义在本文的 [3. HDL 的设计与验证流程](#toc.3)小节和 [Part 3——描述方式和设计层次的 5. 设计层次][]小节有详细论述。
# 2. Verilog 语言的特点<a id="02"></a> # 2. Verilog 语言的特点<a id="toc.2"></a>
## 2.1. Verilog 的由来 ## 2.1. Verilog 的由来
@@ -107,7 +107,7 @@ Verilog 语言是根据 C 语言发明而来的,因此 Verilog 具备了 C 语
Verilog 毕竟是硬件描述语言,在抽象层次上比 C 语言要差一些,语法不如 C 灵活,例如文件输入和输出方面,Verilog 的功能显然不如 C。为了克服 Verilog 高级抽象的缺陷,Verilog 的设计者们发明了 PLI。PLI 可以在仿真器中将 C 语言的程序和 Verilog 的程序互相通信,或者在 Verilog 中调用 C 语言的函数库,这样就大大扩展了 Verilog 语言的灵活性和高层次抽象的能力。开发时,一方面,硬件设计者使用 Verilog 进行硬件建模;另一方面,验证工程师却常常使用 C 来编写测试向量,然后通过 Verilog 的 PLI 将 Verilog 和 C 联系起来。这样,C 语言就很好地弥补了 Verilog 高层次建模的缺陷。 Verilog 毕竟是硬件描述语言,在抽象层次上比 C 语言要差一些,语法不如 C 灵活,例如文件输入和输出方面,Verilog 的功能显然不如 C。为了克服 Verilog 高级抽象的缺陷,Verilog 的设计者们发明了 PLI。PLI 可以在仿真器中将 C 语言的程序和 Verilog 的程序互相通信,或者在 Verilog 中调用 C 语言的函数库,这样就大大扩展了 Verilog 语言的灵活性和高层次抽象的能力。开发时,一方面,硬件设计者使用 Verilog 进行硬件建模;另一方面,验证工程师却常常使用 C 来编写测试向量,然后通过 Verilog 的 PLI 将 Verilog 和 C 联系起来。这样,C 语言就很好地弥补了 Verilog 高层次建模的缺陷。
# 3. HDL 的设计与验证流程<a id="03"></a> # 3. HDL 的设计与验证流程<a id="toc.3"></a>
HDL 的基本功能就是有效地描述并仿真硬件系统。在此部分,抛开具体的 PLD 或 ASIC 设计流程,从 HDL 语言层次入手,分析典型的 HDL 设计与验证流程。基于 HDL 的设计、仿真流程如[图 1-1](#fig.1-1) 所示。图中,虚线框表示的步骤可以根据项目的复杂度省略,而实线框表示的步骤为必须执行的步骤。 HDL 的基本功能就是有效地描述并仿真硬件系统。在此部分,抛开具体的 PLD 或 ASIC 设计流程,从 HDL 语言层次入手,分析典型的 HDL 设计与验证流程。基于 HDL 的设计、仿真流程如[图 1-1](#fig.1-1) 所示。图中,虚线框表示的步骤可以根据项目的复杂度省略,而实线框表示的步骤为必须执行的步骤。
@@ -157,6 +157,6 @@ RTL 级综合指 **{% label prime@将 RTL 级 HDL 语言翻译成由与、或、
EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012. EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012.
[《Part 3——描述方式和设计层次》的“设计层次”]: https://josh-gao.top/posts/fd117896.html#%E8%AE%BE%E8%AE%A1%E5%B1%82%E6%AC%A1 [Part 3——描述方式和设计层次的 5. 设计层次]: https://josh-gao.top/posts/fd117896.html#toc.5
[^1]: 最新的 Verilog HDL标准为 IEEE 1364-2005。 [^1]: 最新的 Verilog HDL标准为 IEEE 1364-2005。
+13 -13
View File
@@ -199,7 +199,7 @@ endmodule
这种**在模块中实例化其他功能模块**的描述方式被称之为**结构化描述**。 这种**在模块中实例化其他功能模块**的描述方式被称之为**结构化描述**。
## 2.2. 3 种描述方法 ## 2.2. 3 种描述方法<a id="toc.2.2"></a>
从以上的描述可以看出 Verilog 语言的 3 种基本的描述方法(基本上所有的 Verilog 功能模块都是由这 3 种方式来描述): 从以上的描述可以看出 Verilog 语言的 3 种基本的描述方法(基本上所有的 Verilog 功能模块都是由这 3 种方式来描述):
@@ -215,9 +215,9 @@ endmodule
- 用户定义原语(UDP)实例化:实例化用户定义的原语。 - 用户定义原语(UDP)实例化:实例化用户定义的原语。
在[《Part 3——描述方式和设计层次》][]中会详细介绍 Verilog 语言中的这 3 种描述方式。 在 [Part 3——描述方式和设计层次][]中会详细介绍 Verilog 语言中的这 3 种描述方式。
# 3. Verilog 的基本词法 # 3. Verilog 的基本词法<>
1. Verilog HDL 是一种**大小写敏感**的语言,这一点与 VHDL 不同,因此在书写的时候要格外注意。在 Verilog 语言中,**所有的关键字(又叫保留字)都为小写**。Verilog 的内部信号名(或称为标识符)使用大写和小写都可以。标识符可以是字母、数字、`$`(美元符号)和 `_`(下划线)任意组合,只要**第一个字符是字母或者下划线**即可。 1. Verilog HDL 是一种**大小写敏感**的语言,这一点与 VHDL 不同,因此在书写的时候要格外注意。在 Verilog 语言中,**所有的关键字(又叫保留字)都为小写**。Verilog 的内部信号名(或称为标识符)使用大写和小写都可以。标识符可以是字母、数字、`$`(美元符号)和 `_`(下划线)任意组合,只要**第一个字符是字母或者下划线**即可。
@@ -419,7 +419,7 @@ Verilog 语言中的编译指令不止这几条,其他不常用的指令这里
- `Z` 表示高阻状态,也就是**没有任何驱动**,通常用来对**三态总线**进行建模。 - `Z` 表示高阻状态,也就是**没有任何驱动**,通常用来对**三态总线**进行建模。
在[第 7 节](#7)将提到 Verilog 语言中的变量类型。通常 `net` 型变量如果没有初始化,其值为 `Z`,`register` 型变量如果没有初始化,其值为 `X` 。这一点在仿真时显得比较有意义。 在[第 7 节](#toc.7)将提到 Verilog 语言中的变量类型。通常 `net` 型变量如果没有初始化,其值为 `Z`,`register` 型变量如果没有初始化,其值为 `X` 。这一点在仿真时显得比较有意义。
{% note warning %} {% note warning %}
@@ -487,7 +487,7 @@ Message = "Hello World";
这样就将字符串常量存入到 `Message` 变量中。 这样就将字符串常量存入到 `Message` 变量中。
# 7. 变量类型<a id=7></a> # 7. 变量类型<a id="toc.7"></a>
在 Verilog 语言中,有两大变量类型: 在 Verilog 语言中,有两大变量类型:
@@ -518,7 +518,7 @@ Message = "Hello World";
线网类型主要用在连续赋值语句中,以及作为模块之间的互连信号。 线网类型主要用在连续赋值语句中,以及作为模块之间的互连信号。
在[《Part 3——描述方式和设计层次》][]中会详细阐述常用线网类型的使用方法。 在 [Part 3——描述方式和设计层次][]中会详细阐述常用线网类型的使用方法。
## 7.2. 寄存器类型 ## 7.2. 寄存器类型
@@ -566,7 +566,7 @@ Message = "Hello World";
同时,在 Verilog 中,**不存可以对整个存储器赋值的语句**,必须对每个单元独立赋值。除非使用 `$readmemb` 或 `$readmemh` 系统任务从文件中读入整个或者部分存储器的数据。 同时,在 Verilog 中,**不存可以对整个存储器赋值的语句**,必须对每个单元独立赋值。除非使用 `$readmemb` 或 `$readmemh` 系统任务从文件中读入整个或者部分存储器的数据。
> 在[《Part 7—— 逻辑验证与 testbench 编写》][]中将讨论如何从文件中读入数据给存储器赋值。 > 在 [Part 7—— 逻辑验证与 testbench 编写][]中将讨论如何从文件中读入数据给存储器赋值。
### 7.2.2. `integer` 类型 ### 7.2.2. `integer` 类型
@@ -593,7 +593,7 @@ end
在下一小节中,将引入“驱动”和“赋值”两个概念,深入探讨两种变量的含义。 在下一小节中,将引入“驱动”和“赋值”两个概念,深入探讨两种变量的含义。
## 7.4. 驱动和赋值 ## 7.4. 驱动和赋值<a id>="toc.7.4"</a>
为了更清楚地描述寄存器和线网变量的概念,将以上述[实例代码](#HelloVlogCode)中的语句来说明。首先,引入 Verilog 语言中两个重要的概念:驱动(Driving)和赋值(Assigning)。 为了更清楚地描述寄存器和线网变量的概念,将以上述[实例代码](#HelloVlogCode)中的语句来说明。首先,引入 Verilog 语言中两个重要的概念:驱动(Driving)和赋值(Assigning)。
@@ -672,7 +672,7 @@ parameter xor_delay = 4;
前面介绍的 `` `define`` 是一种全局的定义,而 `parameter` 是出现在模块内部的**局部定义**,而且可以被**灵活改变**,这是 `parameter` 的一个重要特征。 前面介绍的 `` `define`` 是一种全局的定义,而 `parameter` 是出现在模块内部的**局部定义**,而且可以被**灵活改变**,这是 `parameter` 的一个重要特征。
在[《Part 3——描述方式和设计层次》的 4.2 小节][]中会详细探讨改变模块参数的方法。 在 [Part 3——描述方式和设计层次的 4.2 参数][]小节会详细探讨改变模块参数的方法。
# 9. Verilog 中的并发与顺序 # 9. Verilog 中的并发与顺序
@@ -695,7 +695,7 @@ Verilog 仿真器用来模拟硬件的并行行为的方式类似于软件中的
后续将会重点介绍 Verilog 语言中的这一并行特性,同时阐述 Verilog 的高级仿真原理,希望能帮助大家领会 Verilog 的语义精髓。 后续将会重点介绍 Verilog 语言中的这一并行特性,同时阐述 Verilog 的高级仿真原理,希望能帮助大家领会 Verilog 的语义精髓。
# 10. 操作数、操作符和表达式 # 10. 操作数、操作符和表达式<a id="toc.10"></a>
## 10.1. 操作符 ## 10.1. 操作符
@@ -984,7 +984,7 @@ $readmemh ("Read_In_File.txt", DataSource);
EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012. EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012.
[Part 1——初识 HDL 设计方法]: https://josh-gao.top/posts/460433dd.html [Part 1——初识 HDL 设计方法]: https://josh-gao.top/posts/460433dd.html
[《Part 3——描述方式和设计层次》]: https://josh-gao.top/posts/fd117896.html [Part 3——描述方式和设计层次]: https://josh-gao.top/posts/fd117896.html
[IEEE Std. 1364-2005]: https://standards.ieee.org/standard/1364-2005.html [IEEE Std. 1364-2005]: https://standards.ieee.org/standard/1364-2005.html
[《Part 7—— 逻辑验证与 testbench 编写》]: https://josh-gao.top/posts/e060f513.html [Part 7—— 逻辑验证与 testbench 编写]: https://josh-gao.top/posts/e060f513.html
[《Part 3——描述方式和设计层次》的 4.2 小节]: https://josh-gao.top/posts/fd117896.html#%E5%8F%82%E6%95%B0 [Part 3——描述方式和设计层次的 4.2 参数]: https://josh-gao.top/posts/fd117896.html#toc.4.2
+18 -18
View File
@@ -25,7 +25,7 @@ description: Verilog 语言可以用多种方式来描述硬件,同时,使
# 1. 描述方式 # 1. 描述方式
在[《Part 2——Verilog 语言基础》的第 3 小节][]中己经介绍过,Verilog 语言有 3 种最基本的描述方式. 在 [Part 2——Verilog 语言基础][]中己经介绍过,Verilog 语言有 3 种最基本的描述方式.
- 数据流描述,采用 `assign` 连续赋值语句; - 数据流描述,采用 `assign` 连续赋值语句;
- 行为描述,使用 `always` 语句或 `initial` 语句块中的过程赋值语句; - 行为描述,使用 `always` 语句或 `initial` 语句块中的过程赋值语句;
@@ -63,7 +63,7 @@ assign #1 A_xor_wire = eq0 ^ eq1;
1. **连续驱动** 1. **连续驱动**
在[《Part 2——Verilog 语言基础》的 7.4 驱动和赋值][]部分,已经解释了驱动和赋值之间的微妙区别。 在 [Part 2——Verilog 语言基础的 7.4 驱动和赋值][]部分,已经解释了驱动和赋值之间的微妙区别。
连续赋值语句是连续驱动的。也就是说,任何时刻输入的任何变化都将导致该语句的重新计算。 连续赋值语句是连续驱动的。也就是说,任何时刻输入的任何变化都将导致该语句的重新计算。
@@ -71,7 +71,7 @@ assign #1 A_xor_wire = eq0 ^ eq1;
由于仿真器中不会存储连续赋值语句中被赋值的变量值,因此该变量是线网类型(Net),不能是寄存器类型。需要记住,只有线网类型的变量才可以在 `assign` 语句中被赋值。 由于仿真器中不会存储连续赋值语句中被赋值的变量值,因此该变量是线网类型(Net),不能是寄存器类型。需要记住,只有线网类型的变量才可以在 `assign` 语句中被赋值。
另外,线网类型的变量可以被多重驱动,也就是说,可以在多个连续赋值语句中驱动同一个线网。在 [2.4 驱动源线网](#drive)中将进一步介绍。但是,寄存器变量就不同了,它不能被不同的行为进程(例如 `always` 语句块)驱动。 另外,线网类型的变量可以被多重驱动,也就是说,可以在多个连续赋值语句中驱动同一个线网。在 [2.4 驱动源线网](#toc.2.4)小节中将进一步介绍。但是,寄存器变量就不同了,它不能被不同的行为进程(例如 `always` 语句块)驱动。
3. **使用 `assign` 对组合逻辑建模** 3. **使用 `assign` 对组合逻辑建模**
@@ -182,7 +182,7 @@ assign #(4:5:6, 3:4:5) A_xor_wire = eq0 ^ eq1;
另外,`assign` 语句中的延时特性通常是被逻辑综合工具忽略的。因为综合工具要将 Verilog 语言模型综合成逻辑电路,而逻辑电路的延时是由基本的单元库和走线延时决定的。用户无法对逻辑单元指定延时,但是,用户可以在综合和实现工具中加时序约束,让工具尽量满足设计的时序要求。 另外,`assign` 语句中的延时特性通常是被逻辑综合工具忽略的。因为综合工具要将 Verilog 语言模型综合成逻辑电路,而逻辑电路的延时是由基本的单元库和走线延时决定的。用户无法对逻辑单元指定延时,但是,用户可以在综合和实现工具中加时序约束,让工具尽量满足设计的时序要求。
## 2.4. 驱动源线网<a id="drive"></a> ## 2.4. 驱动源线网<a id="toc.2.4"></a>
下面分为几种线网类型来描述当线网具有多重驱动源时的情况。 下面分为几种线网类型来描述当线网具有多重驱动源时的情况。
@@ -274,9 +274,9 @@ endmodule
![图 3-5 三态驱动总线电路](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-11-30-josh-verilog-part-3/2020-11-30-josh-verilog-part-3-050-WireTri.png!sign){width=700px} ![图 3-5 三态驱动总线电路](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-11-30-josh-verilog-part-3/2020-11-30-josh-verilog-part-3-050-WireTri.png!sign){width=700px}
# 3. 行为描述<a id="03"><a> # 3. 行为描述<a id="toc.3"><a>
在 [《Part 2——Verilog 语言基础》的第 2.2 小节][]中介绍过行为描述的概念。所谓行为描述,是指用语言描述电路的行为。行为描述的语句有两种:`initial` 和 `always` 语句。 在 [Part 2——Verilog 语言基础的 2.2 3 种描述方法][]中介绍过行为描述的概念。所谓行为描述,是指用语言描述电路的行为。行为描述的语句有两种:`initial` 和 `always` 语句。
## 3.1. 行为描述的语句格式 ## 3.1. 行为描述的语句格式
@@ -474,7 +474,7 @@ endmodule
要注意的是,目前多数综合工具还不支持 `wait` 语句,因此这个锁存器的功能只能在仿真时用,不能实现为具体的电路。 要注意的是,目前多数综合工具还不支持 `wait` 语句,因此这个锁存器的功能只能在仿真时用,不能实现为具体的电路。
## 3.2. 过程赋值语句 ## 3.2. 过程赋值语句<a id="toc.3.2"></a>
所谓过程赋值语句就是在 `initial` 和 `always` 语句块中的赋值语句。赋值对象只能是寄存器变量类型。右边的表达式可以是任意操作符的表达式。 所谓过程赋值语句就是在 `initial` 和 `always` 语句块中的赋值语句。赋值对象只能是寄存器变量类型。右边的表达式可以是任意操作符的表达式。
@@ -697,7 +697,7 @@ end
在 `always` 以外的i变量和 `always` 里面定义的i变量属于**两个不同的变量,并不冲突**。它们在仿真的时候将占用两块不同的内存,类似于 C 语言中的静态局部变量。 在 `always` 以外的i变量和 `always` 里面定义的i变量属于**两个不同的变量,并不冲突**。它们在仿真的时候将占用两块不同的内存,类似于 C 语言中的静态局部变量。
## 3.4. 高级编程语旬 ## 3.4. 高级编程语句<a id="toc.3.4"></a>
### 3.4.1. 为什么需要编程语句 ### 3.4.1. 为什么需要编程语句
@@ -1258,13 +1258,13 @@ Verilog 模块参数的这一特性非常有用,用户可以定义一个通用
使用上面这种参数直接带入法时,要注意一点,所有的参数都需要全部按顺序列出来,不能遗漏,也不能颠倒顺序,否则就容易对应不上。 使用上面这种参数直接带入法时,要注意一点,所有的参数都需要全部按顺序列出来,不能遗漏,也不能颠倒顺序,否则就容易对应不上。
# 5. 设计层次 # 5. 设计层次<a id=toc.5></a>
在前面几节中,介绍了 Verilog 的描述方式。 在前面几节中,介绍了 Verilog 的描述方式。
Verilog 语言是一种强大的硬件描述语言,可以支持多个设计层次。在这里将进行简单的介绍。 Verilog 语言是一种强大的硬件描述语言,可以支持多个设计层次。在这里将进行简单的介绍。
## 5.1. 系统级和行为级<a id=5.1></a> ## 5.1. 系统级和行为级<a id=toc.5.1></a>
Verilog 语言作为一种用户工具,提供给用户许多描述硬件的手段,如前面所述:数据流描述、行为描述(`always` 和 `initial` 语句)、结构化描述。同时,不同用户可以根据自己的需要,在不同抽象层次上对硬件进行描述。有如下设计层次金字塔供参考。 Verilog 语言作为一种用户工具,提供给用户许多描述硬件的手段,如前面所述:数据流描述、行为描述(`always` 和 `initial` 语句)、结构化描述。同时,不同用户可以根据自己的需要,在不同抽象层次上对硬件进行描述。有如下设计层次金字塔供参考。
@@ -1282,7 +1282,7 @@ Verilog 语言作为一种用户工具,提供给用户许多描述硬件的手
4. 验证工程师:他们负责对设计的电路进行验证,他们编写的代码主要是用来产生激励,这些激励大部分需要的抽象层次更高,以使仿真的效率更高。然后在工具中对电路进行仿真,检查响应结果。这些代码不会实现为具体癭件,有些并不需要精确到时钟周期,而只是在软件的仿真工具中运行,实现一定的功能即可,称这种描述层次为行为级。 4. 验证工程师:他们负责对设计的电路进行验证,他们编写的代码主要是用来产生激励,这些激励大部分需要的抽象层次更高,以使仿真的效率更高。然后在工具中对电路进行仿真,检查响应结果。这些代码不会实现为具体癭件,有些并不需要精确到时钟周期,而只是在软件的仿真工具中运行,实现一定的功能即可,称这种描述层次为行为级。
这里的行为级描述,不同于 [3. 行为描述](#03)中所述的“行为描述方式”。这里特指一种描述的抽象层次。 这里的行为级描述,不同于 [3. 行为描述](#toc.3)中所述的“行为描述方式”。这里特指一种描述的抽象层次。
下面举一个实例说明 RTL 级和行为级的区别,要实现的状态机一部分如[图 3-18](#fig.3-18) 所示。= 下面举一个实例说明 RTL 级和行为级的区别,要实现的状态机一部分如[图 3-18](#fig.3-18) 所示。=
@@ -1361,7 +1361,7 @@ end
Verilog 设计电路最常用的设计层次就是 RTL 级。在 RTL 描述时,设计者需要关注寄存器的行为,其中保存着数据;同时需要关注寄存器和寄存器之间的组合逻辑功能,是否能满足功能需求和时序需求。RTL 级模型是严格精确到时钟周期的模型。 Verilog 设计电路最常用的设计层次就是 RTL 级。在 RTL 描述时,设计者需要关注寄存器的行为,其中保存着数据;同时需要关注寄存器和寄存器之间的组合逻辑功能,是否能满足功能需求和时序需求。RTL 级模型是严格精确到时钟周期的模型。
[《Part 4 —— RTL 概念与常用 RTL 建模》][]、[《Part 5 —— RTL 设计与编码指导》][]和[《Part 6—— 如何写好状态机》][]会重点介绍 RTL 设计的方法和技巧。 [Part 4 —— RTL 概念与常用 RTL 建模][]、[Part 5 —— RTL 设计与编码指导][]和[Part 6—— 如何写好状态机][]会重点介绍 RTL 设计的方法和技巧。
{% note info %} {% note info %}
@@ -1407,9 +1407,9 @@ Verilog 支持不同设计层次的混描述。
EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012. EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012.
[《Part 2——Verilog 语言基础》的第 3 小节]: https://josh-gao.top/posts/fd117896.html#%E8%A1%8C%E4%B8%BA%E6%8F%8F%E8%BF%B0 [Part 2——Verilog 语言基础]: https://josh-gao.top/posts/fd2ca242.html
[《Part 2——Verilog 语言基础》的 7.4 驱动和赋值]: https://josh-gao.top/posts/fd2ca242.html#%E9%A9%B1%E5%8A%A8%E5%92%8C%E8%B5%8B%E5%80%BC [Part 2——Verilog 语言基础的 2.2 3 种描述方法]: https://josh-gao.top/posts/fd2ca242.html#toc2.2
[《Part 2——Verilog 语言基础》的第 2.2 小节]: https://josh-gao.top/posts/fd2ca242.html#%E7%A7%8D%E6%8F%8F%E8%BF%B0%E6%96%B9%E6%B3%95 [Part 2——Verilog 语言基础的 7.4 驱动和赋值]: https://josh-gao.top/posts/fd2ca242.html#toc7.4
[《Part 4 —— RTL 概念与常用 RTL 建模》]: https://josh-gao.top/posts/ecb88422.html [Part 4 —— RTL 概念与常用 RTL 建模]: https://josh-gao.top/posts/ecb88422.html
[《Part 5 —— RTL 设计与编码指导》]: https://josh-gao.top/posts/53b8b42e.html [Part 5 —— RTL 设计与编码指导]: https://josh-gao.top/posts/53b8b42e.html
[《Part 6—— 如何写好状态机》]: https://josh-gao.top/posts/d860a67e.html [Part 6—— 如何写好状态机]: https://josh-gao.top/posts/d860a67e.html
+212 -176
View File
@@ -29,19 +29,22 @@ description: Verilog HDL 的基本功能之一是描述可综合的硬件电路
# 1. RTL 和综合 # 1. RTL 和综合
从[《Part 3 —— 描述方式和设计层次》的第 5 小节](https://josh-gao.top/posts/fd117896.html#5-%E8%AE%BE%E8%AE%A1%E5%B1%82%E6%AC%A1)可知,HDL 语言是分层次的。 从 [Part 3——描述方式和设计层次的 5. 设计层次][]可知,HDL 语言是分层次的。
{% note default %} {% note default %}
最常用的层次概念有系统级(System Level)或功能模块级(Functional Model Level)、行为级(BehavioraI Level)、寄存器传输级(RTL,Register Transfer Level)和门级(Gate Level)等。
最常用的层次概念有系统级(System Level)或功能模块级(Functional Model Level)、行为级(Behavioral Level)、寄存器传输级(RTL,Register Transfer Level)和门级(Gate Level)
等。
{% endnote %} {% endnote %}
<!-- more --> <!-- more -->
其中寄存器传输级(RTL,Register Transfer Level)指:**{% label @ 不关注寄存器和组合逻辑的细节(如使用了多少逻辑门,逻辑门之间的连接拓扑结构等),通过描述寄存器到寄存器之间的逻辑功能描述电路的 HDL 层次%}**。RTL 级是比门级更高的抽象层次,使用 RTL 级语言描述硬件电路一般比门级描述电路简单、高效得多。 其中寄存器传输级(RTL,Register Transfer Level)指:**{% label primary@ 不关注寄存器和组合逻辑的细节(如使用了多少逻辑门,逻辑门之间的连接拓扑结构等),通过描述寄存器到寄存器之间的逻辑功能描述电路的 HDL 层次%}**。RTL 级是比门级更高的抽象层次,使用 RTL 级语言描述硬件电路一般比门级描述电路简单、高效得多。
RTL 级语言的最重要特性就是:{% label primary @RTL 级描述是可综合的描述层次 %}。 RTL 级语言的最重要特性就是:{% label primary @RTL 级描述是可综合的描述层次 %}。
综合(Synthesize)指:**将 HDL 语言、原理图等设计输入翻译成由与、或、非门等基本逻辑单元组成的门级连接(网表),并根据设计目标与要求(约束条件)优化所生成的逻辑连接,输出门级网表文件**。RTL 级综合指将 RTL 级源代码翻译并优化为门级网表。 综合(Synthesize)指**将 HDL 语言、原理图等设计输入翻译成由与、或、非门等基本逻辑单元组成的门级连接(网表),并根据设计目标与要求(约束条件)优化所生成的逻辑连接,输出门级网表文件**。RTL 级综合指将 RTL 级源代码翻译并优化为门级网表。
随着综合工具的不断智能化,使用 RTL 级语言描述硬件电路越来越方便。特别是在可编程逻辑器件(PLD,主要指 FPGA 和 CPLD)设计领域,最重要的代码设计层次就是 RTL 级。 随着综合工具的不断智能化,使用 RTL 级语言描述硬件电路越来越方便。特别是在可编程逻辑器件(PLD,主要指 FPGA 和 CPLD)设计领域,最重要的代码设计层次就是 RTL 级。
@@ -49,7 +52,7 @@ RTL 级语言的最重要特性就是:{% label primary @RTL 级描述是可综
典型的 RTL 设计包含以下3个部分: 典型的 RTL 设计包含以下3个部分:
1. 时钟域描述 1. **时钟域描述**
描述设计所使用的所有时钟,时钟之间的主从与派生关系,时钟域之间的转换。 描述设计所使用的所有时钟,时钟之间的主从与派生关系,时钟域之间的转换。
@@ -63,11 +66,11 @@ RTL 级语言的最重要特性就是:{% label primary @RTL 级描述是可综
RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了设计的性能,如何调整时序逻辑、组合逻辑的连接关系和拓扑结构以达到最佳的性能就是后面讨论的编码风格(Coding Style)的核心。 RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了设计的性能,如何调整时序逻辑、组合逻辑的连接关系和拓扑结构以达到最佳的性能就是后面讨论的编码风格(Coding Style)的核心。
工程师们设计 RTL 级代码的顺序各种各样,推荐使用如下的设计步骤: 工程师们设计 RTL 级代码的顺序各种各样,这里推荐使用如下的设计步骤:
1. **功能定义与模块划分** 1. **功能定义与模块划分**
根据系统功能的定义和模块划分准则(详见Part 5 第 4 节)划分各个功能模块。 根据系统功能的定义和模块划分准则(详见 [Part 5——RTL 设计与编码指导的 4. 结构层次和模块划分]小节)划分各个功能模块。
2. **定义所有模炔的接口** 2. **定义所有模炔的接口**
@@ -77,19 +80,21 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
根据设计的时钟复杂程度定义时钟之间的派生关系,分析设计中有哪些时钟域,是否存在异步时钟域之间的数据交换。对于 PLD 设计,还需确认全局时钟和是否使用 PLL/DLL 完成时钟的分频、倍频、移相等功能,哪些时钟使用全局资源布线,哪些时钟使用第二全局时钟资源布线。 根据设计的时钟复杂程度定义时钟之间的派生关系,分析设计中有哪些时钟域,是否存在异步时钟域之间的数据交换。对于 PLD 设计,还需确认全局时钟和是否使用 PLL/DLL 完成时钟的分频、倍频、移相等功能,哪些时钟使用全局资源布线,哪些时钟使用第二全局时钟资源布线。
全局时钟资源的特点是:几乎没有 CIock Skew(时钟偏斜),有一定的 Clock Delay(时钟延迟),驱动能力最强;第二全局时钟的特点是:有较小的 CIock Skew,较小的 CIock DeIay,时钟驱动能力较强。 全局时钟资源的特点是:几乎没有 Clock Skew(时钟偏斜),有一定的 Clock Delay(时钟延迟),驱动能力最强;第二全局时钟的特点是:有较小的 Clock Skew,较小的 Clock DeIay,时钟驱动能力较强。
4. **考虑设计的关键路径** 4. **考虑设计的关键路径**
关键路径是指设计中时序要求最难以满足的路径。设计的时序要求主要体现在频率、建立时间、保持时间等时序指标上。在设计初期,设计者可以根据系统的频率要求,粗略地分析出设计的时序难点(如最高频率的路径,计数器的最低位,包含复杂组合逻辑的时序路径等),通过一些时序优化手段(如 Pipeline、Retiming、逻辑复制等,这些时序优化手段在 Part 5 第 7 节有详细介绍)**从代码上缓解设计的时序压力**,这种方法比单依靠综合与布局布线工具的自动优化有效得多。 关键路径是指设计中**时序要求最难以满足的路径**。设计的时序要求主要体现在频率、建立时间、保持时间等时序指标上。在设计初期,设计者可以根据系统的频率要求,粗略地分析出设计的时序难点(如最高频率的路径,计数器的最低位,包含复杂组合逻辑的时序路径等),通过一些时序优化手段(如 Pipeline、Retiming、逻辑复制等,这些时序优化手段在 [Part 5——RTL 设计与编码指导的 7. RTL 代码优化技巧][]部分有详细介绍)**从代码上缓解设计的时序压力**,这种方法比单依靠综合与布局布线工具的自动优化有效得多。
5. **顶层设计** 5. **顶层设计**
常用的设计方法有两种,一种是自顶而下的设计方法,即先描述设计的顶层,然后描述设计的每个子模块;另一种是由底向上的设计方法,即首先描述设计的子模块,最后定义设计的顶层。RTL 设计推荐使用自顶而下的设计方法,因为这种设计方法与模块规划的顺序一致,而且更利于进行 Modular Design(模块化设计方法),并行开展设计工作,提高模块重用率。 常用的设计方法有两种,一种是**自顶而下**的设计方法,即先描述设计的顶层,然后描述设计的每个子模块;另一种是**由底向上**的设计方法,即首先描述设计的子模块,最后定义设计的顶层。
**RTL 设计推荐使用自顶而下的设计方法**,因为这种设计方法与模块规划的顺序一致,而且更利于进行 Modular Design(模块化设计方法),并行开展设计工作,提高模块重用率。
6. **FSM 设计** 6. **FSM 设计**
有限状态机(FSM)是逻辑设计的最重要内容之一。Part 6 主要讨论如何写好 FSM。 有限状态机(FSM)是逻辑设计的最重要内容之一。[Part 6——如何写好状态机]主要讨论如何写好 FSM。
7. **时序逻辑设计** 7. **时序逻辑设计**
@@ -103,11 +108,11 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
## 3.1. 非阻塞赋值、阻塞赋值、连续赋值 ## 3.1. 非阻塞赋值、阻塞赋值、连续赋值
有很多文章论述阻塞赋值和非阻塞赋值的概念,[《Part 3 —— 描述方式和设计层次》的 3.2 小节](https://josh-gao.top/posts/fd117896.html#3-2-%E8%BF%87%E7%A8%8B%E8%B5%8B%E5%80%BC%E8%AF%AD%E5%8F%A5)详细讨论了因不当使用阻塞赋值和非阻塞赋值引起的仿真与设计意图不一致。为了避免不当使用阻塞賦值和非阻塞赋值而造成的种种歧义和错误,这里推荐: 有很多文章论述阻塞赋值和非阻塞赋值的概念,[Part 3——描述方式和设计层次的 3.2 过程赋值语句][]部分详细讨论了因不当使用阻塞赋值和非阻塞赋值引起的仿真与设计意图不一致。为了避免不当使用阻塞賦值和非阻塞赋值而造成的种种歧义和错误,这里推荐:
1. **对于时序逻辑,即 `always` 模块的敏感表为{% label primary @沿敏感信号 %}(多为时钟或复位的正沿或负沿),统一使用非阻塞賦值 `<=`**。例如: 1. **对于时序逻辑,即 `always` 模块的敏感表为{% label primary @沿敏感信号 %}(多为时钟或复位的正沿或负沿),统一使用非阻塞賦值 `<=`**。例如:
```verilog // cnt1.v ``` verilog cnt1.v
/****************************** /******************************
时序逻辑中使用非阻塞赋值。 时序逻辑中使用非阻塞赋值。
******************************/ ******************************/
@@ -117,17 +122,18 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input clock; input clock;
output [3:0] cnt_out; output [3:0] cnt_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_out; reg [3:0] cnt_out;
always @(posedge clock) always @(posedge clock)
cnt_out <= cnt_out + 1; cnt_out <= cnt_out + 1;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
2. **对于 `always` 模块的敏感表为{% label primary @电平敏感 %}信号的组合逻辑,统一使用阻塞賦值**。例如: 2. **对于 `always` 模块的敏感表为{% label primary @电平敏感 %}信号的组合逻辑,统一使用阻塞賦值**。例如:
<span id="cnt2.v"></span>
<a id="cnt2.v"></a>
``` verilog cnt2.v ``` verilog cnt2.v
/****************************** /******************************
@@ -140,11 +146,11 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input [3:0] cnt_out; input [3:0] cnt_out;
output [3:0] cnt_out_plus; output [3:0] cnt_out_plus;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_out_plus; reg [3:0] cnt_out_plus;
always @(cnt_out) always @(cnt_out)
cnt_out_plus = cnt_out + 1; cnt_out_plus = cnt_out + 1;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -170,22 +176,30 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
``` ```
{% note warning %} {% note warning %}
在 2 和 3 中,为什么不采用类似例 1 中的 `cnt_out <= cnt_out + 1;` 呢?因为,**这样会产生组合逻辑环**。组合逻辑环是同步时序逻辑设计的大忌,它会使得时序路径无法被工具所分析,**因不同的芯片的延时差异,而造成逻辑功能不稳定**。有些已经完成很久的设计,在换了芯片批次后,逻辑功能不正确,大多数都是由组合逻辑环造成的。 在 2 和 3 中,为什么不采用类似例 1 中的 `cnt_out <= cnt_out + 1;` 呢?因为,**这样会产生组合逻辑环**。组合逻辑环是同步时序逻辑设计的大忌,它会使得时序路径无法被工具所分析,**因不同的芯片的延时差异,而造成逻辑功能不稳定**。有些已经完成很久的设计,在换了芯片批次后,逻辑功能不正确,大多数都是由组合逻辑环造成的。
{% endnote %} {% endnote %}
对于初学者而言,以上几点是必须遵循的法则,详细原因可以参考[《Part 3 —— 描述方式和设计层次》](https://josh-gao.top/posts/fd117896.html)和 Part 5 的论述。 对于初学者而言,以上几点是必须遵循的法则,详细原因可以参考 [Part 3——描述方式和设计层次][]和 [Part 5——RTL 设计与编码指导][]的论述。
## 3.2. 寄存器电路建模 ## 3.2. 寄存器电路建模
寄存器和组合逻辑是数字逻辑电路的两大基本要素。寄存器一般和同步时序逻辑关联,其特点为仅当时钟的沿(上升沿或下降沿)到达时,才有可能发生输出的改变。根据实现目标不同,寄存器的建模结构略有不同,需要注意如下要点: 寄存器和组合逻辑是数字逻辑电路的两大基本要素。寄存器一般和同步时序逻辑关联,其特点为仅当时钟的沿(上升沿或下降沿)到达时,才有可能发生输出的改变。根据实现目标不同,寄存器的建模结构略有不同,需要注意如下要点:
1. **寄存器信号声明**:**寄存器定义是 `reg` 型**。 1. **寄存器信号声明**
**寄存器定义是 `reg` 型**。
{% note info %} {% note info %}
请注意,这个命题的反命题不一定成立。**某些信号虽然被定义为 `reg` 型,但是最终综合实现结果并不是寄存器**,如上述例子 [cnt2.v](#cnt2.v) 中 `cnt_out_plus` 虽然被指定为 `reg` 型,但是实现时是纯组合逻辑。**只有定义为 `reg` 型,且 `always` 的敏感表为 `posedge` 或 `negedge` 沿敏感操作时,该信号才是寄存器**。在[《Part 2 —— Verilog 语言基础》](https://josh-gao.top/posts/fd2ca242.html)和[《Part 3 —— 描述方式和设计层次》](https://josh-gao.top/posts/fd117896.html)也有相关内容描述这个问题。 请注意,这个命题的反命题不一定成立。**某些信号虽然被定义为 `reg` 型,但是最终综合实现结果并不是寄存器**,如上述例子 [cnt2.v](#cnt2.v) 中 `cnt_out_plus` 虽然被指定为 `reg` 型,但是实现时是纯组合逻辑。**只有定义为 `reg` 型,且 `always` 的敏感表为 `posedge` 或 `negedge` 沿敏感操作时,该信号才是寄存器**。在[《Part 2 —— Verilog 语言基础》](https://josh-gao.top/posts/fd2ca242.html)和[《Part 3 —— 描述方式和设计层次》](https://josh-gao.top/posts/fd117896.html)也有相关内容描述这个问题。
{% endnote %} {% endnote %}
2. **时钟输入**:在每个时钟的正沿或负沿对数据进行处理。数据的正沿或负沿起作用,是在 `always` 的敏感表中通过 `posedge` 和 `negedge` 指定的。 2. **时钟输入**
在每个时钟的正沿或负沿对数据进行处理。数据的正沿或负沿起作用,是在 `always` 的敏感表中通过 `posedge` 和 `negedge` 指定的。
``` verilog cnt4.v ``` verilog cnt4.v
/****************************** /******************************
@@ -197,21 +211,23 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input clock; input clock;
output [3:0] cnt_out; output [3:0] cnt_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_out; reg [3:0] cnt_out;
always @(negedge clock) always @(negedge clock)
cnt_out <= cnt_out + 1; cnt_out <= cnt_out + 1;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
3. **异步复位/置位**:绝大多数目标器件的寄存器模型都包含异步复位/置位端。异步复位/置位是指无论时钟沿是否有效,当复位/置位信号有效沿到达时,复位/置位立即发挥功能。指定异步复位/置位时,**只需在 `always` 的敏感表中加入复位/置位信号的有效沿即可**。下例描述的异步复位电路是最常用的寄存器复位形式之一。 3. **异步复位/置位**
绝大多数目标器件的寄存器模型都包含异步复位/置位端。异步复位/置位是指无论时钟沿是否有效,当复位/置位信号有效沿到达时,复位/置位立即发挥功能。指定异步复位/置位时,**只需在 `always` 的敏感表中加入复位/置位信号的有效沿即可**。下例描述的异步复位电路是最常用的寄存器复位形式之一。
``` verilog ``` verilog
/****************************** /******************************
最常用的异步复位电路, 设异步复位信号"reset_"是低有效信号(也即下降沿开始复位)。用户可 最常用的异步复位电路, 设异步复位信号"reset_"是低有效信号(也即下降沿开始复
以在 begin/end 结构之间填入复杂的用户逻辑。 位)。用户可以在 begin/end 结构之间填入复杂的用户逻辑。
******************************/ ******************************/
// reg_counter.v // reg_counter.v
@@ -222,7 +238,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
output [3:0] cnt_reg_s; output [3:0] cnt_reg_s;
output [3:0] cnt_reg_a; output [3:0] cnt_reg_a;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_reg_s; // asynchronous reset reg [3:0] cnt_reg_s; // asynchronous reset
always @(posedge clock or negedge reset_) always @(posedge clock or negedge reset_)
if (!reset_) if (!reset_)
@@ -231,7 +247,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
cnt_reg_s [2:0] <= cnt_reg_s [2:0] + 1; cnt_reg_s [2:0] <= cnt_reg_s [2:0] + 1;
cnt_reg_s [3] <= ~cnt_reg_s [3]; cnt_reg_s [3] <= ~cnt_reg_s [3];
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
reg [3:0] cnt_reg_a; // synchronous reset reg [3:0] cnt_reg_a; // synchronous reset
always @(posedge clock) always @(posedge clock)
@@ -245,12 +261,14 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
endmodule endmodule
``` ```
4. **同步复位/置位**:任何寄存器都可实现同步复位/置位功能。指定同步复位/置位时,**`always` 的敏感表中仅有时钟沿信号**,当同步复位/置位信号变化时,同步复位/置位并不立即发生,仅仅当时钟沿采到同步复位/置位的有效电平时,才会在时钟沿到达时刻进行复位/置位操作。 4. **同步复位/置位**
任何寄存器都可实现同步复位/置位功能。指定同步复位/置位时,**`always` 的敏感表中仅有时钟沿信号**,当同步复位/置位信号变化时,同步复位/置位并不立即发生,仅仅当时钟沿采到同步复位/置位的有效电平时,才会在时钟沿到达时刻进行复位/置位操作。
``` verilog reg_counter.v ``` verilog reg_counter.v
/****************************** /******************************
常用的同步复位电路, 设异步复位信号"reset_"是低电平有效信号。请比较上例, 对比 always 常用的同步复位电路, 设异步复位信号"reset_"是低电平有效信号。请比较上例,
的敏感表。 对比 always 的敏感表。
******************************/ ******************************/
module reg_counter (clock, reset_, cnt_reg_s, cnt_reg_a); module reg_counter (clock, reset_, cnt_reg_s, cnt_reg_a);
@@ -269,7 +287,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
cnt_reg_s [3] <= ~cnt_reg_s [3]; cnt_reg_s [3] <= ~cnt_reg_s [3];
end end
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_reg_a; // synchronous reset reg [3:0] cnt_reg_a; // synchronous reset
always @ (posedge clock) always @ (posedge clock)
if (!reset_) if (!reset_)
@@ -278,14 +296,20 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
cnt_reg_a [2:0] <= cnt_reg_a [2:0] + 1; cnt_reg_a [2:0] <= cnt_reg_a [2:0] + 1;
cnt_reg_a [3] <= ~cnt_reg_a [3]; cnt_reg_a [3] <= ~cnt_reg_a [3];
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
> 关于同步复位和异步复位的优缺点,在 [3.9 小节](#0309) 有详细介绍。 > 关于同步复位和异步复位的优缺点,在 [3.9 小节](#toc.3.9) 有详细介绍。
5. **同时使用时钟上升和下降沿的问题**:有时因为数据采样或调整数据相位等需求,设计者会在一个 `always` 的敏感表中同时使用时钟的 `posedge` 和 `negedge`,或者在两个 `always` 的敏感表中分别使用时钟的 `posedge` 和 `negedge` 对某些寄存器电路操作。在这两种描述下,当时钟上沿或下沿到达时,该寄存器电路都会做相应的操作。**这个双沿电路往往可以等同于使用了原时钟的倍频时钟的单沿操作电路**。对于实现在 PLD 的设计而言,同时使用时钟的上、下沿往往是不推荐的,因为 PLD 内嵌的 PLL/DLL 和一些时钟电路往往只能对时钟的一个沿保证非常好的指标,而另个沿的抖动、偏斜、斜率等指标不见得非常优化,有时同时使用时钟的正负沿会因时钟的抖动、偏斜、占空比、斜率等问题造成一定的性能恶化。因此推荐的做法是:将原时钟通过 PLL/DLL 倍频,然后使用倍频时钟的单沿(如上升沿)进行操作。但是电路设计不可一概而论,如果必须使用时钟的双沿对同一个寄存器操作时,请设计者明确此时相当于使用了倍频时钟。 5. **同时使用时钟上升和下降沿的问题**
有时因为数据采样或调整数据相位等需求,设计者会在一个 `always` 的敏感表中同时使用时钟的 `posedge` 和 `negedge`,或者在两个 `always` 的敏感表中分别使用时钟的 `posedge` 和 `negedge` 对某些寄存器电路操作。在这两种描述下,当时钟上沿或下沿到达时,该寄存器电路都会做相应的操作。**这个双沿电路往往可以等同于使用了原时钟的倍频时钟的单沿操作电路**。
对于实现在 PLD 的设计而言,同时使用时钟的上、下沿往往是不推荐的,因为 PLD 内嵌的 PLL/DLL 和一些时钟电路往往只能对时钟的一个沿保证非常好的指标,而另个沿的抖动、偏斜、斜率等指标不见得非常优化,有时同时使用时钟的正负沿会因时钟的抖动、偏斜、占空比、斜率等问题造成一定的性能恶化。
因此推荐的做法是:将原时钟通过 PLL/DLL 倍频,然后使用倍频时钟的单沿(如上升沿)进行操作。但是电路设计不可一概而论,如果必须使用时钟的双沿对同一个寄存器操作时,请设计者明确此时相当于使用了倍频时钟。
``` verilog clock_edge.v ``` verilog clock_edge.v
/****************************** /******************************
@@ -297,7 +321,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input clk_50M, clk_100M, rst_; input clk_50M, clk_100M, rst_;
output [3:0] cnt1, cnt2; output [3:0] cnt1, cnt2;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [3:0] cnt_temp1, cnt_temp2; reg [3:0] cnt_temp1, cnt_temp2;
wire [3:0] cnt1; wire [3:0] cnt1;
always @(posedge clk_50M or negedge rst_ ) always @(posedge clk_50M or negedge rst_ )
@@ -313,7 +337,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
cnt_temp2 <= cnt_temp1 + 1; cnt_temp2 <= cnt_temp1 + 1;
assign cnt1 = (clk_50M)? cnt_temp2 : cnt_temp1; assign cnt1 = (clk_50M)? cnt_temp2 : cnt_temp1;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
reg [3:0] cnt2; reg [3:0] cnt2;
always @(posedge clk_100M or negedge rst_ ) always @(posedge clk_100M or negedge rst_ )
@@ -337,21 +361,19 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
cnt2 <= cnt2 + 1; cnt2 <= cnt2 + 1;
``` ```
以上代码综合后,对应的电路结构如[图 4-1](#图4-1) 所示。 以上代码综合后,对应的电路结构如[图 4-1](#fig.4-1) 所示。
<a name="图4-1"></a> <a id="fig.4-1"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-010-RegisterCircuitModeling-01.png!900px" alt="某双沿加法电路与等价倍频时钟单沿加法电路结构示意图"/>
</div>
RTL 仿真波形如[图 4-2](#图4-2) 所示。 ![图 4-1 某双沿加法电路与等价倍频时钟单沿加法电路结构示意图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-010-RegisterCircuitModeling-01.png!sign){width=900px}
<a name="图4-2"></a> RTL 仿真波形如[图 4-2](#fig.4-2) 所示。
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-020-RegisterCircuitModeling-02.png!900px" alt="某双沿加法电路与等价倍频时钟单沿加法电路仿真波型图"/>
</div>
通过这个例子可以看出 ,某些使用双沿操作的电路等价于使用倍频时钟的单沿电路,也就是说,这些电路使用了时钟的双沿触发寄存器,则相当于提高了设计频率,提高了设计要求的时序难度。对于这些电路,在附加时序约束和进行时序分析时一定要考虑充分。 <a id="fig.4-2"></a>
![图 4-2 某双沿加法电路与等价倍频时钟单沿加法电路仿真波型图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-020-RegisterCircuitModeling-02.png!sign){width=900px}
通过这个例子可以看出 ,某些使用双沿操作的电路等价于使用倍频时钟的单沿电路,也就是说,这些电路使用了时钟的双沿触发寄存器,则**相当于提高了设计频率,提高了设计要求的时序难度**。对于这些电路,在附加时序约束和进行时序分析时一定要考虑充分。
## 3.3. 组合逻辑建模 ## 3.3. 组合逻辑建模
@@ -359,7 +381,9 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
1. **`always` 模块的敏感表为电平敏感信号的组合逻辑电路** 1. **`always` 模块的敏感表为电平敏感信号的组合逻辑电路**
这种形式描述组合逻辑电路应用非常广泛,如果不考虑代码的复杂性,几乎任何组合逻辑电路都可以用这种方式建模。`always` 模块的敏感表为所有判定条件和输入信号,**在使用这种结构描述组合逻辑时一定要将敏感表列写完整**。在 `always` 模块中可以使用 `if ... else`、`case`、`for` 循环等各种 RTL 关键字结构。下面以某组合逻辑译码电路为例,说明这种组合逻辑建模方式。如前所述,在这种组合逻辑 `always` 模块中.推荐使用阻塞赋值 `=`,虽然信号被定义为 `reg` 型,但是最终综合实现结果并不是寄存器,而是组合逻辑,定义为 `reg` 型仅是纯语法需要而已。 这种形式描述组合逻辑电路应用非常广泛,如果不考虑代码的复杂性,几乎任何组合逻辑电路都可以用这种方式建模。`always` 模块的敏感表为所有判定条件和输入信号,**在使用这种结构描述组合逻辑时一定要将敏感表列写完整**。在 `always` 模块中可以使用 `if ... else`、`case`、`for` 循环等各种 RTL 关键字结构。
下面以某组合逻辑译码电路为例,说明这种组合逻辑建模方式。如前所述,在这种组合逻辑 `always` 模块中.推荐使用阻塞赋值 `=`,虽然信号被定义为 `reg` 型,但是最终综合实现结果并不是寄存器,而是组合逻辑,定义为 `reg` 型仅是纯语法需要而已。
``` verilog decode_cmb.v ``` verilog decode_cmb.v
/****************************** /******************************
@@ -372,7 +396,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input CS; // Low effect input CS; // Low effect
output cs1, cs2, cs3, cs4; // Low effect output cs1, cs2, cs3, cs4; // Low effect
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg cs1, cs2, cs3, cs4; reg cs1, cs2, cs3, cs4;
parameter chip1_decode = 2'b00, parameter chip1_decode = 2'b00,
chip2_decode = 2'b01, chip2_decode = 2'b01,
@@ -390,17 +414,16 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
chip4_decode: {cs1, cs2, cs3, cs4} = 4'b1110; chip4_decode: {cs1, cs2, cs3, cs4} = 4'b1110;
endcase endcase
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
综合后对应的结构示意图如下[图 4-3](#图4-3) 所示。 综合后对应的结构示意图如[图 4-3](#fig.4-3) 所示。
<a name="图4-3"></a> <a id="fig.4-3"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-030-CombinatorialLogictModeling.png!700px" alt="某组合逻辑译码电路的结构示意图"/> ![图 4-3 某组合逻辑译码电路的结构示意图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-030-CombinatorialLogictModeling.png!sign){width=700px}
</div>
2. `assign` 等语句描述的组合逻辑电路 2. `assign` 等语句描述的组合逻辑电路
@@ -417,7 +440,7 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input CS; // Low effect input CS; // Low effect
output cs1, cs2, cs3, cs4; // Low effect output cs1, cs2, cs3, cs4; // Low effect
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
wire cs1, cs2, cs3, cs4; wire cs1, cs2, cs3, cs4;
parameter chip1_decode = 2'b00, parameter chip1_decode = 2'b00,
chip2_decode = 2'b01, chip2_decode = 2'b01,
@@ -428,15 +451,17 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
assign cs2 = (!CS && (addr[7:6] == chip2_decode))? 0 : 1 ; assign cs2 = (!CS && (addr[7:6] == chip2_decode))? 0 : 1 ;
assign cs3 = (!CS && (addr[7:6] == chip3_decode))? 0 : 1 ; assign cs3 = (!CS && (addr[7:6] == chip3_decode))? 0 : 1 ;
assign cs4 = (!CS && (addr[7:6] == chip4_decode))? 0 : 1 ; assign cs4 = (!CS && (addr[7:6] == chip4_decode))? 0 : 1 ;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
该描述方式实现的电路同[图 4-3 某组合逻辑译码电路的结构示意图](#图4-3)。 该描述方式实现的电路同[图 4-3](#fig.4-3)。
{% note %} {% note %}
可以看到,对于简单的组合逻辑使用 `assign` 和 `?` 语句比较清晰。但是如果所描述的组合逻辑过于复杂,则需要很多条 `assign` 语句或者嵌套的 `?` 语句来描述,不易解读,此时推荐使用第一种组合逻辑建模方法。 可以看到,对于简单的组合逻辑使用 `assign` 和 `?` 语句比较清晰。但是如果所描述的组合逻辑过于复杂,则需要很多条 `assign` 语句或者嵌套的 `?` 语句来描述,不易解读,此时推荐使用第一种组合逻辑建模方法。
{% endnote %} {% endnote %}
## 3.4. 双线端口与三态信号建模 ## 3.4. 双线端口与三态信号建模
@@ -458,13 +483,13 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
input clk, rst, sel; input clk, rst, sel;
input [7:0] addr; input [7:0] addr;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
inout [7:0] data_bus; inout [7:0] data_bus;
wire [7:0] data_in, data_out; wire [7:0] data_in, data_out;
assign data_in = data_bus; assign data_in = data_bus;
assign data_bus = (sel) ? data_out : 8'bZ; assign data_bus = (sel) ? data_out : 8'bZ;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
decode decode_inst ( decode decode_inst (
.clock (clk), .clock (clk),
@@ -486,8 +511,8 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
``` verilog complex_bibus.v ``` verilog complex_bibus.v
/****************************** /******************************
双向总线 databus 的输出由 3 个使能信号 sel1、sel2、se13共同确定, 下述代码中中使用嵌 双向总线 databus 的输出由 3 个使能信号 sel1、sel2、se13共同确定, 下述代码中
套的问号表达式描述了该三态总线的使能选择。 中使用嵌套的问号表达式描述了该三态总线的使能选择。
******************************/ ******************************/
module complex_bibus (clk, rst, sel1, sel2, sel3, data_bus, addr); module complex_bibus (clk, rst, sel1, sel2, sel3, data_bus, addr);
@@ -517,11 +542,11 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
.cnt_out (cnt_out) .cnt_out (cnt_out)
); );
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
inout [7:0] data_bus; inout [7:0] data_bus;
assign data_bus = (sel1)? decode_out : ((sel2)? cnt_out : ((sel3)? 8'b11111111: assign data_bus = (sel1) ? decode_out : ((sel2) ? cnt_out : ((sel3) ?
8'bZZZZZZZZ)); 8'b11111111 : 8'bZZZZZZZZ));
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -532,7 +557,8 @@ RTL 描述中时序逻辑和组合逻辑的连接关系和拓扑结构决定了
``` verilog complex_bibus2.v ``` verilog complex_bibus2.v
/****************************** /******************************
这种描述需要使到组合逻辑的模块,此时需要引入中间变量 data_out,并定义为 reg 型, 这种描述需要使到组合逻辑的模块,此时需要引入中间变量 data_out,并定义为 reg
型
******************************/ ******************************/
module complex_bibus2 (clk, rst, sel1, sel2, sel3, data_bus, addr); module complex_bibus2 (clk, rst, sel1, sel2, sel3, data_bus, addr);
@@ -655,10 +681,10 @@ mux 也是一种组合逻辑电路,它的常用建模方式也有两种:对
input a, b; input a, b;
output mux_out; output mux_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
wire mux_out; wire mux_out;
assign mux_out = (en)? a : b; assign mux_out = (en)? a : b;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -677,7 +703,7 @@ mux 也是一种组合逻辑电路,它的常用建模方式也有两种:对
input a, b, c, d; input a, b, c, d;
output mux_out; output mux_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg mux_out; reg mux_out;
always @(en or a or b or c or d) always @(en or a or b or c or d)
@@ -687,7 +713,7 @@ mux 也是一种组合逻辑电路,它的常用建模方式也有两种:对
2'b10: mux_out = c; 2'b10: mux_out = c;
2'b11: mux_out = d; 2'b11: mux_out = d;
endcase endcase
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -697,7 +723,7 @@ mux 也是一种组合逻辑电路,它的常用建模方式也有两种:对
逻辑电路设计经常使用一些单口 RAM,双口 RAM 和 ROM 等存储器。Verilog 语法中基本的存储单元定义格式为: 逻辑电路设计经常使用一些单口 RAM,双口 RAM 和 ROM 等存储器。Verilog 语法中基本的存储单元定义格式为:
``` verilog ``` verilog
reg [datawidth] MemoryName [addresswidth]; reg [dataWidth] MemoryName [addressWidth];
``` ```
例如,定义一个数据位宽为 8bit,地址为 63 位的 RAM8x64,则可定义为: 例如,定义一个数据位宽为 8bit,地址为 63 位的 RAM8x64,则可定义为:
@@ -717,8 +743,8 @@ RAM8x64 [32][6:7]
``` verilog ram_basic.v ``` verilog ram_basic.v
/****************************** /******************************
一个简单的 8 位宽, 64 位地址 RAM 的读/写电路,读的时候,先将 RAM8x64 某地址的数据读 一个简单的 8 位宽, 64 位地址 RAM 的读/写电路,读的时候,先将 RAM8x64 某地址的数
到 mem_data 寄存器,然后就可以对寄存器的任意位进行相关操作。 据读到 mem_data 寄存器,然后就可以对寄存器的任意位进行相关操作。
******************************/ ******************************/
@@ -732,7 +758,7 @@ input [5:0] addr;
input [7:0] data_in; input [7:0] data_in;
output [7:0] data_out; output [7:0] data_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [7:0] RAM8x64 [0:63]; reg [7:0] RAM8x64 [0:63];
reg [7:0] mem_data; reg [7:0] mem_data;
@@ -743,20 +769,20 @@ always @(posedge clk)
mem_data <= RAM8x64 [addr]; mem_data <= RAM8x64 [addr];
assign data_out = (en)? mem_data[7:0] : {~mem_data[7], mem_data[6:0]}; assign data_out = (en)? mem_data[7:0] : {~mem_data[7], mem_data[6:0]};
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
上面讲解的仅仅是 Verilog 语法建模存储单元的一般方法。而对于 PLD 设计、由于几乎所有的 FPGA 都内嵌有 RAM 资源,则**并不推荐使用 Verilog 直接建模 RAM**。FPGA 内嵌的 RAM 资源大致分为两类:块RAM(BIock RAM)资源和分布式 RAM 资源(Distributed RAM,是一种基于特殊底层逻辑单元,通过查找表和触发器实现的 RAM 结构)。PLD中使用存储结构的基本方法有两大类: 上面讲解的仅仅是 Verilog 语法建模存储单元的一般方法。而对于 PLD 设计、由于几乎所有的 FPGA 都内嵌有 RAM 资源,则**并不推荐使用 Verilog 直接建模 RAM**。FPGA 内嵌的 RAM 资源大致分为两类:块 RAM(Block RAM)资源和分布式 RAM 资源(Distributed RAM,是一种基于特殊底层逻辑单元,通过查找表和触发器实现的 RAM 结构)。PLD中使用存储结构的基本方法有两大类:
- 第一种方法是:通过器件商的开发平台中内嵌的 **IP 生成器**,在图形化界面中直接选择存储器类型(如双口 RAM、单口 RAM、ROM、分布式 RAM 等),配置存储器参数,生产相应 IP,然后在用户逻辑中直接调用该 IP 即可。这种设计方法是 PLD 设计中推荐的方法,因为器件商最了解 PLD 的底层硬件结构,通过 IP 生成器,可以自动地选择使用 PLD 内嵌的 RAM 资源,并生成存储器的粘合逻辑(glue logic),方便、高效、可靠。 - 第一种方法是:通过器件商的开发平台中内嵌的 **IP 生成器**,在图形化界面中直接选择存储器类型(如双口 RAM、单口 RAM、ROM、分布式 RAM 等),配置存储器参数,生产相应 IP,然后在用户逻辑中直接调用该 IP 即可。**这种设计方法是 PLD 设计中推荐的方法**,因为器件商最了解 PLD 的底层硬件结构,通过 IP 生成器,可以自动地选择使用 PLD 内嵌的 RAM 资源,并生成存储器的粘合逻辑(glue logic),方便、高效、可靠。
- 第二种方法是:直接根据上面的描述用 Verilog 语言建模存储器,由综合器根据代码描述类推并优化存储器结构,调用器件内嵌的硬件存储器资源。这种方法有两个问题:第一是要清晰合理地在代码中描述存储器,有一定的设计难度;第二是最终实现结果在很大的程度上取决于综合器的类推算法,有一定的不确定性。这种方法经常使用在两个场合;第一是 PLD 本身没有块 RAM 或分布式 RAM 等专用存储单元(如 CPLD 等);第二是用户非常熟悉综合器的类推算法,并能通过综合器的相关约束属性指定所需使用的底层硬件 RAM 资源。 - 第二种方法是:直接根据上面的描述用 Verilog 语言建模存储器,由综合器根据代码描述类推并优化存储器结构,调用器件内嵌的硬件存储器资源。这种方法有两个问题:第一是要清晰合理地在代码中描述存储器,有一定的设计难度;第二是最终实现结果在很大的程度上取决于综合器的类推算法,有一定的不确定性。这种方法经常使用在两个场合;第一是 PLD 本身没有块 RAM 或分布式 RAM 等专用存储单元(如 CPLD 等);第二是用户非常熟悉综合器的类推算法,并能通过综合器的相关约束属性指定所需使用的底层硬件 RAM 资源。
## 3.7. 简单的时钟分频电路 ## 3.7. 简单的时钟分频电路
时钟电路是 PLD 设计的核心。在 Part 5 介绍了同步时序电路的相关知识,并介绍了如何处理一些常用时钟电路设计。 时钟电路是 PLD 设计的核心。[Part 5——RTL 设计与编码指导]中介绍了同步时序电路的相关知识,并介绍了如何处理一些常用时钟电路设计。
对于 PLD 设计而言,由于大多数 PLD(特别是FPGA)都内嵌有专用 PLL/DLL 模块,通过这些内嵌的 PLL 或 DLL,可以实现灵活的分频/倍频(一般可实现小数分频倍频)、移相等调整与运算。所以这类 PLD 设计时钟电路的方法如上小节一样,都推荐使用器件商的开发平台中内嵌的 IP 生成器,在图形化界面中直接配置 PLL/DLL 的参数,生产相应的 IP,然后在用户逻辑中直接调用该 IP 即可。 对于 PLD 设计而言,由于大多数 PLD(特别是FPGA)都内嵌有专用 PLL/DLL 模块,通过这些内嵌的 PLL 或 DLL,可以实现灵活的分频/倍频(一般可实现小数分频倍频)、移相等调整与运算。所以这类 PLD 设计时钟电路的方法如上小节一样,都推荐使用器件商的开发平台中内嵌的 IP 生成器,在图形化界面中直接配置 PLL/DLL 的参数,生产相应的 IP,然后在用户逻辑中直接调用该 IP 即可。
@@ -768,10 +794,10 @@ endmodule
``` verilog clk_div_phase.v ``` verilog clk_div_phase.v
/****************************** /******************************
将一个 200kHz 时钟做 2 分频、4 分频、8 分频, 要求分频后的 3 个时钟同相, 而且与源时 将一个 200kHz 时钟做 2 分频、4 分频、8 分频, 要求分频后的 3 个时钟同相, 而且
钟近似同相。在这个设计中, 因为输入时钟速率很低, 仅有 200kHz, 而一般 PLD 内嵌的 PLL 与源时钟近似同相。在这个设计中, 因为输入时钟速率很低, 仅有 200kHz, 而一般
的输入频率下限都在 MHz 级,所以无法使用 PLL 完成分频与相位调整要求。另外对于低速时 PLD 内嵌的 PLL的输入频率下限都在 MHz 级,所以无法使用 PLL 完成分频与相位调整
钟的分频,使用计数器既能满足时序要求,也比较节约器件资源。 要求。另外对于低速时钟的分频,使用计数器既能满足时序要求,也比较节约器件资源。
******************************/ ******************************/
module clk_div_phase (rst, clk_200K, clk_100K, clk_50K, clk_25K); module clk_div_phase (rst, clk_200K, clk_100K, clk_50K, clk_25K);
@@ -781,7 +807,7 @@ endmodule
output clk_100K, clk_50K, clk_25K; output clk_100K, clk_50K, clk_25K;
wire clk_100K, clk_50K, clk_25K; wire clk_100K, clk_50K, clk_25K;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [2:0] cnt; reg [2:0] cnt;
always @ (posedge clk_200K or negedge rst) always @ (posedge clk_200K or negedge rst)
@@ -793,12 +819,12 @@ endmodule
assign clk_100K = ~cnt [0]; assign clk_100K = ~cnt [0];
assign clk_50K = ~cnt [1]; assign clk_50K = ~cnt [1];
assign clk_25K = ~cnt [2]; assign clk_25K = ~cnt [2];
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
这个设计的**难点在于如何调整所有时钟的相位关系**。本例巧妙地通过对计数器每个 bit 的反向,完成了所有分频后时钟的相位调整,保证了3个分频后时钟的相位严格同相。对硬件结构比较清晰则会发现,这 3 个派生时钟与源时钟相比有一个非常小的相位差,**这个相位差是由寄存器的固有 $T_{\mathrm{co}}$(延迟)和计数器累加的组合逻辑造成的**,一般来说在 PLD 中寄存器固有 $T_{\mathrm{co}}$ 的典型值为 1~2ns,而简单的加法运算的组合逻辑门延迟也约为 ns 级,这两个延迟的总和与时钟周期相比微乎其微。如果忽略这个 ns 级的延迟,则通过每个分频时钟的反向,使 3 个分频时钟与源时钟同相,也就是说这 4 个时钟有共同的上升沿。 这个设计的**难点在于如何调整所有时钟的相位关系**。本例巧妙地**{% label primary @通过对计数器每个 bit 的反向,完成了所有分频后时钟的相位调整,保证了3个分频后时钟的相位严格同相 %}**。对硬件结构比较清晰则会发现,这 3 个派生时钟与源时钟相比有一个非常小的相位差,**这个相位差是由寄存器的固有 $T_{\mathrm{co}}$(延迟)和计数器累加的组合逻辑造成的**,一般来说在 PLD 中寄存器固有 $T_{\mathrm{co}}$ 的典型值为 1~2ns,而简单的加法运算的组合逻辑门延迟也约为 ns 级,这两个延迟的总和与时钟周期相比微乎其微。如果忽略这个 ns 级的延迟,则通过每个分频时钟的反向,使 3 个分频时钟与源时钟同相,也就是说这 4 个时钟有共同的上升沿。
2. **奇数分频** 2. **奇数分频**
@@ -812,7 +838,7 @@ endmodule
input clk, reset; input clk, reset;
output clk_out; output clk_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [1:0] state; reg [1:0] state;
reg clk1; reg clk1;
@@ -834,7 +860,7 @@ endmodule
clk1 <= state[0]; clk1 <= state[0];
assign clk_out = state[0] & clk1; assign clk_out = state[0] & clk1;
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -857,7 +883,7 @@ input rst;
input srl_in; input srl_in;
output [7:0] pal_out; output [7:0] pal_out;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
reg [7:0] pal_out; reg [7:0] pal_out;
always @ (posedge clk or negedge rst) always @ (posedge clk or negedge rst)
@@ -865,12 +891,12 @@ always @ (posedge clk or negedge rst)
pal_out <= 8'b0; pal_out <= 8'b0;
else else
pal_out <= {pal_out,srl_in}; pal_out <= {pal_out,srl_in};
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
## 3.9. 同步复位和异步复位<a name="0309"></a> ## 3.9. 同步复位和异步复位<a id="toc.3.9"></a>
复位电路是每个数字逻辑电路的最重要组成部分之一。复位的目的有两个方面:第一是仿真的时候使电路进入初始状态或者其他预知状态;第二是对于综合实现的真实电路,通过复位使电路进入初始状态或者其他预知状态。一般来说,逻辑电路的任何一个寄存器,存储器结构和其他逻辑单元都必须要附加复位逻辑以保证电路能够从错误状态中恢复,可靠地工作。 复位电路是每个数字逻辑电路的最重要组成部分之一。复位的目的有两个方面:第一是仿真的时候使电路进入初始状态或者其他预知状态;第二是对于综合实现的真实电路,通过复位使电路进入初始状态或者其他预知状态。一般来说,逻辑电路的任何一个寄存器,存储器结构和其他逻辑单元都必须要附加复位逻辑以保证电路能够从错误状态中恢复,可靠地工作。
@@ -884,7 +910,7 @@ endmodule
#### 3.9.1.1. 同步复位电路建模 #### 3.9.1.1. 同步复位电路建模
1. 指定**同步复位时,`always` 的敏感表中仅有时钟沿信号**,仅仅当时钟沿采到同步复位的有效电平时,才会在时钟沿到达时刻进行复位操作。如果目标器件或可用库中的触发器本身包含同步复位端口,则在实现时可用直接调用同步复位端。然而很多目标器件(如 PLD)和 ASIC 库的触发器本身并不包含同步复位端口,则复位信号与输入信号组成某种组合逻辑(比如复位低电平有效,则复位与输入信号两者相与即可),然后输入至寄存器的输入端。为了提高复位电路的优先级,一般在电路描述时使用带有优先级的 `if ... else` 结构,复位电路在第一个 `if` 下描述,其他电路在 `else` 或 `else ... if` 分支中描述。 1. 指定**同步复位时,`always` 的敏感表中仅有时钟沿信号**,仅仅当时钟沿采到同步复位的有效电平时,才会在时钟沿到达时刻进行复位操作。如果目标器件或可用库中的触发器本身包含同步复位端口,则在实现时可用直接调用同步复位端。然而很多目标器件(如 PLD)和 ASIC 库的触发器本身并不包含同步复位端口,则复位信号与输入信号组成某种组合逻辑(比如复位低电平有效,则复位与输入信号两者相与即可),然后输入至寄存器的输入端。**为了提高复位电路的优先级,一般在电路描述时使用带有优先级的 `if ... else` 结构,复位电路在第一个 `if` 下描述,其他电路在 `else` 或 `else ... if` 分支中描述。**
``` verilog syn_rst.v ``` verilog syn_rst.v
/****************************** /******************************
@@ -898,7 +924,7 @@ endmodule
output [4:0] cnt1 , cnt2; output [4:0] cnt1 , cnt2;
reg [4:0] cnt1 , cnt2; reg [4:0] cnt1 , cnt2;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(posedge clk) always @(posedge clk)
if (!rst_) begin if (!rst_) begin
cnt1 <= 4'b0; cnt1 <= 4'b0;
@@ -911,17 +937,16 @@ endmodule
cnt1 <= cnt1; cnt1 <= cnt1;
cnt2 <= cnt1 - 1; cnt2 <= cnt1 - 1;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
2. 很多目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器本身并不包含同步复位端口,则同步复位会被实现为如[图 4-4](#图4-4) 所示的结构。 2. 很多目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器本身并不包含同步复位端口,则同步复位会被实现为如[图 4-4](#fig.4-4) 所示的结构。
<a name="图4-4"></a> <a id="fig.4-4"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-040-SynchronousReset-01.png!600px" alt="同步复位实现结构示意图"/> ![图 4-4 同步复位实现结构示意图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-040-SynchronousReset-01.png!sign){width=600px}
</div>
#### 3.9.1.2. 同步复位的优点 #### 3.9.1.2. 同步复位的优点
@@ -932,22 +957,26 @@ endmodule
- 同步复位仅在时钟的有效沿生效,可以**有效地避免因复位电路毛刺造成的亚稳态和错误**。在进行复位和释放复位信号时,都是仅当时钟沿采到复位信号电平变化时才进行相关操作,如果复位信号树的组合逻辑出现了某种毛刺,此时时钟沿采样到毛刺的概率非常低,通过时钟沿采样,可以十分有效地过滤复位电路组合逻辑产生的毛刺,增强电路稳定性。 - 同步复位仅在时钟的有效沿生效,可以**有效地避免因复位电路毛刺造成的亚稳态和错误**。在进行复位和释放复位信号时,都是仅当时钟沿采到复位信号电平变化时才进行相关操作,如果复位信号树的组合逻辑出现了某种毛刺,此时时钟沿采样到毛刺的概率非常低,通过时钟沿采样,可以十分有效地过滤复位电路组合逻辑产生的毛刺,增强电路稳定性。
{% note info %} {% note info %}
同步时序电路的概念参见 Part 5 2.1 小节;亚稳态的概念参见 Part 5 2.2 小节。
同步时序电路的概念参见 [Part 5——RTL 设计与编码指导的 2.1 同步设计原则][]小节;亚稳态的概念参见 [Part 5——RTL 设计与编码指导的 2.2 亚稳态][]小节。
{% endnote %} {% endnote %}
#### 3.9.1.3. 同步复位的缺点 #### 3.9.1.3. 同步复位的缺点
- 很多目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器本身并不包含同步复位端口,使用同步复位会增加更多逻辑资源。 - 很多目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器本身并不包含同步复位端口,使用同步复位会增加更多逻辑资源。
- 同步复位的最大问题在于必须保证复位信号的有效时间足够长,从而才能保证所有触发器都有效地复位。由于仅当时钟沿采样到复位信号时才会进行复位操作,所以**同步复位信号的持续时间起码要大于设计的最长时钟周期**,以保证所有时钟的有效沿都能采样到同步复位信号。其实仅仅保证同步复位信号的持续时间大于最慢的时钟周期还是不够的,设计中**还要考虑到同步复位信号树通过所有相关组合逻辑路径的延时以及由于时钟布线产生的偏斜(skew)**,只有同步复位大于时钟最大周期加上同步信号穿过的组合逻辑路径延迟加上时钟偏斜时,才能保证同步复位可靠、彻底。同步复位实现结构如下图所示,假设同步复位逻辑树组合逻辑的延时为 $\mathtt{t1}$,复位信号传播路径的最大延迟为 $\mathtt{t2}$,最慢时钟的周期为 $\mathtt{Period max}$,时钟的 skew 为 $\mathtt{(clk2 - clk1)}$,则同步复位的周期 $\mathtt{Tsyn\_rst}$ 应该满足下述公式: - 同步复位的最大问题在于必须保证复位信号的有效时间足够长,从而才能保证所有触发器都有效地复位。由于仅当时钟沿采样到复位信号时才会进行复位操作,所以**同步复位信号的持续时间起码要大于设计的最长时钟周期**,以保证所有时钟的有效沿都能采样到同步复位信号。其实仅仅保证同步复位信号的持续时间大于最慢的时钟周期还是不够的,设计中**还要考虑到同步复位信号树通过所有相关组合逻辑路径的延时以及由于时钟布线产生的偏斜(skew)**,**{% label primary @只有同步复位大于时钟最大周期加上同步信号穿过的组合逻辑路径延迟加上时钟偏斜时,才能保证同步复位可靠、彻底 %}**。
同步复位实现结构如[图 4.5](#fig.4-5) 所示,假设同步复位逻辑树组合逻辑的延时为 $\mathtt{t1}$,复位信号传播路径的最大延迟为 $\mathtt{t2}$,最慢时钟的周期为 $\mathtt{Period max}$,时钟的 skew 为 $\mathtt{(clk2 - clk1)}$,则同步复位的周期 $\mathtt{Tsyn\_rst}$ 应该满足下述公式:
$$ $$
\mathtt{Tsyn\_rst > Period max + \left(clk2 - clk1 \right) + t1 + t2} \mathtt{Tsyn\_rst > Period max + \left(clk2 - clk1 \right) + t1 + t2}
$$ $$
<a name="图4-5"></a> <a id="fig.4-5"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-050-SynchronousReset-02.png!800px" alt="同步复位实现结构示意图"/> ![图 4-5 同步复位实现结构示意图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-050-SynchronousReset-02.png!sign){width=800px}
</div>
### 3.9.2. 异步复位 ### 3.9.2. 异步复位
@@ -967,7 +996,7 @@ $$
output [4:0] cnt1 , cnt2; output [4:0] cnt1 , cnt2;
reg [4:0] cnt1 , cnt2; reg [4:0] cnt1 , cnt2;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(posedge clk or negedge rst_) always @(posedge clk or negedge rst_)
if (!rst_) begin if (!rst_) begin
cnt1 <= 4'b0; cnt1 <= 4'b0;
@@ -980,17 +1009,16 @@ $$
cnt1 <= cnt1; cnt1 <= cnt1;
cnt2 <= cnt1 - 1; cnt2 <= cnt1 - 1;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
2. 大多数目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器都包含异步复位端口,异步复位会被直接接到触发器的异步复位端口。同步复位实现结构如[图 4-6](#图4-6) 所示。 2. 大多数目标器件(如 FPGA 和 CPLD)和 ASIC 库的触发器都包含异步复位端口,异步复位会被直接接到触发器的异步复位端口。同步复位实现结构如[图 4-6](#fig.4-6) 所示。
<a name="图4-6"></a> <a id="fig.4-6"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-060-AsynchronousReset-01.png!500px" alt="异步复位实现结构示意图"/> ![图 4-6 异步复位实现结构示意图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-060-AsynchronousReset-01.png!sign){width=500px}
</div>
#### 3.9.2.2. 异步复位的优点 #### 3.9.2.2. 异步复位的优点
@@ -1008,15 +1036,15 @@ $$
#### 3.9.2.4. 推荐的复位电路设计方式 #### 3.9.2.4. 推荐的复位电路设计方式
1. 推荐的复位电路设计方式是异步复位,同步释放。这种方式,可以有效地继承异步复位设计简单的优势,并克服异步复位的上述风险与缺陷。 1. 推荐的复位电路设计方式是**异步复位,同步释放**。这种方式,可以有效地继承异步复位设计简单的优势,并克服异步复位的上述风险与缺陷。
2. 在 FPGA/CPLD 等可编程逻辑器件设计中,使用异步复位,同步释放可以节约器件资源,并获得稳定可靠地复位效果,是推荐的复位设计方式。 2. 在 FPGA/CPLD 等可编程逻辑器件设计中,使用**异步复位,同步释放**可以节约器件资源,并获得稳定可靠地复位效果,是推荐的复位设计方式。
``` verilog asyn_rst_syn_release.v ``` verilog asyn_rst_syn_release.v
/****************************** /******************************
异步复位,同步释放电路建模。异步复位,同步释放的具体设计方法很多,关键是如何保证同步 异步复位,同步释放电路建模。异步复位,同步释放的具体设计方法很多,关键是如何保证
地释放复位信号,本例举例的方法是在复位信号释放时,用系统时钟采样后再将复位信号送到寄 同步地释放复位信号,本例举例的方法是在复位信号释放时,用系统时钟采样后再将复位信
存器的异步复位端。 号送到寄存器的异步复位端。
******************************/ ******************************/
module asyn_rst_syn_release(clk, rst_, cnt1, cnt2); module asyn_rst_syn_release(clk, rst_, cnt1, cnt2);
@@ -1026,7 +1054,7 @@ input rst_;
output [4:0] cnt1 , cnt2; output [4:0] cnt1 , cnt2;
reg [4:0] cnt1 , cnt2; reg [4:0] cnt1 , cnt2;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// reset release circuit // reset release circuit
reg reset_reg; reg reset_reg;
@@ -1045,23 +1073,22 @@ always @(posedge clk or negedge reset_reg)
cnt1 <= cnt1; cnt1 <= cnt1;
cnt2 <= cnt1 - 1; cnt2 <= cnt1 - 1;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
上例使用时钟**将外部输入的异步复位信号寄存一个节拍**后,再送到触发器异步复位端口的设计方法的另一个好处在于:做 STA(静态时序分析)分析时,时序工具会自动检查同步后的异步复位信号和时钟的到达(Recovery)/撤销(Removal)时间关系,如果因布线造成的 skew 导致该到达I撤销时间不能满足,STA工具会上报该路径,帮助设计者进一步分析问题。异步部位同步化电路如[图 4-7](#图4-7) 所示。 上例使用时钟**将外部输入的异步复位信号寄存一个节拍**后,再送到触发器异步复位端口的设计方法的另一个好处在于:做 STA(静态时序分析)分析时,时序工具会自动检查同步后的异步复位信号和时钟的到达(Recovery)/撤销(Removal)时间关系,如果因布线造成的 skew 导致该到达I撤销时间不能满足,STA 工具会上报该路径,帮助设计者进一步分析问题。异步部位同步化电路如[图 4-7](#fig.4-7) 所示。
<a name="图4-7"></a> <a id="fig.4-7"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-070-AsynchronousReset-02.png!500px" alt="异步复位同步化"/> ![图 4-7 异步复位同步化](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-070-AsynchronousReset-02.png!sign){width=500px}
</div>
## 3.10. 用 `case` 和 `if ... else` 建模 ## 3.10. 用 `case` 和 `if ... else` 建模
一般来说, `case` 语句是“平行”(Balance, Parallel)的结构,即每个 `case` 分支的条件判断和执行都是并行的,没有“优先级(Prior)” 。而 `if ... else`、`if ... else if ...` 语句可以建模**无优先级**的判断结构,`if ... if ... if ...` 结构可以建模具**有优先级**的判断结构。 一般来说, `case` 语句是“平行”(Balance, Parallel)的结构,即每个 `case` 分支的条件判断和执行都是并行的,没有“优先级(Prior)” 。而 `if ... else`、`if ... else if ...` 语句可以建模**无优先级**的判断结构,`if ... if ... if ...` 结构可以建模具**有优先级**的判断结构。
一般来说,建立优先级结构(优先级树)会消耗组合逻辑资源,如果非设计需要,推荐使用 `case` 或 `if ... else` 建立无优先级的判断结构。但是某些设计中,有些信号要求先到达(如:关键使能信号、选择信号等),而有些信号需要后到达(如:慢速信号、有效时间较长的信号等),此时则需要使用 `if ... if ...` 结构建立具有优先级的判断结构。 一般来说,**建立优先级结构(优先级树)会消耗组合逻辑资源**,如果非设计需要,推荐使用 `case` 或 `if ... else` 建立无优先级的判断结构。但是某些设计中,有些信号要求先到达(如:关键使能信号、选择信号等),而有些信号需要后到达(如:慢速信号、有效时间较长的信号等),此时则需要使用 `if ... if ...` 结构建立具有优先级的判断结构。
目前综合工具的优化能力越来越强,大多数情况下可以将不必要的优先级树优化掉,综合结果是否具有优先级,很大的程度上取决于综合工具的类型、综合工具的版本、目标器件(目标库)的固有硬件结构。 目前综合工具的优化能力越来越强,大多数情况下可以将不必要的优先级树优化掉,综合结果是否具有优先级,很大的程度上取决于综合工具的类型、综合工具的版本、目标器件(目标库)的固有硬件结构。
@@ -1080,7 +1107,7 @@ output z;
reg z; reg z;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(a or b or c or d or sel0, sel1, sel2, sel3) begin always @(a or b or c or d or sel0, sel1, sel2, sel3) begin
casex ({sel0, sel1, sel2, sel3}) casex ({sel0, sel1, sel2, sel3})
4'b1xxx: z = d; 4'b1xxx: z = d;
@@ -1090,7 +1117,7 @@ always @(a or b or c or d or sel0, sel1, sel2, sel3) begin
default: z = 1'b0; default: z = 1'b0;
endcase endcase
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -1108,7 +1135,7 @@ output z;
reg z; reg z;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin
z = 0; z = 0;
if (sel3) if (sel3)
@@ -1120,7 +1147,7 @@ always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin
else if (sel0) else if (sel0)
z = a; z = a;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
@@ -1138,7 +1165,7 @@ output z;
reg z; reg z;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin
z = 0; // must add default value z = 0; // must add default value
if (sel0) z = a; if (sel0) z = a;
@@ -1146,48 +1173,42 @@ always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin
if (sel2) z = c; if (sel2) z = c;
if (sel3) z = d; if (sel3) z = d;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
上面的描述如果在 Synopsys 公司的 Design compiler 或 FPGA Compiler 等综合工具的较早版本下综合,多`if` 语句(`if ... if ... if ...`)综合结构如[图 4-8](#图4-8) 所示,而单 `if` 语旬(`if ... else if ... else if ...`)和 `casex` 语句综合结构如[图 4-9](#图4-9) 所示。从图中分析河以看到多if 语句建模结构带有优先级,这时最后一条 `if` 语句对应的 `sel3` 和 `d` 的优先级最高;而单 `if` 语句和 `casex` 语句建模时没有建立优先级。 上面的描述如果在 Synopsys 公司的 Design compiler 或 FPGA Compiler 等综合工具的较早版本下综合,多 `if` 语句(`if ... if ... if ...`)综合结构如[图 4-8](#fig.4-8) 所示,而单 `if` 语句(`if ... else if ... else if ...`)和 `casex` 语句综合结构如[图 4-9](#fig.4-9) 所示。从图中分析可以看到多 `if` 语句建模结构带有优先级,这时最后一条 `if` 语句对应的 `sel3` 和 `d` 的优先级最高;而单 `if` 语句和 `casex` 语句建模时没有建立优先级。
<a name="图4-8"></a> <a id="fig.4-8"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-080-MultipleIf.png!800px" alt="多 if 语句 Design Compiler 综合结构视图"/>
</div>
<a name="图4-9"></a> ![图 4-8 多 `if` 语句 Design Compiler 综合结构视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-080-MultipleIf.png!sign){width=800px}
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-090-SingleIf.png!800px" alt="单 if 语句和 casex 语句建模使用 Design Compiler 综合结构视图"/>
</div>
但是由于综合工具的综合优化策略不同,即使对于相同的代码,其综合结果也不尽相同。为了加深理解,使用业界最流行的 PLD 综合工具 Synplify Pro 和 Precision RTL 对多 `if` 语句(`if ... if ... if ...`)、单 `if` 语句(`if ... else if ... else if ...`)和 `casex` 语句分别综合,其中多 `if` 语句的 Synplify Pro 综合结果对应的 RTL 视图和工艺结构视图分别如[图 4-10](#图4-10)、[图 4-11](#图4-11) 所示;多 `if` 语句的 Precision RTL 综合结果对应的 RTL 视图和工艺结构视图分别如[图 4-12](#图4-12)、[图 4-13](#图4-13) 所示。简单分析即可发现,这 3 种语句的 Synplify Pro 和 Precision RTL 综合结果基本一致,都没有明显的优先级结构。这是因为 Synplify Pro 和Precision RTL 这两种综合工具为了节约硬件资源,根据其优化算法.优化掉了冗余的优先级判断结构。 <a id="fig.4-9"></a>
<a name="图4-10"></a> ![图 4-9 单 `if` 语句和 `casex` 语句建模使用 Design Compiler 综合结构视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-090-SingleIf.png!sign){width=800px}
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-100-MultipleIfSynplifyProRTLView.png!600px" alt="多 if 语句的 Synplify Pro 综合结果 RTL 视图"/>
</div>
<a name="图4-11"></a> 但是由于综合工具的综合优化策略不同,即使对于相同的代码,其综合结果也不尽相同。为了加深理解,使用业界最流行的 PLD 综合工具 Synplify Pro 和 Precision RTL 对多 `if` 语句(`if ... if ... if ...`)、单 `if` 语句(`if ... else if ... else if ...`)和 `casex` 语句分别综合,其中多 `if` 语句的 Synplify Pro 综合结果对应的 RTL 视图和工艺结构视图分别如[图 4-10](#fig.4-10)、[图 4-11](#fig.4-11) 所示;多 `if` 语句的 Precision RTL 综合结果对应的 RTL 视图和工艺结构视图分别如[图 4-12](#fig.4-12)、[图 4-13](#fig.4-13) 所示。简单分析即可发现,这 3 种语句的 Synplify Pro 和 Precision RTL 综合结果基本一致,都没有明显的优先级结构。这是因为 Synplify Pro 和 Precision RTL 这两种综合工具为了节约硬件资源,根据其优化算法.优化掉了冗余的优先级判断结构。
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-110-MultipleIfSynplifyProTechView.png!800px" alt="多 if 语句的 Synplify Pro 综合结果工艺结构视图"/>
</div>
<a name="图4-12"></a> <a id="fig.4-10"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-120-MultipleIfPrecisionRTLRTLView.png!800px" alt="多 if 语句的 Precision RTL 综合结果 RTL 视图"/>
</div>
<a name="图4-13"></a> ![图 4-10 多 `if` 语句的 Synplify Pro 综合结果 RTL 视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-100-MultipleIfSynplifyProRTLView.png!sign){width=600px}
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-130-MultipleIfPrecisionRTLTechView.png!800px" alt="多 if 语句的 Precision RTL 综合结果工艺结构视图"/>
</div>
所以,从语法上讲,多 `if` 语句(`if ... if ... if ...`)可以建模具有优先级的条件判断结构,而单 `if` 语句(`if ... else if ... else if ...`)和 `case` 语旬用于建模不带优先级的条件判断。但是随着综合工具优化能力的不断增强,新型的综合工具,大多时候会自动优化掉优先级结构,以减少芯片面积,提高时序性能。条件结构的综合结果是否带有优先级不但取决于综合工具的类型和版本.还和目标器件或目标库有直接关系。 <a id="fig.4-11"></a>
这里推荐初学者尽量使用 `case` 或单 `if` 语句(`if ... else if ... else if ...`)建模判断结构,这样不论何种综合工具, 一般情况下都不会产牛不必要的优先级结构。使用单 `if` 结构.如果没有为所有的 `if` 指定默认的输出,则会生成 Latch(锁存器),如将上例代码删除 `z=0` 这一默认输出,而改为下面的描述,则会生成 Latch。使用 Synplify Pro 综合的综合结果的RTL 视图和工艺结构视图分别如[图 4-14](#图4-14) 、[图 4-15](#图4-15) 所示。 ![图 4-11 多 `if` 语句的 Synplify Pro 综合结果工艺结构视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-110-MultipleIfSynplifyProTechView.png!sign){width=800px}
<a id="fig.4-12"></a>
![图 4-12 多 `if` 语句的 Precision RTL 综合结果 RTL 视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-120-MultipleIfPrecisionRTLRTLView.png!sign){width=800px}
<a id="fig.4-13"></a>
![图 4-13 多 `if` 语句的 Precision RTL 综合结果工艺结构视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-130-MultipleIfPrecisionRTLTechView.png!sign){width=800px}
所以,从语法上讲,多 `if` 语句(`if ... if ... if ...`)可以建模具有优先级的条件判断结构,而单 `if` 语句(`if ... else if ... else if ...`)和 `case` 语句用于建模不带优先级的条件判断。但是随着综合工具优化能力的不断增强,新型的综合工具,大多时候会自动优化掉优先级结构,以减少芯片面积,提高时序性能。条件结构的综合结果是否带有优先级不但取决于综合工具的类型和版本.还和目标器件或目标库有直接关系。
这里推荐初学者尽量使用 `case` 或单 `if` 语句(`if ... else if ... else if ...`)建模判断结构,这样不论何种综合工具, 一般情况下都不会产生不必要的优先级结构。使用单 `if` 结构.如果没有为所有的 `if` 指定默认的输出,则会生成 Latch(锁存器),如将上例代码删除 `z=0` 这一默认输出,而改为下面的描述,则会生成 Latch。使用 Synplify Pro 综合的综合结果的RTL 视图和工艺结构视图分别如[图 4-14](#fig.4-14) 、[图 4-15](#fig.4-15) 所示。
``` verilog mult_if.v ``` verilog mult_if.v
/****************************** /******************************
@@ -1202,29 +1223,27 @@ output z;
reg z; reg z;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin always @(a or b or c or d or sel0 or sel1 or sel2 or sel3) begin
if (sel0) z = a; if (sel0) z = a;
if (sel1) z = b; if (sel1) z = b;
if (sel2) z = c; if (sel2) z = c;
if (sel3) z = d; if (sel3) z = d;
end end
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
endmodule endmodule
``` ```
<a name="图4-14"></a> <a id="fig.4-14"></a>
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-140-MultipleIfSynplifyProRTLView.png!800px" alt="多 if 语句无默认输出时 Synplify Pro 综合结果 RTL 视图"/>
</div>
<a name="图4-15"></a> ![图 4-14 多 `if` 语句无默认输出时 Synplify Pro 综合结果 RTL 视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-140-MultipleIfSynplifyProRTLView.png!sign){width=800px}
<div align="center">
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-150-MultipleIfSynplifyProTechView.png!800px" alt="多 if 语句无默认输出时 Synplify Pro 综合结果工艺结构视图"/>
</div>
如果生成的 Latch 并非设计者意愿,则会造成与设计意图的偏离甚至是错误。而使用完整的 `if. .. else` 或 `case`(全译码或加有 `default` 关键字)语句,则可以有效地避免无意之中生成的 Latch 。在[《Part 3 —— 描述方式和设计层次》的 3.4 小节](https://josh-gao.top/posts/fd117896.html#3-4-%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E8%AF%AD%E6%97%AC)也有防止产生不必要 Latch 的相关描述,可以参考。 <a id="fig.4-15"></a>
![图 4-14 多 `if` 语句无默认输出时 Synplify Pro 综合结果工艺结构视图](https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-02-josh-verilog-part-4/2020-12-02-josh-verilog-part-4-150-MultipleIfSynplifyProTechView.png!sign){width=800px}
如果生成的 Latch 并非设计者意愿,则会造成与设计意图的偏离甚至是错误。而使用完整的 `if. .. else` 或 `case`(全译码或加有 `default` 关键字)语句,则可以有效地避免无意之中生成的 Latch 。在 [Part 3——描述方式和设计层次的 3.4 高级编程语句][]中也有防止产生不必要 Latch 的相关描述,可以参考。
## 3.11. 可综合的 Verilog 语法子集 ## 3.11. 可综合的 Verilog 语法子集
@@ -1236,16 +1255,33 @@ endmodule
- 端口声明:`input`、`output`、`inout`; - 端口声明:`input`、`output`、`inout`;
- 信号类型: `wire`、`reg`、`tri`等, `integer` 通常用于 `for` 语句中的索引; - 信号类型: `wire`、`reg`、`tri`等, `integer` 通常用于 `for` 语句中的索引;
- 参数定义: `parameter`; - 参数定义: `parameter`;
- 运算操作符:各种逻辑操作、移位操作、算术操作符,请参考[《Part 2 —— Verilog 语言基础》的第 10 小节](https://josh-gao.top/posts/fd2ca242.html#10-%E6%93%8D%E4%BD%9C%E6%95%B0%E3%80%81%E6%93%8D%E4%BD%9C%E7%AC%A6%E5%92%8C%E8%A1%A8%E8%BE%BE%E5%BC%8F); - 运算操作符:各种逻辑操作、移位操作、算术操作符,请参考[Part 2——Verilog 语言基础的 10. 操作数、操作符和表达式][]部分;
- 比较判断: `case [default] endcase (casex/casez)`、`if ... else ...`; - 比较判断: `case [default] endcase (casex/casez)`、`if ... else ...`;
- 连续赋值: `assign`、问号表达式; - 连续赋值: `assign`、问号表达式;
- always 模块:建模时序和组合逻辑(敏感表为电平或 `posedge` 或 `negedge` 的沿信号); - `always` 模块:建模时序和组合逻辑(敏感表为电平或 `posedge` 或 `negedge` 的沿信号);
- 语法分割符: `begin ... end`; - 语法分割符: `begin ... end`;
- 任务定义:`tast. .. end task`; - 任务定义:`task ... end task`;
- 循环语句: `for`。 - 循环语句: `for`。
这些关键字的语法在[《Part 2 —— Verilog 语言基础》](https://josh-gao.top/posts/fd2ca242.html)和[《Part 3 —— 描述方式和设计层次》](https://josh-gao.top/posts/fd117896.html)中都有详细介绍,请参考这部分内容。 这些关键字的语法在 [Part 2——Verilog 语言基础][]和 [Part 3——描述方式和设计层次][] 中都有详细介绍,请参考这部分内容。
# 4. 小结 # 4. 小结
本篇笔记中首先介绍了 RTL 和综合的基本概念。然后摆脱长篇大论的理论说教模式,通过一个个具体范例,力图使初学者逐步建立起可综合 RTL 子集的概念。希望大家认真琢磨常用电路结构的建模方法,在实践中掌握 RTL 级设计的基本技巧。 本文首先介绍了 RTL 和综合的基本概念,然后摆脱长篇大论的理论说教模式,通过一个个具体范例,力图使初学者逐步建立起可综合 RTL 子集的概念。希望大家认真琢磨常用电路结构的建模方法,在实践中掌握 RTL 级设计的基本技巧。
# 参考文献
EDA 先锋工作室. *轻松成为设计高手——Verilog HDL 实用精解.* 北京航空航天大学出版社, 2012.
[Part 2——Verilog 语言基础]: https://josh-gao.top/posts/fd2ca242.html
[Part 2——Verilog 语言基础的 10. 操作数、操作符和表达式]: https://josh-gao.top/posts/fd2ca242.html#toc.10
[Part 3——描述方式和设计层次]: https://josh-gao.top/posts/fd117896.html
[Part 3——描述方式和设计层次的 3.2 过程赋值语句]: https://josh-gao.top/posts/fd117896.html#toc.3.2
[Part 3——描述方式和设计层次的 3.4 高级编程语句]: https://josh-gao.top/posts/fd117896.html#toc.3.4
[Part 3——描述方式和设计层次的 5. 设计层次]: https://josh-gao.top/posts/fd117896.html#toc.5
[Part 5——RTL 设计与编码指导]: https://josh-gao.top/posts/53b8b42e.html
[Part 5——RTL 设计与编码指导的 2.1 同步设计原则]: https://josh-gao.top/posts/53b8b42e.html#toc.2.1
[Part 5——RTL 设计与编码指导的 2.2 亚稳态]: https://josh-gao.top/posts/53b8b42e.html#toc.2.2
[Part 5——RTL 设计与编码指导的 4. 结构层次和模块划分]: https://josh-gao.top/posts/53b8b42e.html#toc.4
[Part 5——RTL 设计与编码指导的 7. RTL 代码优化技巧]: https://josh-gao.top/posts/53b8b42e.html#toc.7
[Part 6——如何写好状态机]: https://josh-gao.top/posts/d860a67e.html
@@ -509,7 +509,7 @@ case (var)
# 2. 同步设计原则和多时钟处理 # 2. 同步设计原则和多时钟处理
## 2.1 同步设计原则<a name="0201"></a> ## 2.1 同步设计原则<a name="toc.2.1"></a>
同步设计是 PLD 和 ASIC 设计的最重要原则。本小节首先阐释为什么在 PLD 设计中要采用同步时序设计,然后重点论述同步时序设计的要点。 同步设计是 PLD 和 ASIC 设计的最重要原则。本小节首先阐释为什么在 PLD 设计中要采用同步时序设计,然后重点论述同步时序设计的要点。
@@ -550,7 +550,7 @@ case (var)
- 同步时序电路的延迟。同步时序设计中电路延迟最常用的设计方法是用分频或倍频的时钟或者同步计数器完成所需延迟。换句话说,同步时序电路的延时被当作一个电路逻辑来设计。对于**比较大的和特殊定时要求的延时,一般用高速时钟产生一个计数器,根据计数器的计数,控制延时**;对于**比较小的延时,可以用 D 触发器打一下**,这种做法**不仅使信号延时了一个时钟周期,而且完成了信号与时钟的初次同步**,在输入信号采样或增加时序约束余量时使用。另外许多初学者用行为级(Behavioral Level)方法描述延时,如 `#5 a<= 4'b0101;` 这种行为级描述方法常用于仿真测试激励,但是**在电路综合时会被忽略、并不能起到延时作用**。 - 同步时序电路的延迟。同步时序设计中电路延迟最常用的设计方法是用分频或倍频的时钟或者同步计数器完成所需延迟。换句话说,同步时序电路的延时被当作一个电路逻辑来设计。对于**比较大的和特殊定时要求的延时,一般用高速时钟产生一个计数器,根据计数器的计数,控制延时**;对于**比较小的延时,可以用 D 触发器打一下**,这种做法**不仅使信号延时了一个时钟周期,而且完成了信号与时钟的初次同步**,在输入信号采样或增加时序约束余量时使用。另外许多初学者用行为级(Behavioral Level)方法描述延时,如 `#5 a<= 4'b0101;` 这种行为级描述方法常用于仿真测试激励,但是**在电路综合时会被忽略、并不能起到延时作用**。
## 2.2 亚稳态 ## 2.2 亚稳态<a id="toc.2.2"></a>
在后续笔记中会介绍异步时钟域转换的问题,在谈这个话题之前,先分析一下什么是亚稳态,以及如何避免。 在后续笔记中会介绍异步时钟域转换的问题,在谈这个话题之前,先分析一下什么是亚稳态,以及如何避免。
@@ -593,7 +593,7 @@ case (var)
在早期逻辑电路图设计阶段,有一些设计者养成了手工加入 Buffer 或者非门调整数据延迟的习惯,从而保证本级模块的时钟对上级模块数据的建立、保持时间的要求。这些做法目前主要应用的场合有两种:一是使用分立逻辑元件(如 74 系列)搭建数字逻辑电路;另一种是在 ASIC 设计领域。**目前使用分立逻辑元件搭建数字逻辑电路的场合一般为系统复杂度相对较低,系统灵活性要求不高的场合。**在上述场合使用分立逻辑器件设计数字逻辑电路,由于可以使用的调整延时的手段相对有限,而且采用插入 Buffer 、数字延迟逻辑甚至两个非门等手段调整采样的 Setup 和 Hold 时间是可以接受的。而 ASIC 设计领域采用这种方法是以严格的仿真和约束条件作为强力支持的。 在早期逻辑电路图设计阶段,有一些设计者养成了手工加入 Buffer 或者非门调整数据延迟的习惯,从而保证本级模块的时钟对上级模块数据的建立、保持时间的要求。这些做法目前主要应用的场合有两种:一是使用分立逻辑元件(如 74 系列)搭建数字逻辑电路;另一种是在 ASIC 设计领域。**目前使用分立逻辑元件搭建数字逻辑电路的场合一般为系统复杂度相对较低,系统灵活性要求不高的场合。**在上述场合使用分立逻辑器件设计数字逻辑电路,由于可以使用的调整延时的手段相对有限,而且采用插入 Buffer 、数字延迟逻辑甚至两个非门等手段调整采样的 Setup 和 Hold 时间是可以接受的。而 ASIC 设计领域采用这种方法是以严格的仿真和约束条件作为强力支持的。
正如 [2.1 同步设计原则](#0201)所述,**使用组合逻辑方法产生延迟,容易产生毛刺**,而且这种设计方法的**时序余量较差**,一旦外界条件变换(环境试验,特别是高低温试验),采样时序就有可能完全紊乱,造成电路瘫痪。另外,一旦芯片更新换代,或者移植到其他器件族的芯片上,采样时延必须重新调整,电路的可维护性和继承性都很差。 正如 [2.1 同步设计原则](#toc.2.1)所述,**使用组合逻辑方法产生延迟,容易产生毛刺**,而且这种设计方法的**时序余量较差**,一旦外界条件变换(环境试验,特别是高低温试验),采样时序就有可能完全紊乱,造成电路瘫痪。另外,一旦芯片更新换代,或者移植到其他器件族的芯片上,采样时延必须重新调整,电路的可维护性和继承性都很差。
2. 盲目使用时钟正负沿调整数据采样 2. 盲目使用时钟正负沿调整数据采样
@@ -649,7 +649,7 @@ case (var)
在后面的几节中,将详细谈谈设计和代码风格。 在后面的几节中,将详细谈谈设计和代码风格。
# 4. 结构层次和模块划分 # 4. 结构层次和模块划分<a id="toc.4"></a>
## 4.1 结构层次化编码 ## 4.1 结构层次化编码
@@ -822,7 +822,7 @@ endmodule
## 6.2 Ripple Counter ## 6.2 Ripple Counter
所谓 Ripple Counter,即行波计数器,其结构为:一组寄存器级连,每个寄存器的输出端接到下一级寄存器的时钟管脚,这种计数器常常用于异步分频电路。早期的 PLD 设计经常使用 Ripple Counter 以节约芯片资源。但是由于 Ripple Counter 是一种典型的异步时序逻辑,正如前面“[同步设计原则](#0201)”所述,异步时序逻辑会带来各种各样的时序问题,**在同步时序电路设计中必须严格避免使用 Ripple Counter**。 所谓 Ripple Counter,即行波计数器,其结构为:一组寄存器级连,每个寄存器的输出端接到下一级寄存器的时钟管脚,这种计数器常常用于异步分频电路。早期的 PLD 设计经常使用 Ripple Counter 以节约芯片资源。但是由于 Ripple Counter 是一种典型的异步时序逻辑,正如前面“[同步设计原则](#toc.2.1)”所述,异步时序逻辑会带来各种各样的时序问题,**在同步时序电路设计中必须严格避免使用 Ripple Counter**。
## 6.3 时钟选择 ## 6.3 时钟选择
@@ -843,7 +843,7 @@ endmodule
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-05-josh-verilog-part-5/2020-12-05-josh-verilog-part-5-170-GatedClock.png!sign" width="800px" alt="门控时钟"/> <img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-05-josh-verilog-part-5/2020-12-05-josh-verilog-part-5-170-GatedClock.png!sign" width="800px" alt="门控时钟"/>
</div> </div>
但是 Gated Clock 不是同步时序电路,其 Gating Logic(门控逻辑)会污染 Clock 的质量,通过控制门后会产生毛刺并使时钟的 Skew(偏斜)、Jitter(延时)等指标恶化。正如“[同步设计原则](#0201)” 一节所述,在同步时序电路中,应该尽量不使用 Gated Clock 。 但是 Gated Clock 不是同步时序电路,其 Gating Logic(门控逻辑)会污染 Clock 的质量,通过控制门后会产生毛刺并使时钟的 Skew(偏斜)、Jitter(延时)等指标恶化。正如“[同步设计原则](#toc.2.1)” 一节所述,在同步时序电路中,应该尽量不使用 Gated Clock 。
虽然有些材料指出当功耗成为主要矛盾时,可以使用门控时钟改进电路如[图 5-18](#fig.5-18) 所示的电路完成类似门控时钟的功能,但是笔者仍强烈建议在 PLD 设计中不要使用该图所示的门控时钟改进电路。虽然这个改进电路已经在较大程度上解决了门控电路产生的毛刺,但是请读者注意,这个电路工作的前提是时钟源Clock 的占空比(Duty Cycle)是非常理想的 50%,如果时钟的占空比不能保证 50%,则会产生许多有规律的毛刺信号。另外这个电路还有一个前提,那就是 Clock 与 Enable 信号的不布线 Skew 为 0,否则也会造成宽度为 Skew 的毛刺。 虽然有些材料指出当功耗成为主要矛盾时,可以使用门控时钟改进电路如[图 5-18](#fig.5-18) 所示的电路完成类似门控时钟的功能,但是笔者仍强烈建议在 PLD 设计中不要使用该图所示的门控时钟改进电路。虽然这个改进电路已经在较大程度上解决了门控电路产生的毛刺,但是请读者注意,这个电路工作的前提是时钟源Clock 的占空比(Duty Cycle)是非常理想的 50%,如果时钟的占空比不能保证 50%,则会产生许多有规律的毛刺信号。另外这个电路还有一个前提,那就是 Clock 与 Enable 信号的不布线 Skew 为 0,否则也会造成宽度为 Skew 的毛刺。
@@ -865,7 +865,7 @@ endmodule
<img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-05-josh-verilog-part-5/2020-12-05-josh-verilog-part-5-190-EnableSynchronization.png!sign" width="500px" alt="同步使能端"/> <img src="https://josh-blog-1257563604.cos.ap-beijing.myqcloud.com/img/2020-12-05-josh-verilog-part-5/2020-12-05-josh-verilog-part-5-190-EnableSynchronization.png!sign" width="500px" alt="同步使能端"/>
</div> </div>
# 7. RTL 代码优化技巧 # 7. RTL 代码优化技巧<a id="toc.7"></a>
## 7.1 使用 Pipelining 技术优化时序 ## 7.1 使用 Pipelining 技术优化时序