如何编写一个基本的 Verilog Module(模块)

news2024/12/28 20:40:05

1、概述

这篇文章主要介绍了 Verilog 在 FPGA 设计中的概念和使用方法。首先讨论使用模块(module)关键字构造 Verilog 设计的方式,以及这与所描述的硬件的关系。这包括对参数、端口(port)和例化(instantiaton)的讨论及一个完整示例。

虽然不需要为了使用它而讨论Verilog的整个发展历史,但是却必须考虑另一个重点——所使用的Verilog语言版本。Verilog 的主要标准发布于1995年(Verilog-1995)和2001年(Verilog-2001)。除此之外,2005年(Verilog-2005)还发布了另一个小更新。

原则上,我们应该为创建的任何新 FPGA 设计使用 Verilog-2001 或 Verilog-2005 标准。然而,我们可能仍然会遇到基于 Verilog-1995 标准的遗留设计。因此,我们将在这些文章中看到 Verilog-1995 标准和 Verilog-2001 年标准之间的重要区别。


2、构建 Verilog 代码

当我们设计 FPGA 时,需要记住一个基本的原则——我们是在设计硬件,而不是在编写软件程序。因此,我们必须描述许多不同电子组件(electronic component)的行为,然后将它们连接在一起。这反映在 Verilog 文件的结构方式中。

对于每个电子组件,我们需要知道组件的外部接口。此信息使我们能够将其连接到我们系统中的其他组件。我们还需要知道组件的行为方式,以便我们可以在我们的系统中使用它。

在 Verilog 中,我们使用称为模块(module)的构造来定义此信息。Verilog中的module相当于VHDL中的 entity architecture pair。

下面的代码片段显示了在 Verilog 中声明模块的一般语法。

module (        //module开始
    //定义参数
    parameter <parameter_name> = <default_value>
)
<module_name> (
    //IO定义
    <direction> <data_type> <size> <port_name>
);

    //完整的RTL代码

endmodule        //module结束

在此构造中,<module_name> 是正在设计的模块的名称。虽然我们可以在一个文件中声明多个模块,但最好让一个文件对应一个模块(好的开发习惯)。保持文件名和模块名相同也是一种很好的做法。这会让管理包含许多层级的大型设计变得更加简单。

在 Verilog 中,我们使用 // 字符来表示我们正在编写注释。我们使用注释来包含有关我们代码的重要信息,这些信息可能会帮助到其他人理解代码。Verilog 编译器会忽略我们在注释中写的任何内容。

在上面的 Verilog 代码片段中,我们可以看到这一点,因为这些注释仅用于描述代码的功能。


2.1、Verilog-1995 模块(Modules)

Verilog 模块声明语法作为 verilog-2001 标准的一部分进行了更新。这意味着用于在 Verilog 中声明模块的方法会略有不同。

使用 Verilog-1995 代码时,我们仅在初始模块声明中定义端口名称。然后我们在模块主体中定义每个端口的方向、数据类型和大小。除此之外,我们还在模块中定义了模块参数。

下面的代码片段显示了如何使用 Verilog-1995语法来声明模块。

module <module_name> (
    // All IO names are defined here
    <port_name>
);
    //在这里声明参数,可选项
    parameter <parameter_name> = <default_value>;
    
    //IO定义
    <direction> <data_type> <size> <port_name>;
    
    //完整的RTL代码
    
endmodule

2.2、Verilog 模块中的参数(Parameters

参数(parameter)是常量(constant)的局部形式,我们可以使用它来配置 Verilog 中的模块。当我们在设计的另一部分实例化我们的模块时,可以为参数分配一个值来配置模块的行为。

由于参数的作用域有限,所以我们可以多次调用同一个模块,每次都为参数赋不同的值。因此,参数允许我们随时修改模块的行为。

参数是 Verilog 模块声明的可选部分,在大多数情况下我们并不需要使用它们。然而,参数允许我们编写更通用的模块接口,这些接口更容易在其他 Verilog 设计中被复用。

在模块中声明了一个参数之后,我们就可以像使用普通变量一样使用它。但是,必须记住--参数是一个常量值,所以只能读取它。因此,我们只能在参数声明时为其赋值。


2.3、功能代码

我们使用模块 IO 声明下方的空间来定义模块的功能。我们最常为此使用RTL(Register Transfer Level),但也可以编写结构代码或描述性原语。

我们使用关键字 module 开始一个模块的设计,从关键字 module 到关键字 endmodule 内的所有内容构成了一个完整的模块。

当我们完成描述模块行为的代码时,使用 endmodule 关键字来作为结束。在此关键字之后编写的任何代码都不会包含该模块中。


2.4、Verilog 模块端口(Ports)

我们在模块声明中使用端口来定义 Verilog 模块的输入和输出。可以近似地认为这些端口相当于传统电子元件中的管脚。

下面的代码片段显示了我们用来声明端口的一般语法。

<direction> <data_type> <size> <port_name>

<direction> :我们可以在 Verilog 模块中将端口定义为in(输入)、out(输出)或 inout(双向端口)。上述构造中的可用于执行此操作。

<port_name> :字段用于为端口指定一个唯一的名称。

<data_type> :声明端口的数据类型,最常见的类型是reg 和 wire 。

<size> :用来表述端口多位向量的位数。当我们定义向量类型输入的大小时,我们必须指出向量中的最高有效位(most significant bit,MSB)和最低有效位(least significant bit,LSB)。因此,我们在声明端口大小时使用 [MSB:LSB]。

下面的示例显示了名为 example_in 的 8 位输入的声明。

input wire [7:0] example_in;

在此示例中,[7:0] 表示第 7 位是最高有效位。这称为小字节序或低字节序(little endian),是 FPGA 设计中最常用的表示方法。

如果我们将声明改为 [0:7],也可以将 MSB 定义为位置 0。这种称为大字节序或高字节序(big endian)的表示方法在设计 FPGA 时不像小字节序那样常用。


2.5、Verilog 中的 Reg 和 Wire 类型

Verilog中的数据类型有很多,但是最常用的只有两种--reg 和 wire。

wire 类型被用来来声明信号,这些信号在我们的 Verilog 代码中是简单的点对点连接(电线)。因此,电线不能驱动数据,也不能存储数值。顾名思义,它们大致相当于传统电路中的一根导线。

reg 类型被用来声明一个信号,该信号在我们的 Verilog 代码中主动驱动数据。顾名思义,它们大致相当于传统数字电路中的触发器(flip flop)。

由于wire类型是基本的点对点连接,所以我们可以在声明 Verilog 模块时将wire用作in或者out类型。相反,reg 类型只能用于 Verilog 模块中的输出。

wire类型主要被用来构建组合逻辑电路,当我们使用 assign 关键字在 Verilog 中对组合逻辑建模时,我们只能将其与wire类型一起使用。

reg 类型主要被用来构建时序逻辑电路,我们必须使用always块(always block)来为时序逻辑电路建模。我们只能在 always 块中使用 reg 类型。

在声明模块端口时,其默认数据类型是wire。因此,当我们使用wire端口时,可以省略掉wire这个关键字,但是reg关键字则不能被省略。


3、Verilog 模块例化(Instantiation

我们可以调用已经在设计的另一部分编写的 Verilog 模块。在 Verilog 中调用模块的过程称为实例化(实例化,instantiation)。

每次例化一个模块时,都会创建一个唯一的对象(Object),它有自己的名称、参数和端口。

在 Verilog 设计中,我们将每个例化模块称为模块的实例(instance)。我们使用例化来创建许多不同的实例,并用它们来构建更复杂的设计。

我们可以认为Verilog中的模块例化相当于在传统电子电路中放置了一个电子元件。一旦我们创建了设计中需要的所有实例,就必须将它们互连以创建一个完整的系统。这与在传统电子系统中将组件接线在一起完全相同。

Verilog 为我们提供了两种可用于模块实例化的方法——命名例化(named instatiation)和位置例化(positional instatiation)。

Verilog-2001 标准编支持以上两种例化方式,而Verilog-1995标准只支持位置例化方法。


3.1、位置例化方法

在 Verilog 中使用位置例化方法时,需要使用有序列表来连接模块端口。被例化的模块实例的端口顺序必须与我们模块中声明端口的顺序一样。

例如,如果我们依次声明时钟clock,复位rst,那么我们在例化时就必须先将时钟信号连接到模块端口。

下面的 Verilog 代码片段显示了位置模块例化方法的一般语法。

<module_name> # (
    //例化的参数列表
    <parameter_value>
)
<instance_name> (
    //例化的端口,需要按照被例化模块的顺序排列出来
    <signal_name>, //第一个端口
    <signal_name>  //第二个端口
);

<module_name> :必须与在声明模块时给它的名称相匹配。

<instance_name>:设计中的例化模块的唯一名称。

这种方法可能难以维护,因为我们的端口顺序可能会随着我们设计的发展而改变。

下面看一个简单的例子来展示如何使用位置例化方法。对于这个例子,我们将创建一个如下所示的简单电路的实例。

当我们使用位置例化方法时,模块声明中端口的顺序很重要。下面的代码片段显示了如何为这个电路声明一个模块。

and_or (
  input a,
  input b,
  input c,
  output logic_out
);

下面的 Verilog 代码片段显示了如何使用位置例化方法来创建此模块的实例。

and_or example_and_or (
 in_a,
 in_b,
 in_c,
 and_or_out
);

3.2、命名例化方法

在 Verilog 中使用命名例化方法时,我们需要明确定义将信号连接到的端口的名称。与位置例化方法不同的是--声明端口的顺序并不重要。

这种方法通常优于位置例化方法,因为它生成的代码更易于阅读和理解。它也更容易维护,因为我们可以修改端口而不必担心我们声明它们的顺序。

下面的 Verilog 代码片段显示了命名模块实例化的一般语法。

<module_name> # (
    //被例化的参数与例化的参数
    .<parameter_name> (<parameter_value>)
)
<instance_name> (
    //被例化的端口与例化的端口
    .<port_name> (<signal_name>),
    .<port_name> (signal_name>)
);

<module_name>、<parameter_name> 和 <port_name> 必须与在定义模块时使用的名称相匹配。

<instance_name> 对位置例化和命名例化具有相同的功能。

使用上一节用到的电路作为简单案例来展示如何使用命名例化方法。

下面的 Verilog 代码片段显示了我们如何使用命名例化创建此模块的实例。

and_or example_and_or (
    .a (in_a),
    .b (in_b),
    .c (in_c),
    .logic_out (and_or_out)
);

4、完整的Verilog module示例

为了完全理解我们在这篇文章中讨论的所有概念,让我们看一个基本示例。

在这个例子中,我们将创建一个使用参数的同步计数器电路,然后实例化它的两个实例。

这些实例之一将在输出中具有 12 位,而另一个将只有 8 位。

我们将在这里排除这些模块的 RTL,因为我们还没有学会如何编写它。相反,我们将简单地定义模块的 IO 以及它们之间的互连。

计数器模块将有两个输入——时钟和复位——和一个输出——计数器值。

除此之外,我们还需要一个参数来定义输出中的位数。

下面的代码片段显示了使用 verilog 2001 和 verilog 1995 兼容代码的计数器模块的声明。

// Verilog-2001
module counter #(
    parameter WIDTH = 8
)
(
    input clock,
    input reset,
    output reg [WIDTH-1:0] count
);



// Verilog-1995
module counter (
    clock,
    reset,
    count
);

paramater WIDTH = 8;

input clock;
input reset;
output reg [WIDTH-1:0] count;

endmodule

我们现在需要一个模块--用它来例化这个计数器的两个实例。该模块将有两个输入--时钟和复位以及两个来自例化计数器的输出。

在计数器模块中,我们定义了默认的计数器输出为 8 位。这意味着我们可以在不改变参数值的情况下实例化 8 位计数器。但是,当例化 12 位计数器时,则必须将 WIDTH 参数的值设置为 12。

下面的代码片段显示了使用命名例化方法连接到端口时此模块的代码。

module top_level (
    input clock,
    input reset,
    output reg [7:0] count_8,
    output reg [11:0] count_12
);

//例化一个8bit的计数器
counter 8bit_count (
    .clock (clock),
    .reset (reset),
    .count (count_8)
);

//例化一个12bit的计数器
counter #(.WIDTH (12)) 12_bit_count (
    .clock (clock),
    .reset (reset),
    .count (count_12)
);

endmodule

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

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

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


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

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

相关文章

JSON Web Token (JWT)

1&#xff0c;什么是JWT JWT是JSON Web Token是简称&#xff0c;是一个行业开发标准&#xff08;RFC 7519&#xff09;定义了一种简介的 自包含的协议格式&#xff0c;用于在通信双方传递JSON对象&#xff0c;传递的信息经过数字签名可以被验证和信任。它可以使用HMAC算法或使…

4.如何靠IT逆袭大学?

学习的动力不止于此&#xff1a; IT逆袭 这两天利用工作空余时间读了贺利坚老师的《逆袭大学——传给 IT 学子的正能量》&#xff0c;感触很多&#xff0c;有些后悔没有好好利用大学时光。 不过人都是撞了南墙再回头的&#xff0c;吃一堑长一智。 这本书无论你是工作了还是…

DataX简介、部署、原理和使用介绍

DataX简介、部署、原理和使用介绍 1.DataX简介 1-1.项目地址 项目地址&#xff1a;https://github.com/alibaba/DataX 官方文档&#xff1a;https://github.com/alibaba/DataX/blob/master/introduction.md 1-2.DataX概述 ​ DataX 是阿里云 DataWorks数据集成 的开源版本…

JDK安装环境变量配置

jdk下载地址:https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html 下载安装包和exe都可。 安装JDK 安装JDK时&#xff0c;除了修改安装目录&#xff0c;其他的一路【下一步】&#xff0c;傻瓜式安装。 &#xff08;注&#xff1a;当提示安装JRE时…

WPF MVVM系统入门-上

WPF MVVM系统入门-上 Models:存放数据的模型&#xff0c;实体对象 Views:可视化界面 ViewModels:业务逻辑。ViewModels与Models的联系会更紧密&#xff0c;而Views页面会主动绑定ViewModels中的数据&#xff0c;原则上ViewModels不要直接去操作Views&#xff0c;被动的被Vie…

教唆chat ai 吵架--chatGPT和chatBing体验

教唆chat ai 吵架–chatGPT和chatBing体验 请注意&#xff0c;本文主观性非常高&#xff0c;只是一个参考性文章&#xff0c;无任何其他含义。 当我们谈到人工智能对话模型时&#xff0c;ChatGPT和ChatBing是两个备受关注的模型。它们都是自然语言处理领域中的重要里程碑&…

hive学习(仅供参考)

hive搭建Hive什么是hiveHive的优势和特点hive搭建解压、改名修改环境变量添加hive-site.xml将maven架包拷贝到hive替换一下gua包使环境变量生效初始化安装成功Hive 什么是hive 将结构化的数据文件映射为数据库表 提供类sql的查询语言HQL(Hive Query Language) Hive让更多的人…

【C++内存管理机制】学习笔记(4):重载operate new/::operator new..../new()

目录 简介C++应用程序 分配内存的途径重载::operator new/::operator delete重载operator new/operator delete重载new()/delete()结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简…

自学软件测试?一般人“别来沾边”...

本人7年测试经验&#xff0c;在学测试之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说&#xff1a;自学软件测试&#xff0c;一般人我还是劝你算了吧&#xff1f;因为我就是那个一般人&#xff01; 软件测试基础真的很简单&…

mac环境,安装NMP遇到的问题

一 背景 项目开发中,公司项目需要使用本地的环境运行,主要是php这块的业务。没有使用docker来处理,重新手动撸了一遍。记录下其中遇到的问题; 二 遇到的问题 2.1 Nginx的问题 brew install nginx后,启动nginx,报错如下:nginx: [emerg] no "ssl_certificate" …

数据结构与算法(二):线性表

上一篇《数据结构与算法&#xff08;一&#xff09;&#xff1a;概述》中介绍了数据结构的一些基本概念&#xff0c;并分别举例说明了算法的时间复杂度和空间复杂度的求解方法。这一篇主要介绍线性表。 一、基本概念 线性表是具有零个或多个数据元素的有限序列。线性表中数据…

零售电子标签解决方案

电子货架标签系统&#xff08;ESLs&#xff09;&#xff0c;是一种放置在货架上、可替代传统纸质价格标签的电子显示装置&#xff0c; 每一个电子货架标 签通过有线或者无线网络与商场计算机数据库相连&#xff0c; 并将最新的商品价格通过电子货架标签上的屏显示出来。 电子…

2023年数学建模美赛D题(Prioritizing the UN Sustainability Goals)分析与编程

2023年数学建模美赛D题分析建模与编程 重要说明&#xff1a; 本文介绍2023年美赛题目&#xff0c;并进行简单分析&#xff1b;本文首先对 D题进行深入分析&#xff0c;其它题目分析详见专题讨论&#xff1b;本文及专题分析将在 2月17日每3小时更新一次&#xff0c;完全免费&am…

使用chatgpt生成快速入眠笔记

以下是使用chatgpt生成快速入眠笔记的简单过程 可以发现&#xff0c;增加详细两个字&#xff0c;可以让它表述的更明白。 通过询问“还有其他方法吗”&#xff0c;获取更多可能性&#xff0c;当然你也可以直接说继续 但实测继续有时候不会记住上一条提问 详细讲解一下程序员怎…

类似LeetCode的登录页面(小程序版)

前言每一个项目都会有用户端的注册和登录页面&#xff0c;对于刚入门的小白来说&#xff0c;在UI设计方面不太擅长&#xff0c;就算大致的UI界面设计出来了&#xff0c;但是落实到代码上来实现的时候就很容易卡住。这篇博客主要介绍的就是仿作一个类似LeetCode登录的简约大方页…

离线环境轻量级自动化部署

流程图&#xff1a; 常规系统发布的痛点 服务器频繁重启&#xff0c;上面部署的应用服务不能随之重启&#xff0c;导致服务时常宕机应用手动部署相对比较麻烦&#xff0c;步骤繁琐应用发布环境取决于发布人本地环境&#xff0c;导致不同发布人每次发布环境不一致&#xff0c;导…

【玩转多核异构】双核高速率CAN-FD评测——飞凌嵌入式

为了能够让更多的工程师朋友了解多核异构处理器&#xff0c;飞凌嵌入式特别推出了【玩转多核异构】专题&#xff0c;帮助大家解决在多核异构处理器的开发过程中遇到的问题。【玩转多核异构】专题持续更新中&#xff0c;欢迎您的持续关注。引言凭借实时性、抗干扰性和安全性等优…

Redis 开发规范

原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elastic-Job 源码解析分布式事务中间件 TCC-Transaction 源…

详细总结Ansible中使用playbook

文章目录前言一、Playbook的功能二、YAML三、playbook执行命令1.使用ansible-playbook部署ftp服务&#xff0c;并开启匿名用户访问权利2.使用ansible-playbook部署apache服务&#xff0c;设定默认发布文件内容为www.westos.org3.tags&#xff1a;标签四、使用vim解决yaml书写格…

使用git中可能出现的问题

问题1&#xff1a;如果遇到自己的文件在远程仓库dev分支被别人修改了&#xff0c;自己在本地仓库test分支继续在写代码先拉取最新的代码 覆盖本地dev分支 TortoiseGit->Pull被修改如图2.拉取最新的代码(拉取成功后 本地dev分支user有四条属性)3.切换到自己的分支tortoiseGit…