Verilog Tutorial(10)如何实现可复用的设计?

news2024/10/6 18:25:52

写在前面

在自己准备写verilog教程之前,参考了许多资料----FPGA Tutorial网站的这套verilog教程即是其一。这套教程写得不错,只是没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。

这是网站原文:https://fpgatutorial.com/verilog/

这是系列导航:Verilog教程系列文章导航


本文将讨论可以用来实现代码可复用性的参数parameter和generate语句(生成语句)。

与大多数编程语言一样,设计者也应该尽量使verilog代码尽可能地具备可复用性----这能够减少未来项目的开发时间,因为设计者可以更轻松地将代码从一个设计移植到另一个设计。

在 verilog 中有两种语法可以帮助设计者编写可复用的代码——参数parameter和generate语句。这两种语法都允许设计者创建更通用的代码,以便在例化组件时可以通过修改代码的方式来满足其他的设计需求。

参数Parameter

参数parameter是常量(constant)的局部形式,它可以在例化模块时为其赋值。由于参数的作用范围有限,设计者可以多次调用同一个模块,并为参数赋不同的值。

在编写模块时必须定义一个模块接口,然后可以使用这个接口在FPGA 设计中与许多不同的模块实现互连。接口可以声明参数以及模块的输入和输出。

下面的 verilog 代码片段展示了在模块中声明参数的方法。当这样在 verilog 模块中声明参数时,我们称其为参数化模块(a parameterized module)。

module <module_name> #(
  parameter <parameter_name> = <default_value>    //声明参数
)
(
  //端口定义
);

上面代码中的 <parameter_name> 用于为参数提供标识符。设计者可以使用此标识符在代码中调用参数值--就像使用普通变量一样。

代码中的<default_value> 将为参数分配一个默认值,这很有用,因为它允许设计者例化组件而无需专门为参数分配值。

在例化一个模块时可以使用命名关联(named association)或位置关联(positional association)的方法来为参数赋值,这与直接将信号分配给模块上的输入或输出的作用完全相同。但是在使用verilog-1995标准编写代码时,只能使用位置关联方法来为参数赋值。

下面的 verilog 代码片段展示了在例化模块时用于为参数赋值的方法。

//名称关联
<module_name> # (
  .<parameter_name> (<parameter_value>)
)
<instance_name> (
  //端口连接
);
 
//位置关联
<module_name> # (<parameter_values>)
<instance_name> (
  //端口连接
);

示例

为了更好地理解如何在 verilog 中使用参数,请考虑一个基本示例:设计两个同步计数器,一个是 8 位宽,另一个是 12 位宽。

为了实现这个电路可以设计两个具有不同宽度的不同计数器,但这显然是一种低效的编码方式。相反,如果设计一个计数器并使用一个参数来更改输出中的位数,则会更加高效。

下面的 verilog 代码片段展示了如何为参数化计数器模块编写接口。

module counter #(
  parameter BITS = 8;
)
(
  input wire clock,
  input wire reset,
  output reg [BITS-1 : 0] count
);

这个例子展示了如何使用参数来调整 verilog 中信号的位宽。示例并不是使用固定数字来声明端口信号的位宽,而是将参数值替换到端口声明中。这是 verilog 中一个最常见的参数示例。

上面的verilog代码中定义了BITS参数的默认值为8。因此,如果想要一个不是 8 位的输出时,设计者只需要为参数重新赋值。

下面的代码片段展示了当需要 12 位输出时如何例化此模块。这种情况必须在例化 verilog 模块时重写参数的默认值。

counter # (
  .BITS (12)
) count_12 (
  .clock  (clock),
  .reset  (reset),
  .count  (count_out)
);

虽然上面的例子中只使用了命名关联方法,但其实也可以使用位置关联方法来为参数赋值。

下面的代码片段就展示了如何使用位置关联方法来将 12 赋给参数 BITS 。

counter # (12) count_12 (clock, reset, count_out);

Generate语句

generate 语句可以被用来在设计中生成条件的(Conditional)或迭代的(iterative)代码块。这允许设计可以有选择地包含或排除代码块或创建特定代码块的多个实例。

只能在并发verilog代码块(concurrent verilog code blocks)中使用 generate 语句,这意味着它不能被包含在always blocks或initial blocks中。此外,generate 关键字还必须结合if语句、case语句或者for循环语句来使用。

generate if 和 generate case 语句被用来来有条件地生成代码,而 generate for 语句则迭代地生成代码。generate块中可以编写需要的任何有效的 verilog 代码,包括always blocks、模块例化和其他generate语句。

generate语句是在verilog-2001标准中引入的,所以设计者不能在基于verilog-1995标准的设计中使用它。

Generate For 语句

设计者可以在generate块中使用verilog中的for循环来迭代地创建一段代码的多个实例。 generate for loop 语法通常被用来使用描述有规则的和重复的结构的硬件。

例如,设计者可能想要使用单个总线控制多个RAM模块。如果使用generate块而不是手动例化所有模块,那么就可以有效地减少代码行数。

下面的代码片段展示了generate for 块的一般语法。

//声明循环变量
genvar <name>;
 
//generate块的代码
generate
  for (<initial_condition>; <stop_condition>; <increment>) begin
    //要重复执行的代码
  end
endgenerate

从这里可以看出来,这种语句实际上可for循环语句是很类似的,但是两者之间仍有两个很大的区别。首先,设计者必须使用 genvar 类型来声明循环变量。其次,设计者应在 generate 块中声明循环,而不是像在always块这样的普通过程块中,这种差异很重要,因为它改变了代码的基本行为。

编写一个 generate for block 实际上是让编译工具创建代码的多个实例。相反,使用普通的 for 循环则是让编译工具创建代码的单个实例但要多次执行它。

作为示例,请看一个非常简单的用例:将数据分配给 2 bits向量。

下面的 verilog 代码展示了如何使用 generate for 和 for 循环来做到这一点。在这两种情况下,代码的功能相同,但生成的结构却大不相同。

//使用for循环的方法
always @(posedge clock) begin
  for (i = 0; i < 2; i = i + 1) begin
    sig_a[i] = 1'b0;
  end
end

//使用generate for循环的方法
generate
  for (i = 0; i < 2; i = i + 1) begin
    always @(posedge clock) begin
      sig_a[i] = 1'b0;
    end
  end
endgenerate

如果将for循环的代码展开,那么将得到如下所示的代码。

always @(posedge clock) begin
  sig_a[0] = 1'b0;
  sig_a[1] = 1'b0;
end

相反,将generate for的代码展开将如下所示。

always @(posedge clock) begin
  sig_a[0] = 1'b0;
end

always @(posedge clock) begin
  sig_a[1] = 1'b0;
end

由此可以看出 generate for 与 for 循环有何根本不同。

示例

为了更好地演示 verilog generate for 语句的工作原理,请考虑一个基本示例:连接到同一总线的 3 个 RAM 模块的数组,每个 RAM 模块都有一个写使能端口、一个 4 位地址输入和 4 位数据输入。这些信号都被连接到同一总线。此外,每个 RAM 都有一个 4 位数据输出总线和一个使能信号,它们对于每个 RAM 块都是独立的。下面的电路图展示了将要设计的电路。

为此需要声明一个 3 位的向量,可以使用它来连接到 RAM 使能端口,庵后可以根据循环变量的值将不同的位连接到每个 RAM。

对于数据输出总线,可以创建一个 12 位向量并将读取的数据输出连接到该向量的不同 4 位向量。然而,更高效的解决方案是使用由 3 个 4 位向量组成的数组。同样也可以使用循环变量根据需要赋值该数组的不同元素。

下面的 verilog 代码片段展示了如何使用 for generate 语句对这个电路进行设计。


wire [3:0] rd_data [2:0];    //读数据数组
wire [2:0] enable;           //使能信号构建的向量


genvar i;        //循环变量
generate
  for (i=0; i<=2; i=i+1) begin
    ram ram_i (
      .clock    (clock),
      .enable   (enable[i]),
      .wr_en    (wr_en),
      .addr     (addr),
      .wr_data  (wr_data),
      .rd_data  (rd_data[i])
    );
  end
endgenerate

综合这段代码后,将得到如下所示的电路。

Generate If语句

generate if语句可以在设计中有条件地生成 verilog 代码。当设计者只想将代码在特定条件下使用时,可以使用 generate if 语句。与此有关的一个例子是当设计者想在设计中包含一个专门用于测试的功能时,为此可以使用 generate if 语句来确保仅将此函数包含在调试版本的代码中,而不会包含在生产版本代码中。

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

generate
  if (<condition1>) begin
    //要执行的代码
  end
  else if (<condition2>) begin
    //要执行的代码
  end
  else begin
    //要执行的代码
  end
endgenerate

从这个示例可以看到,该语法虽然与verilog中的if语法是类似的,但这两种方法之间存在根本区别。

编写 generate if 语句时,实际上是让编译工具根据某些条件创建代码块的实例。这意味着只有一个分支会被编译,而任何其他分支都将被排除在编译之外。

相反,使用 if 语句时,整个 if 语句将被编译并且可以执行语句的每个分支。每次在仿真期间触发 if 语句时,都会判断条件以确定执行哪个分支。

示例

为了更好地演示generate if 语句的工作原理,请考虑一个基本示例:编写一个输出 4 位计数器值的测试电路。由于这是一个测试功能,所以只需要在使用调试版本时激活它;构建生产版本代码时,将计数器输出接地。为此将使用一个参数来确定何时应该构建调试版本的电路。

下面的代码片段展示了如何实现这个示例。

parameter debug_build = 0;

generate
  if (debug_build) begin
    always @(posedge clock, posedge reset) begin
      if (reset) begin
        count <= 4'h0;
      end
      else begin
        count <= count + 1;
      end
    end
  end
  else begin
    initial begin
      count <= 4'h0;
    end
  end
endgenerate

当 debug_build 被设置为 1 时,综合工具会生成如下所示的电路----四位计数器电路。

但当 debug_build 被设置为 0 时,综合工具则会生成如下所示的电路----计数信号的所有位均接地。

Generate Case语句

generate case 语句可以被用来有条件地在设计中包含 verilog 代码块。generate case 语句基本上与 generate if 语句功能相同,但语法不同。这意味着如果只想在特定条件下将某部分包含在设计中时,设计者也可以使用 generate case 语句。

例如,如果设计者想要设计一个只想包含在调试版本中的测试电路,就可以使用 generate case 语句来确定构建哪个版本的代码。

下面的代码片段展示了generate case 语句的一般语法。

generate
  case (<variable>)
    <value1> : begin
      //当<variable> = <value1>执行这个分支语句
    end
    <value2> : begin
      //当<variable> = <value2>执行这个分支语句
    end
    default : begin
    //其他情况执行这个分支语句
    end
  endcase
endgenerate

从这个示例可以看到,该语法虽然与verilog中的case语法是类似的,但这两种方法之间存在根本区别。

编写 generate case 语句时,实际上是让编译工具根据某些条件创建代码块的实例。这意味着只有一个分支会被编译,而任何其他分支都将被排除在编译之外。

相反,使用 case 语句时,整个 case 语句将被编译并且可以执行语句的每个分支。每次在仿真期间触发 case语句时,都会判断条件以确定执行哪个分支。

示例

由于generate case语句与 if generate 语句功能类似,所以仍以generate if章节中使用的示例为例。下面的 verilog 代码展示了如何使用 generate case 语句来实现这个例子。

parameter debug_build = 0;

generate
  case (debug_build)
    1 : begin
      always @(posedge clock, posedge reset) begin
        if (reset) begin
          count <= 4'h0;
        end
        else begin
          count <= count + 1;
        end
      end
    end
    default : begin
      initial begin
        count <= 4'h0;
      end
    end
  endcase
endgenerate

当 debug_build 被设置为 1 时,综合工具会生成如下所示的电路----四位计数器电路。

但当 debug_build 被设置为 0 时,综合工具则会生成如下所示的电路----计数信号的所有位均接地。


  • 📣您有任何问题,都可以在评论区和我交流📃

  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net

  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【敬伟ps教程】平移、缩放、移动、选区

文章目录平移抓手工具旋转抓手缩放工具移动工具详解选区选区工具详解平移 抓手工具 当打开一张大图时&#xff0c;可以通过修改底部的百分比或使用抓手工具&#xff08;H或在任何时候按住空格键来使用抓手工具&#xff09;来查看更多细节 使用抓手工具时滚动所有打开的文档&…

几何的显式表示 - 曲线和曲面

点云&#xff1a;list of points&#xff08;x, y, z&#xff09; 将点云变成多边形的面&#xff0c;从而在计算机中输出多边形网格&#xff1a;Polygon Mesh 就是 拆成小三角 贝塞尔曲线 定义曲线只要满足起止点即可&#xff0c;P1&#xff0c;P2决定了它要往哪个方向弯 de Ca…

STM32 HAL库PID控制电机 第三章 PID控制双电机

STM32 HAL库PID控制电机 第三章 PID控制双电机 注:本文含全部PID控制代码,保证可以运行,如不能运行可以留言回复 1 基础配置 1.1 编码器电路图及配置 引脚定时器通道PA0TIM2_CH1PA1TIM2_CH2PB6TIM4_CH1PB7TIM4_CH2因此需要把TIM2、TIM4配置为编码器模式。在STM32CubeMX中…

如何在react中处理报错

本文为 360 奇舞团前端工程师翻译 原文地址&#xff1a;https://www.developerway.com/posts/how-to-handle-errors-in-react我们都希望我们的应用能稳定、完美运行&#xff0c;并且能够考虑到每一个边缘情况。但是现实情况是&#xff0c;我们是人&#xff0c;是人就会犯错&…

【区块链】走进web3的世界-DApp如何快速接入wall

在web3中&#xff0c;wall是您进入区块链的一个标识&#xff0c;每个用户使用的wall都不近相同&#xff0c;因此接入更多的wall是很有必要的&#xff0c;从用户角度来说&#xff0c;非必要情况下&#xff0c;我是不愿意去额外下载wall的。因此今天我们来聊一下&#xff0c;DApp…

手把手教你搭建 Docker API 未授权漏洞环境

环境&#xff1a;ubuntu 16.04 我是在虚拟机中安装了ubuntu 16.04的环境&#xff0c;里面暂时没有docker、ssh远程、vim等。 1. 更换国内源 直接输入docker 这里没有安装&#xff0c;那安装下 sudo apt install docker.io 开始安装 因为我没有修改源&#xff0c;所以这个速…

数据的表示和存储1

目录 数制和编码 信息的二进制编码 数值数据的表示 进制计数制 定点数和浮点数 定点数的编码表示 原码表示 补码表示 求特殊数的补码 求真值的补码 求补码的真值 移码表示Excess (biased) notiion C语言中的整数 无符号整数(unsigned integer) 带符号整数&#xff0…

QT sql查询模型,视图显示

SQL模型定义定义sql查询模型查询设置格式创建视图并显示setHeaderData() 函数定义 SQL 查询模型&#xff08;QSqlQueryModel&#xff09;是 Qt 提供的一种数据模型&#xff0c;用于执行 SQL 查询并返回结果集。它继承自 QSqlTableModel 类&#xff0c;可以使用 QSqlQuery 对象执…

【eXtplorer】本地搭建免费在线文件管理器并实现在外远程登录

文章目录1. 前言2. eXtplorer网站搭建2.1 eXtplorer下载和安装2.2 eXtplorer网页测试2.3 cpolar的安装和注册3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置4.公网访问测试5.结语1. 前言 通过互联网传输文件&#xff0c;是互联网最重要的应用之一&#xff0c;无论是网上…

springboot学习2

一、spring boot自动装配原理 pom.xml spring-boot-dependencies 核心依赖在父工程中 在写或者引入一些spring boot依赖的时候&#xff0c;不需要指定版本&#xff0c;因为有这些版本仓库启动器 <dependency><groupId>org.springframework.boot</groupId>&…

使用golang实现日志收集系统的logagent

整体架构 参考 七米老师的日志收集项目 主要用go实现logagent的部分&#xff0c;logagent的作用主要是实时监控日志追加的变化&#xff0c;并将变化发送到kafka中。 之前我们已经实现了 用go连接kafka并向其中发送数据&#xff0c;也实现了使用tail库监控日志追加操作。 我们…

【Java基础】反射详述简单模拟SpringMVC

&#x1f6a9; 本文已收录至专栏&#xff1a;JAVA基础 &#x1f44d;希望能对你有所帮助 一.概述 反射是指对于任何一个Class类&#xff0c;在运行的时候都可以直接得到这个类全部成分&#xff0c;使得我们可以动态操作Java代码&#xff0c;同时反射也破坏了Java的封装性。 例…

如何清理电脑缓存,分享4个简单方法!

案例&#xff1a;如何清理电脑缓存 【朋友们&#xff01;我感觉我电脑的内存已经严重不足了&#xff0c;想清理下电脑的缓存释放空间&#xff0c;却不知如何清理&#xff0c;大家有什么好的方法吗&#xff1f;】 经常使用电脑的朋友可能都会发现&#xff0c;好像我们没下载什…

平台和编译器决定 char 是 signed char 或者 unsigned char

平台和编译器决定 char 是 signed char 或者 unsigned charThe C and C standards allows the character type char to be signed or unsigned, depending on the platform and compiler. Most systems, including x86 GNU/Linux and Microsoft Windows, use signed char, but …

NetCore3.1或Net6.0项目升级到Net7.0

其实与我之前发布的步骤基本一致&#xff0c;升级到net6.0之后&#xff0c;在升级net7.0基本没有可修改的代码&#xff0c;只是升级一些nuget包而已&#xff0c;NetCore3.1升级到Net6.0&#xff0c;可参考此文章&#xff1a;NetCore3.1项目升级到Net6.0_csdn_aspnet的博客-CSDN…

Rust China Conf 2023 筹备启动:议题征集开始

大会介绍Rust China Conf 2023 由 Rust 中文社区发起主办、知名企业和开源组织联合协办&#xff0c;是年度国内规模最大并唯一的 Rust 线下大型会议&#xff0c;深受 Rust 中文社区开发者与相关企业的喜爱与推崇。本次大会为线下会议&#xff0c;将于6月17日-18日在上海举办&am…

Java虚拟机总结

前言 Java是目前用户最多、使用范围最广的软件开发技术之一。Java的技术体系主要由支撑Java程序运行的虚拟机、提供各开发领域接口支持的Java APl、,Java编程语言及许多第三方Java框架&#xff08;如Spring、Struts等&#xff09;构成。在国内&#xff0c;有关Java APl、Java语…

【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式省赛[第一场]程序设计题以及详细题解

文章目录原题展示原题分析原题题解LED相关LCD相关按键相关ADC相关定时器相关PWM输入捕获小结文章福利原题展示 原题分析 今年的第一场比赛绝对np,官方将串口直接省掉了&#xff0c;将其替换成很多小功能&#xff0c;如&#xff1a;切换计时、频率均匀变化、锁机制等等&#xff…

Angular 全屏后选择器 (nz-select) 下拉选项框失效【开发笔记】

问题&#xff1a;Angular 全屏后选择器 (nz-select) 下拉选择无法使用 如图&#xff1a; 相应解决方法的文章&#xff1a;https://medium.com/shahar.kazaz/adding-fullscreen-support-to-ng-zorro-a38140da676 三种解决方法&#xff1a; ① FullscreenOverlyContainer&#…

SAP中用CS20批量修改BOM应用问题处理实例

在应用中可能会遇到这样的情况&#xff0c;用户通过某个工艺或技术上的改进&#xff0c;节约了某个原料的用量&#xff0c;而这个原料可能应用在一批成品上。如果成品数量太大&#xff0c;就需要做批量的变更了。 CS20这个事务应该就是用于做BOM批量处理的&#xff0c;笔者之前…