FPGA的设计艺术(13)使用generate语句构建可重用的逻辑设计
前言
本文首发:https://www.ebaina.com/articles/140000010059
我们在verilog中使用generate语句在我们的设计中有条件地或迭代地生成代码块。
这使我们可以:
- 有选择地包括或排除代码块,
- 创建给定代码块的多个例化。
这很重要,也很方便,对于第一条,我们没必要删减代码就可以令某一个模块无效,或者有效;毕竟删掉了,就破坏代码结构了,这对代码迭代管理不利。
第二条就更重要了,例如例化多个模块,如果一个一个例化,少了还可以,如果多了的话,例化几十个,那么代码臃肿,简直了。使用generate语句,几行搞定。
generate 语句一定要正确使用,不是什么地方都可以使用的。关于此,我写过几篇类似的博客:
Verilog中关于for与generate for用法和区别的一点愚见
Verilog 中如何无误使用 generate for?
HDLBits 系列(7)对for循环以及generate for的各种实践
但这几篇文章只是针对generate的一种用法,那就是generate for,generate还有很多其他结构,后面讲到!
Verilog的generate语句
我们只能在并发Verilog代码块中使用generate语句。这意味着我们不能将其包含在Always块或初始块中。
除此之外,我们还必须将if语句,case语句或for循环与generate关键字一起使用。
我们使用if和case generate语句有条件地生成代码,而for generate语句则迭代生成代码。
我们可以编写在generate块内部需要的任何有效的verilog代码。这始终包括block,模块实例化和其他generate语句。
generate块在verilog 2001标准中引入。结果,我们不能在基于verilog 1995的设计中使用此构造。
让我们看一下我们可以在Verilog设计中使用的三种不同类型的generate块。
Verilog中的generate for语句
我们可以在generate块内使用verilog for循环来迭代创建一段代码的多个实例。
我们通常使用generate for 循环方法来描述具有规则和重复结构的硬件。
例如,我们可能希望描述由单个总线控制的多个RAM模块。
如果我们使用generate块而不是手动实例化所有模块,那么我们可以减少代码开销。
下面的代码段显示了verilog中generate for块的常规语法。
// Declare the loop variablegenvar <name>;// Code for thegeneratefor (<initial_condition>; <stop_condition>; <increment>) begin// Code to executeendendgenerate
从该示例可以看出,该方法的语法实际上与我们在verilog for 循环中看到的语法相同。
但是,此方法与常规for循环之间有两个重要区别。
首先,我们必须使用genvar类型声明循环变量。
第二个区别是,我们在generate块中声明了循环,而不是在常规程序块(例如verilog always块)中声明了循环。
这种差异很重要,因为它会改变代码的基本行为。
当我们编写generate for块时,实际上是在告诉Verilog编译器创建代码块的多个实例。
相反,当我们使用普通的for循环时,我们告诉Verilog编译器创建代码块的单个实例,但是执行多次。
作为示例,让我们看一个非常简单的用例,其中我们希望将数据分配给2位向量。
下面的Verilog代码显示了如何使用generate for和for循环来执行此操作。在这两种情况下,代码的功能都是相同的,但产生的结构却大不相同。
// Example using the for loopalways @(posedge clock) beginfor (i = 0; i < 2; i = i + 1) beginsig_a[i] = 1'b0;endend
// Example using the generate for blockgenerate
1for (i = 0; i < 2; i = i + 1) begin
1always @(posedge clock) begin
1sig_a[i] = 1'b0;endendendgenerate
如果我们要展开for循环示例,我们将得到下面的代码。
always @(posedge clock) beginsig_a[0] = 1'b0;sig_a[1] = 1'b0;end
相反,展开代码的生成将导致以下代码。
always @(posedge clock) beginsig_a[0] = 1'b0;endalways @(posedge clock) beginsig_a[1] = 1'b0;end
由此可见,for生成与for循环在本质上有何不同。
Verilog generate for示例
为了更好地说明verilog生成for语句的工作方式,让我们考虑一个基本示例。
在此示例中,我们将使用3个RAM模块的阵列,它们连接到同一条总线。
每个RAM模块都有一个写使能端口,一个4位地址输入和一个4位数据输入。这些信号都连接到同一总线。
此外,每个RAM都有一个4位数据输出总线和一个使能信号,它们对于每个RAM块都是独立的。
电路图显示了我们将要描述的电路。
电路图显示了连接到一条总线的三个RAM模块。
我们需要声明一个3位向量,该向量可用于连接到RAM使能端口。然后,我们可以根据循环变量的值将不同的位连接到每个RAM块。
对于数据输出总线,我们可以创建一个12位向量,并将读取的数据输出连接到该向量的不同4位片上。
但是,更优雅的解决方案是使用由3个4位向量组成的数组。同样,我们可以根据需要使用循环变量分配此数组的不同元素。
下面的Verilog代码片段显示了我们如何使用for generate语句对该电路进行编码。
// rd data array
wire [3:0] rd_data [2:0];// vector for the enable signalswire [2:0] enable;// Genvar to use in the for loopgenvar i;generatefor (i=0; i<=2; i=i+1) beginram ram_i (.clock (clock),.enable (enable[i]),.wr_en (wr_en),.addr (addr),.wr_data (wr_data),.rd_data (rd_data[i]));endendgenerate
综合此代码后,我们得到如下所示的电路。
verilog中的generate if语句
我们使用verilog中的generate if块在我们的设计中有条件地包括verilog代码块。
当我们拥有仅在特定条件下使用的代码时,可以使用generate if语句。
这样的一个例子是当我们想要在我们的设计中包括专门用于测试的功能时。
我们可以使用generate if语句来确保仅在调试版本中包含此功能,而在生产版本中不包含此功能。
下面的代码段显示了verilog generate if语句的一般语法。
generateif (<condition1>) begin// Code to executeendelse if (<condition2>) begin// Code to executeendelse begin// Code to executeend
endgenerate
从该示例可以看出,该方法的语法实际上与我们在verilog if语句中看到的语法相同。
但是,这两种方法之间存在根本差异。
当我们编写generate if语句时,实际上是在告诉verilog编译器根据某种条件创建代码块的实例。
这意味着只编译了一个分支,而其他任何分支都从编译中排除。结果,在我们的设计中只能使用其中一个分支。
相反,当我们使用if语句时,整个if语句将被编译,并且该语句的每个分支都可以执行。
每次在仿真过程中触发if语句代码时,都会评估条件以确定要执行哪个分支。
Verilog generate if示例
为了更好地演示verilog if语句如何工作,让我们考虑一个基本示例。
对于此示例,我们将编写一个输出4位计数器值的测试函数。
由于这是一个测试功能,因此仅在使用调试版本时才需要将其激活。
构建代码的生产版本时,我们将计数器输出绑定到地面。
我们将使用参数来确定何时构建调试版本。
下面的代码片段显示了此示例的实现。
// Use a parameter to control our buildparameter debug_build = 0;// Conditionally generate a countergenerateif (debug_build) begin// Code for the counteralways @(posedge clock, posedge reset) beginif (reset) begincount <= 4'h0;endelse begincount <= count + 1;endendendelse begininitial begincount <= 4'h0;endendendgenerate
当我们将debug_build变量设置为1时,综合器将产生如下所示的电路。在这种情况下,综合工具产生了一个四位计数器电路。
但是,当我们将debug_build参数设置为0时,综合工具将产生如下所示的电路。在这种情况下,综合工具已将计数信号的所有位都接地。
Verilog generate case语句
我们在verilog中使用generate case语句在我们的设计中有条件地包括verilog代码块。
本质上,generate case语句执行与generate if语句相同的功能。
这意味着当我们拥有只希望在特定条件下包括在设计中的代码时,我们也可以使用generate case语句。
例如,我们可以设计一个测试功能,只想将其包含在调试版本中。
然后,我们可以使用generate case语句来确定要构建哪个版本的代码。
下面的代码段显示了verilog中generate case语句的一般语法。
generatecase (<variable>)<value1> : begin// This branch executes when <variable> = <value1>end<value2> : begin// This branch executes when <variable> = <value2>enddefault : begin// This branch executes in all other casesendendcaseendgenerate
从该示例中可以看出,此方法的语法实际上与我们在verilog case语句中所看到的语法相同。
但是,这两种方法之间存在根本差异。
当我们编写generate case语句时,实际上是在告诉verilog编译器根据给定条件创建代码块的实例。
这意味着只编译了一个分支,而其他任何分支都从编译中排除。结果,在我们的设计中只能使用其中一个分支。
相反,当我们使用case语句时,整个case语句将被编译,并且该语句的每个分支都可以执行
每次在仿真期间触发case语句代码时,都会评估条件以确定要执行哪个分支。
Verilog生成案例示例
为了更好地说明verilog生成case语句的工作方式,我们来看一个基本示例。
由于case语句执行与if语句相同的功能,因此我们将再次查看同一示例。
这意味着我们将编写一个输出值4位计数器的测试函数。
由于这是一个测试功能,因此仅在使用调试版本时才需要将其激活。
构建代码的生产版本时,我们将计数器输出绑定到地面。
我们将使用参数来确定何时构建调试版本。
下面的verilog代码使用generate case语句显示了此示例的实现。
// Use a parameter to control our buildparameter debug_build = 0;// Conditionally generate a counter
generatecase (debug_build)1 : begin// Code for the counteralways @(posedge clock, posedge reset) beginif (reset) begincount <= 4'h0;endelse begincount <= count + 1;endendenddefault : begininitial begincount <= 4'h0;endendendcaseendgenerate
当我们将debug_build变量设置为1时,合成器将产生如下所示的电路。在这种情况下,综合工具产生了一个四位计数器电路。
但是,当我们将debug_build参数设置为0时,综合工具将产生如下所示的电路。在这种情况下,综合工具已将计数信号的所有位都接地。
练习
- 使用参数化模块有什么好处?
- 我们可以在实例化模块时配置其功能。这使我们可以使代码更易于重用。
- 写一个generate for块,实例化2个16位同步计数器。这两个计数器应该使用参数化模块示例。
// Variable for the generate loop
genvar i;// Array for the outputswire [15:0] count_out [1:0]// Generate the two countersgeneratefor (i=0; i < 2, i = i+1) begincounter # (.BITS (16)) count_12 (.clock (clock),.reset (reset),.count (count_out[i]));endendgenerate
- 编写一个generate for块,该块根据参数的值实例化一个8位计数器或一个16位计数器。这两个计数器应该使用本文前面的参数化模块示例。您可以使用generate case或generate if块来编写此代码。
// Parameter to contr, teh generate block
parameter COUNT_16 = 0;// Using a generate case statement
generatecase (COUNT_16)0 : begincounter # (.BITS (16)) count_16 (.clock (clock),.reset (reset),.count (count16_out));enddefault : begincounter # (.BITS (8)) count_8 (.clock (clock),.reset (reset),.count (count8_out));endendcaseendgenerate// Using a generate if statementgenerateif (COUNT_16) begincounter # (.BITS (16)) count_16 (.clock (clock),.reset (reset),.count (count16_out));endelse begincounter # (.BITS (8)) count_8 (.clock (clock),.reset (reset),.count (count8_out));endendgenerate
FPGA的设计艺术(13)使用generate语句构建可重用的逻辑设计相关推荐
- FPGA的设计艺术(12)使用parameter构建可重用的逻辑设计
前言 与大多数编程语言一样,我们应该尝试使尽可能多的代码可重用. 这使我们可以减少将来项目的开发时间,因为我们可以更轻松地将代码从一种设计移植到另一种设计. 我们在verilog中有两个可用的构造,它 ...
- FPGA的设计艺术(14)使用函数和任务提升逻辑的可重用性
前言 提到函数与任务,很多已从业的逻辑设计人员甚至都会对此陌生,听过是听过,但是很少用过. 与大多数编程语言一样,我们应该尝试使尽可能多的Verilog代码可重用.这使我们能够减少将来项目的开发时间, ...
- 设计艺术字体如何确定
对于刚刚开始接触字体设计的设计师来说,可能在最初在选择艺术字体设计时候会比较纠结,因为似乎选择哪个类型的字体都可以,而在经过一番考虑之后又觉得选择那个设计用字体都不合适,面对这样的情况,其实是需要遵照 ...
- 数字逻辑计算机组成,数字逻辑设计与计算机组成pdf
数字逻辑设计与计算机组成 内容简介 本书从简单的数字逻辑电路设计基础开始,由浅入深,讲解组合逻辑和时序逻辑电路的设计技术.计算机组成的基本原理和计算机体系结构的相关概念,后深入探讨了现代计算机系统如何 ...
- 【领域驱动设计】(4):从 DDD 落实到数据库设计的整个过程
云世 公众号 过去,系统的软件设计是以数据库设计为核心,当需求确定下来以后,团队首先开始进行数据库设计.因为数据库是各个模块唯一的接口,当整个团队将数据库设计确定下来以后,就可以按照模块各自独立地进 ...
- FPGA的设计艺术(16)逻辑设计中无刻不在的判断之if/case语句
前言 Verilog中的if或者case语句十分简单,但确实十分重要,我们的逻辑设计可以说一定离不开它,我们时时刻刻使用它,我们使用它进行建模,通常对应的是多路选择器这样的硬件单元或者变种. 我们通常 ...
- FPGA的设计艺术(17)如何搭建一个简易的逻辑测试平台?
前言 提到FPGA逻辑的仿真,一般指的是行为仿真或者功能仿真,还有人会称为前仿,不包含时间延迟信息,只验证逻辑功能.对于小模块的仿真,需要写一个测试文件,英文是testbench,即测试平台.在tes ...
- FPGA的设计艺术(6)STA实战之SmartTime时序约束及分析示例(I)
前言 FPGA进行时序分析通常使用厂家的编译工具,进行时序分析,但是万变不离其宗,时序分析的知识通常都是通用的,原理都是一致的.下面根据SmartTime的资料来看下时序分析的实际操作是如何的,这在其 ...
- FPGA的设计艺术(7)STA实战之SmartTime时序约束及分析示例(II)
前言 本文续FPGA的设计艺术(6)STA实战之SmartTime时序约束及分析示例(I),分析了时钟的不确定性,多周期路径,以及门控时钟的STA分析方法.可以使用各大厂家的时序分析工具,大多数都自带 ...
最新文章
- Scala的基本语法总结
- 学界 | 和清华大学自然语言处理与社会人文计算实验室一起读机器翻译论文
- 界面Hello world
- 华为mate 20pro升级鸿蒙,华为放大招!华为Mate20系列也能拍月亮,以后还能升级鸿蒙...
- 记一次mongoDB-@Document(collection = “XXX“)配置的探索
- SDUT - 2623 The number of steps(概率dp)
- Lync Server 2010迁移至Lync Server 2013部署系列 Part1: 扩展AD架构
- 图的深度优先搜索(DFS)和广度优先搜索(BFS)算法
- 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)...
- Input禁用文本框
- 课堂作业(求几个数的最大值)
- Win32 文件(2)
- Error: Cound not create the Java Virtual Machine. Error: A fatal exception has occurred. Program wil
- Python爬虫入门教程 19-100 51CTO学院IT技术课程抓取
- 两个高斯分布乘积的理论推导
- Vue回炉重造之封装防刷新考试倒计时组件
- 宏碁暗影骑士擎AN515-58原厂预装Windows11恢复镜像oem系统
- 【Solidity】函数returns多个值的接收方式
- Zookeeper——Watcher原理详解
- 【读论文-笔记】——1.沐神读Alexnet
热门文章
- /etc/fstab 文件解释
- 一个自己设计的软件框架
- ffmpeg+mencoder环境搭建和视频处理总结
- prototype.js 1.4版开发者手册
- ADO.NET 2.0 - 读者询问能否使用 SqlBulkCopy 对象来大量复制文字文件
- centos java tomcat_centos配置Tomcat以指定的身份(非root)运行
- 三星s8自带测试硬件软件,三星S8手机国行固件开启测试:或支持桌面级操作体验...
- php帝国下载文件,帝国CMS如何支持弹出下载txt jpg等格式
- sun服务器清理内存日志_SUN 服务器消除黄灯告警灯详情教程
- mysql源 如何编译安装,MySQL5.7.22-源代码编译安装