☰
FPGA层次化工程实战:VHDL结构化描述与元件例化
2026/10/15 6:01:12 网站建设 项目流程

简介:这份资源是面向高校软件工程、电子信息等专业学生的FPGA数字系统设计实验报告,对应西电软工院选课程第1次实验,适合正在学习FPGA开发、VHDL硬件描述语言与Xilinx ISE集成环境的初学者参考。报告围绕层次化工程创建展开,涵盖简单逻辑门的RTL描述、结构化电路描述、VHDL层次结构设计,以及ISE中HDL编辑器的使用,并完整记录了实验原理、实现步骤、测试结果与实验总结,还附有AND_OR顶层模块原理图与底层电路说明。资源包共1个docx文档,约267KB,内容紧凑、结构清晰,可直接对照完成实验报告撰写与代码调试。目前已有344人学习下载,适合需要快速上手FPGA基础实验、理解RTL描述与层次化设计思路的同学借鉴。

1. 从一份实验报告说起:FPGA层次化工程到底练什么

很多人第一次拿到 FPGA 实验报告,看到“层次化工程创建”几个字,第一反应是“不就是画个与门或门吗”。但真正动手把两个独立实体 MY_AND2、MY_OR2 用结构化描述拼成一个顶层 AND_OR,再跑通语法检查、生成原理图,你会发现卡住新手的从来不是逻辑本身,而是工程结构、端口映射和元件例化这三件事。这份实验报告围绕 ISE 集成环境,用 VHDL 完成简单逻辑门的 RTL 描述,再通过层次化方式把底层模块串起来,本质上是一次完整的“自底向上”设计流程演练。它适合刚接触 FPGA 开发、需要把课堂理论落到 ISE 操作上的学生,也适合想回头补一补 VHDL 结构化描述基本功的工程师。下面我按实际复现顺序,把这份报告里的技术点拆开讲透。

2. 层次化工程创建:从新建工程到两个底层实体

2.1 为什么先建工程再写代码

ISE 的工程文件管理方式和现在很多 FPGA 工具不同,它把源码、约束、综合结果、仿真文件全部挂在同一个工程目录下。如果先写代码再补工程,很容易出现源文件路径对不上、综合时找不到顶层模块的情况。常见做法是:打开 ISE 后先通过 File → New Project 建立工程,器件系列和型号按实验平台实际选,仿真器可以选 ISim 也可以先跳过。工程建好后,再通过 Project → New Source 分别添加两个 VHDL Module,命名必须和实体名一致,也就是 MY_AND2 和 MY_OR2。

这里有个细节:ISE 新建 VHDL Module 时会弹出一个端口定义向导,很多人图快直接点下一步跳过,结果实体里没有端口声明,后面例化时顶层找不到信号可连。我一般会在这个向导里把输入输出端口先定义好,或者干脆跳过向导,手动在代码里补全。两种方式都行,但端口名和方向必须和后续顶层例化严格对应。

2.2 MY_AND2 与 MY_OR2 的 RTL 描述

RTL 描述的核心是用 HDL 表达寄存器传输级行为,对于纯组合逻辑门来说,就是用一个并发赋值语句把输入和输出的逻辑关系写清楚。下面是我在复现这份报告时用的两个底层实体代码,端口名和报告里的描述保持一致。

-- MY_AND2.vhd library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity MY_AND2 is Port ( A : in STD_LOGIC; B : in STD_LOGIC; Y : out STD_LOGIC); end MY_AND2; architecture Behavioral of MY_AND2 is begin Y <= A and B; -- 并发赋值,综合后映射为与门 end Behavioral;
-- MY_OR2.vhd library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity MY_OR2 is Port ( A : in STD_LOGIC; B : in STD_LOGIC; Y : out STD_LOGIC); end MY_OR2; architecture Behavioral of MY_OR2 is begin Y <= A or B; -- 并发赋值,综合后映射为或门 end Behavioral;

这两段代码的逻辑说明很直接:Y <= A and B和Y <= A or B都是并发信号赋值,综合工具会把它们分别映射成二输入与门和二输入或门。参数上唯一需要注意的是端口类型统一用STD_LOGIC,不要混用BIT或STD_ULOGIC,否则顶层例化时类型不匹配会报错。另外实体名和文件名必须一致,ISE 对这一点比较敏感,文件名写成my_and2.vhd而实体名是MY_AND2,综合时可能提示找不到实体。

2.3 语法检查与原理图生成

两个底层文件写完后,不要急着写顶层。先在 ISE 里对每个文件单独做一次 Check Syntax,快捷键是右键文件 → Check Syntax。这一步能抓出大部分拼写错误、缺少库声明、端口方向写反的问题。语法通过后,可以右键综合后的模块选择 View RTL Schematic,看工具把and和or综合成了什么。常见情况是看到两个独立的门级符号,输入输出端口和代码一致,说明底层实体没问题。

这里有个容易翻车的地方:如果 Check Syntax 报错说STD_LOGIC未定义,多半是忘了写use IEEE.STD_LOGIC_1164.ALL;。如果报错说实体名不匹配,检查文件名和实体名是否完全一致,包括大小写。ISE 在 Windows 下对大小写不敏感,但综合器内部有时会区分,统一用大写最省事。

3. 顶层 AND_OR 的结构化描述:元件例化与端口映射

3.1 结构化描述和 RTL 描述的区别

RTL 描述关注的是“这个模块做什么”,比如Y <= A and B描述的是与逻辑行为。结构化描述关注的是“这个模块由哪些子模块组成、怎么连”,它不直接写逻辑表达式,而是通过元件例化和端口映射把底层模块拼起来。对于顶层 AND_OR 来说,它内部实例化一个 MY_AND2 和一个 MY_OR2,然后把外部输入信号分别接到两个子模块的输入端,再把两个子模块的输出接到顶层输出端口。

这种描述方式的价值在于层次清晰。底层模块可以单独仿真、单独综合,顶层只负责连线。项目大了以后,每个子模块可以由不同人维护,顶层结构不变,替换底层实现时不影响整体框架。这也是 FPGA 开发里常说的“自底向上”设计流程。

3.2 顶层实体的端口定义

顶层 AND_OR 的端口需要根据实际连接关系来定。假设我们要实现的功能是:两个输入 A、B 分别同时送入与门和或门,输出分别是与结果和或结果。那么顶层需要四个端口:两个输入 A、B,两个输出 Y_AND、Y_OR。实体声明如下。

-- AND_OR.vhd library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity AND_OR is Port ( A : in STD_LOGIC; B : in STD_LOGIC; Y_AND : out STD_LOGIC; Y_OR : out STD_LOGIC); end AND_OR;

端口方向说明:A、B 是输入,Y_AND、Y_OR 是输出。类型全部用STD_LOGIC,和底层模块保持一致。这里不要图省事把两个输出合并成一个总线,除非实验要求明确说要用向量,否则分开写更直观,也方便后面单独看波形。

3.3 元件例化与端口映射的两种写法

结构化描述的核心是component声明和port map例化。VHDL 里端口映射有两种写法:位置映射和命名映射。位置映射要求端口顺序严格对应,一旦底层端口顺序调整,顶层就得跟着改,维护性差。命名映射用端口名 => 信号名的形式,顺序无关,可读性好,我一般优先用命名映射。

-- AND_OR.vhd 架构部分 architecture Structural of AND_OR is -- 声明底层元件 MY_AND2 component MY_AND2 Port ( A : in STD_LOGIC; B : in STD_LOGIC; Y : out STD_LOGIC); end component; -- 声明底层元件 MY_OR2 component MY_OR2 Port ( A : in STD_LOGIC; B : in STD_LOGIC; Y : out STD_LOGIC); end component; begin -- 例化与门,命名映射 U1: MY_AND2 port map ( A => A, B => B, Y => Y_AND ); -- 例化或门,命名映射 U2: MY_OR2 port map ( A => A, B => B, Y => Y_OR ); end Structural;

逻辑说明:component声明告诉综合器“我要用一个叫 MY_AND2 的元件,它的端口是这样”,port map则把顶层信号和元件端口一一对应起来。U1、U2是例化标签,同一个元件多次例化时标签必须不同。参数上需要注意的是,port map左边的端口名必须和 component 声明里的端口名完全一致,右边的信号名必须是顶层实体或架构里已经存在的信号。如果右边写了一个未声明的信号名,综合会报“signal not declared”。

3.4 综合后看原理图验证层次结构

顶层写完后,把 AND_OR 设为顶层模块,右键综合,然后 View RTL Schematic。正常情况下你会看到两个层次:顶层是 AND_OR,里面包含两个子模块 MY_AND2 和 MY_OR2,子模块内部再展开成门级符号。如果原理图里只看到一个扁平的门电路,没有层次,说明综合器把层次展开了,可以在综合选项里把“Keep Hierarchy”设为 Yes 或 Soft,再看一次。

这一步的验证价值在于:它能直观告诉你端口映射有没有连错。比如你把与门的输出连到了 Y_OR,原理图上会显示 Y_OR 接在与门输出端,一眼就能看出来。比跑仿真看波形更快。

4. 仿真测试与常见翻车点排查

4.1 写一个最小 Testbench 验证逻辑

原理图看的是结构,逻辑对不对还得靠仿真。ISE 里可以新建一个 VHDL Test Bench 文件,也可以手写一个简单的测试平台。下面是我常用的最小 Testbench,给 A、B 各扫一遍四种组合,观察 Y_AND 和 Y_OR 的输出。

-- AND_OR_tb.vhd library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity AND_OR_tb is end AND_OR_tb; architecture Behavioral of AND_OR_tb is signal A : STD_LOGIC := '0'; signal B : STD_LOGIC := '0'; signal Y_AND : STD_LOGIC; signal Y_OR : STD_LOGIC; begin -- 例化被测顶层 UUT: entity work.AND_OR port map ( A => A, B => B, Y_AND => Y_AND, Y_OR => Y_OR ); -- 激励过程 process begin A <= '0'; B <= '0'; wait for 10 ns; A <= '0'; B <= '1'; wait for 10 ns; A <= '1'; B <= '0'; wait for 10 ns; A <= '1'; B <= '1'; wait for 10 ns; wait; end process; end Behavioral;

逻辑说明:Testbench 里用signal声明内部信号,通过port map把被测模块的端口和这些信号连起来。process里按时间顺序给 A、B 赋值,wait for 10 ns让每个组合保持 10 纳秒,方便波形观察。参数上唯一要注意的是wait语句不能写在 process 外面,否则仿真器会报错。仿真跑完后,在 ISim 里看波形,预期结果是:Y_AND 只在 A=1、B=1 时为 1,Y_OR 在 A=0、B=0 时为 0,其余为 1。

4.2 常见问题排查

现象一:综合报错“Entity MY_AND2 is not found”。原因通常是底层文件没有添加到工程里,或者文件名和实体名不一致。解决方法是检查工程目录下是否有 MY_AND2.vhd,以及文件里的实体名是否严格写成 MY_AND2。

现象二:顶层例化时报“Port Y of MY_AND2 is not connected”。原因是port map里漏写了某个端口的映射。命名映射虽然顺序无关,但每个端口都必须出现。解决方法是对照 component 声明逐个检查,确保 A、B、Y 都有对应。

现象三:仿真波形里 Y_AND 和 Y_OR 一直是红色或未知态。原因是 Testbench 里没有给输入信号赋初值,或者被测模块的端口没有正确连接。解决方法是给 A、B 一个初始值,比如signal A : STD_LOGIC := '0';,并检查port map里的信号名是否和 Testbench 里声明的一致。

现象四:原理图里看不到层次结构。原因是综合选项里 Keep Hierarchy 没有打开。解决方法是右键 Synthesize → Process Properties,把 Keep Hierarchy 设为 Yes 或 Soft,重新综合。

现象五:Check Syntax 通过但综合报“Multiple drivers”错误。原因是同一个信号在多个地方被赋值。比如在顶层架构里既写了Y_AND <= A and B;,又例化了 MY_AND2 把输出连到 Y_AND。解决方法是结构化描述里只保留例化,不要再写并发赋值语句。

5. 从这次实验延伸出去:层次化设计的几个实用习惯

层次化工程创建这个实验看起来简单,但它练的是 FPGA 开发里最基础也最容易被忽视的能力:把一个大设计拆成可独立验证的小模块,再用顶层把它们连起来。我后来做更大的项目时,发现几个习惯越早养成越好。

第一,底层模块的端口命名尽量统一。比如所有模块的输入都叫 A、B,输出都叫 Y,顶层例化时不容易连错。如果每个模块端口名都不一样,顶层port map写起来容易串行。

第二,每个底层模块单独写一个最小 Testbench。不要等顶层写完再一起仿真,底层模块单独验证通过后,顶层出问题的概率会小很多。这次实验里 MY_AND2 和 MY_OR2 各跑一遍四种输入组合,加起来不到二十行代码,但能省掉后面大量排查时间。

第三,顶层文件里只放例化和连线,不放逻辑表达式。结构化描述的意义就在于把行为和结构分开。如果顶层里又写Y_AND <= A and B;又例化 MY_AND2,综合器会报多驱动错误,而且层次结构也会乱掉。

第四,综合后先看 RTL Schematic,再看 Technology Schematic。RTL 图看的是代码结构,Technology 图看的是映射到具体器件后的资源。这次实验里 RTL 图能看到两个子模块,Technology 图里会看到 LUT 和 IO 缓冲,对照着看能理解综合器到底做了什么。

第五,养成看综合日志的习惯。ISE 的综合报告里会列出每个模块的资源占用、端口连接情况、警告信息。很多问题在日志里已经有提示,只是被忽略了。比如“Port Y_AND is connected to multiple drivers”这种警告,翻到日志末尾就能看到。

从那以后我每次新建 FPGA 工程,都会先把底层模块和对应 Testbench 建好,单独跑通后再写顶层。这个顺序看起来多花了几分钟,但后面省下的调试时间远不止这几分钟。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询