热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

FPGA的设计艺术(13)使用generate语句构建可重用的逻辑设计

前言本文首发:https:www.ebaina.comarticles140000010059我们在verilog中使用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

从该示例可以看出&#xff0c;该方法的语法实际上与我们在verilog for 循环中看到的语法相同。

但是&#xff0c;此方法与常规for循环之间有两个重要区别。


  • 首先&#xff0c;我们必须使用genvar类型声明循环变量。

  • 第二个区别是&#xff0c;我们在generate块中声明了循环&#xff0c;而不是在常规程序块&#xff08;例如verilog always块&#xff09;中声明了循环。

这种差异很重要&#xff0c;因为它会改变代码的基本行为。


  • 当我们编写generate for块时&#xff0c;实际上是在告诉Verilog编译器创建代码块的多个实例。

  • 相反&#xff0c;当我们使用普通的for循环时&#xff0c;我们告诉Verilog编译器创建代码块的单个实例&#xff0c;但是执行多次。

作为示例&#xff0c;让我们看一个非常简单的用例&#xff0c;其中我们希望将数据分配给2位向量。

下面的Verilog代码显示了如何使用generate for和for循环来执行此操作。在这两种情况下&#xff0c;代码的功能都是相同的&#xff0c;但产生的结构却大不相同。

// Example using the for loopalways &#64;(posedge clock) beginfor (i &#61; 0; i < 2; i &#61; i &#43; 1) beginsig_a[i] &#61; 1&#39;b0;endend

// Example using the generate for blockgenerate
1for (i &#61; 0; i < 2; i &#61; i &#43; 1) begin
1always &#64;(posedge clock) begin
1sig_a[i] &#61; 1&#39;b0;endendendgenerate

如果我们要展开for循环示例&#xff0c;我们将得到下面的代码。

always &#64;(posedge clock) beginsig_a[0] &#61; 1&#39;b0;sig_a[1] &#61; 1&#39;b0;end

相反&#xff0c;展开代码的生成将导致以下代码。

always &#64;(posedge clock) beginsig_a[0] &#61; 1&#39;b0;endalways &#64;(posedge clock) beginsig_a[1] &#61; 1&#39;b0;end

由此可见&#xff0c;for生成与for循环在本质上有何不同。


Verilog generate for示例

为了更好地说明verilog生成for语句的工作方式&#xff0c;让我们考虑一个基本示例。

在此示例中&#xff0c;我们将使用3个RAM模块的阵列&#xff0c;它们连接到同一条总线。

每个RAM模块都有一个写使能端口&#xff0c;一个4位地址输入和一个4位数据输入。这些信号都连接到同一总线。

此外&#xff0c;每个RAM都有一个4位数据输出总线和一个使能信号&#xff0c;它们对于每个RAM块都是独立的。

电路图显示了我们将要描述的电路。

RAM电路

电路图显示了连接到一条总线的三个RAM模块。

我们需要声明一个3位向量&#xff0c;该向量可用于连接到RAM使能端口。然后&#xff0c;我们可以根据循环变量的值将不同的位连接到每个RAM块。

对于数据输出总线&#xff0c;我们可以创建一个12位向量&#xff0c;并将读取的数据输出连接到该向量的不同4位片上。

但是&#xff0c;更优雅的解决方案是使用由3个4位向量组成的数组。同样&#xff0c;我们可以根据需要使用循环变量分配此数组的不同元素。

下面的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&#61;0; i<&#61;2; i&#61;i&#43;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

综合此代码后&#xff0c;我们得到如下所示的电路。

综合后电路


verilog中的generate if语句

我们使用verilog中的generate if块在我们的设计中有条件地包括verilog代码块。

当我们拥有仅在特定条件下使用的代码时&#xff0c;可以使用generate if语句。

这样的一个例子是当我们想要在我们的设计中包括专门用于测试的功能时。

我们可以使用generate if语句来确保仅在调试版本中包含此功能&#xff0c;而在生产版本中不包含此功能。

下面的代码段显示了verilog generate if语句的一般语法。

generateif (<condition1>) begin// Code to executeendelse if (<condition2>) begin// Code to executeendelse begin// Code to executeend
endgenerate

从该示例可以看出&#xff0c;该方法的语法实际上与我们在verilog if语句中看到的语法相同。

但是&#xff0c;这两种方法之间存在根本差异。

当我们编写generate if语句时&#xff0c;实际上是在告诉verilog编译器根据某种条件创建代码块的实例。

这意味着只编译了一个分支&#xff0c;而其他任何分支都从编译中排除。结果&#xff0c;在我们的设计中只能使用其中一个分支。

相反&#xff0c;当我们使用if语句时&#xff0c;整个if语句将被编译&#xff0c;并且该语句的每个分支都可以执行。

每次在仿真过程中触发if语句代码时&#xff0c;都会评估条件以确定要执行哪个分支。


Verilog generate if示例

为了更好地演示verilog if语句如何工作&#xff0c;让我们考虑一个基本示例。

对于此示例&#xff0c;我们将编写一个输出4位计数器值的测试函数。

由于这是一个测试功能&#xff0c;因此仅在使用调试版本时才需要将其激活。

构建代码的生产版本时&#xff0c;我们将计数器输出绑定到地面。

我们将使用参数来确定何时构建调试版本。

下面的代码片段显示了此示例的实现。

// Use a parameter to control our buildparameter debug_build &#61; 0;// Conditionally generate a countergenerateif (debug_build) begin// Code for the counteralways &#64;(posedge clock, posedge reset) beginif (reset) begincount <&#61; 4&#39;h0;endelse begincount <&#61; count &#43; 1;endendendelse begininitial begincount <&#61; 4&#39;h0;endendendgenerate

当我们将debug_build变量设置为1时&#xff0c;综合器将产生如下所示的电路。在这种情况下&#xff0c;综合工具产生了一个四位计数器电路。

显示四位计数器电路的电路图

但是&#xff0c;当我们将debug_build参数设置为0时&#xff0c;综合工具将产生如下所示的电路。在这种情况下&#xff0c;综合工具已将计数信号的所有位都接地。

电路图显示了四个缓冲器&#xff0c;其输入接地


Verilog generate case语句

我们在verilog中使用generate case语句在我们的设计中有条件地包括verilog代码块。

本质上&#xff0c;generate case语句执行与generate if语句相同的功能。

这意味着当我们拥有只希望在特定条件下包括在设计中的代码时&#xff0c;我们也可以使用generate case语句。

例如&#xff0c;我们可以设计一个测试功能&#xff0c;只想将其包含在调试版本中。

然后&#xff0c;我们可以使用generate case语句来确定要构建哪个版本的代码。

下面的代码段显示了verilog中generate case语句的一般语法。

generatecase (<variable>)<value1> : begin// This branch executes when &#61; end<value2> : begin// This branch executes when &#61; enddefault : begin// This branch executes in all other casesendendcaseendgenerate

从该示例中可以看出&#xff0c;此方法的语法实际上与我们在verilog case语句中所看到的语法相同。

但是&#xff0c;这两种方法之间存在根本差异。

当我们编写generate case语句时&#xff0c;实际上是在告诉verilog编译器根据给定条件创建代码块的实例。

这意味着只编译了一个分支&#xff0c;而其他任何分支都从编译中排除。结果&#xff0c;在我们的设计中只能使用其中一个分支。

相反&#xff0c;当我们使用case语句时&#xff0c;整个case语句将被编译&#xff0c;并且该语句的每个分支都可以执行

每次在仿真期间触发case语句代码时&#xff0c;都会评估条件以确定要执行哪个分支。


Verilog生成案例示例

为了更好地说明verilog生成case语句的工作方式&#xff0c;我们来看一个基本示例。

由于case语句执行与if语句相同的功能&#xff0c;因此我们将再次查看同一示例。

这意味着我们将编写一个输出值4位计数器的测试函数。

由于这是一个测试功能&#xff0c;因此仅在使用调试版本时才需要将其激活。

构建代码的生产版本时&#xff0c;我们将计数器输出绑定到地面。

我们将使用参数来确定何时构建调试版本。

下面的verilog代码使用generate case语句显示了此示例的实现。

// Use a parameter to control our buildparameter debug_build &#61; 0;// Conditionally generate a counter
generatecase (debug_build)1 : begin// Code for the counteralways &#64;(posedge clock, posedge reset) beginif (reset) begincount <&#61; 4&#39;h0;endelse begincount <&#61; count &#43; 1;endendenddefault : begininitial begincount <&#61; 4&#39;h0;endendendcaseendgenerate

当我们将debug_build变量设置为1时&#xff0c;合成器将产生如下所示的电路。在这种情况下&#xff0c;综合工具产生了一个四位计数器电路。
显示四位计数器电路的电路图

但是&#xff0c;当我们将debug_build参数设置为0时&#xff0c;综合工具将产生如下所示的电路。在这种情况下&#xff0c;综合工具已将计数信号的所有位都接地。

电路图显示了四个缓冲器&#xff0c;其输入接地


练习


  1. 使用参数化模块有什么好处&#xff1f;

  • 我们可以在实例化模块时配置其功能。这使我们可以使代码更易于重用。

  1. 写一个generate for块&#xff0c;实例化2个16位同步计数器。这两个计数器应该使用参数化模块示例。

// Variable for the generate loop
genvar i;// Array for the outputswire [15:0] count_out [1:0]// Generate the two countersgeneratefor (i&#61;0; i < 2, i &#61; i&#43;1) begincounter # (.BITS (16)) count_12 (.clock (clock),.reset (reset),.count (count_out[i]));endendgenerate

  1. 编写一个generate for块&#xff0c;该块根据参数的值实例化一个8位计数器或一个16位计数器。这两个计数器应该使用本文前面的参数化模块示例。您可以使用generate case或generate if块来编写此代码。

// Parameter to contr, teh generate block
parameter COUNT_16 &#61; 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

推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Java各个版本新特性及Lambda表达式简介
    本文介绍了Java各个版本的新特性,包括接口的默认方法和Lambda表达式。接口的默认方法允许给接口添加非抽象的方法实现,使用default关键字。Lambda表达式提供了更简洁的语法,可以替代传统的匿名对象的方式。同时,还介绍了Lambda表达式在排序中的应用。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
author-avatar
南阳啸68
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有