构建自定义硬件系统
本教程将介绍如何使用 Qsys 系统集成工具,利用 Intel® FPGA IP 库中提供的 IP 来创建自定义现场可编程门阵列 (FPGA) 硬件设计。
概述
本教程将介绍如何使用 Qsys* 系统集成工具,利用 Intel® FPGA IP 库中提供的 IP 来创建自定义现场可编程门阵列 (FPGA) 硬件设计。Qsys 通过创建 IP 块之间的可配置互连来加速嵌入式系统设计。创建自己 IP 块的开发者可以将其发布到 Qsys IP 库中,以便在自己的系统中重复使用。完成本教程后,您将拥有一个如图下方的框图所示的系统设计。
级别:初学者
材质
硬件
Windows* 或 Linux* 开发主机 PC
软件
以下是必需的
已安装 Intel® Quartus® Prime 设计软件。可以是 Quartus® Prime Lite 版本软件或 Quartus® Prime Standard 版本软件,但不是 Intel® Quartus® Prime Pro 版本软件。这已在 为您的第一个 FPGA 设备编程 教程中完成。
来自“为您的第一个 FPGA 设备编程”教程的已完成的 Intel® Quartus® 软件项目。如果您尚未完成该教程,您可以
- 单击 此处 完成“为您的第一个 FPGA 设备编程”教程。
- 单击 此处下载 zip 文件,其中包含“为您的第一个 FPGA 设备编程”教程的已完成项目文件。将文件解压缩到 PC 上的一个唯一项目文件夹中。
- 项目文件应放在哪里?
-
在选择项目目录时,请遵循以下几点指南:
- 不要将项目放在 Intel® Quartus® 工具目录内。新的 Intel Quartus 版本每六个月发布一次,因此将其放置在特定版本目录中会在安装新版本后使其成为“孤儿”。更糟糕的是,如果删除旧的工具版本,您可能会丢失它们。
- 避免使用名称中带有空格的路径,因为有些工具不喜欢目录路径中的空格。
- 使用您拥有读/写访问权限的目录。这听起来很直观,但有时 IT 部门会限制管理员权限。请确保您创建的文件夹不需要管理员权限。
注意:使用早期或更高版本的 Intel® Quartus® Prime 软件套件时,用户体验可能会有所不同。本文档中的屏幕截图基于 17.0 版本。
创建 Qsys* 系统
本节介绍创建 Qsys 系统所需的步骤。假设您已在 Intel Quartus 开发软件中打开了来自“为您的第一个 FPGA 设备编程”教程的已完成的 **blink** 项目。
1. 启动 Intel Quartus 软件,从 **文件** 菜单中单击 **打开项目**,然后选择前一个教程中的 **blink** 项目 (blink.qpf)。
2. 单击工具栏上的 **Qsys** 按钮,或从 **工具** 菜单中选择 Qsys,来启动 Qsys。
3. Qsys 打开一个未保存的系统,其中已实例化一个时钟源组件。要添加另一个 IP 块,请使用左侧栏中的 **IP Catalog** 窗格。在搜索栏中,开始输入“on-chip memory”,直到看到 **On-Chip Memory (RAM or ROM)** IP。
添加片上内存
1. 双击片上内存组件(或用鼠标选中它并单击 **添加…** 按钮)以在新系统中插入一个新实例。这将打开片上内存 IP 的参数对话框。将 **Slave S1 Data Width** 更改为 **64**,并将 **Total Memory Size** 字段更改为 **65536**,使其成为一个 64 千字节的片上内存,支持 64 位访问,然后单击对话框底部的 **完成** 按钮。
2. 在系统中实例化了新的内存组件后,我们应该为其指定一个更有意义的名称。要在 Qsys 中重命名组件,请选中组件,然后右键单击以弹出上下文菜单,并选择 **重命名**。将片上内存从 **onchip_memory2_0** 重命名为 **oncram_64k**。
3. 要在 Qsys 中连接组件,请使用 **System Contents** 窗格中的 **Connections** 列。类似类型的连接可能显示为交叉点处的圆圈,并以灰色显示。单击与 ocram_64k 的 **clk1** 和 **reset1** 信号关联的圆圈,将它们连接到时钟和复位源。
请注意,片上 RAM (S1) 上的 Avalon Memory Mapped Slave 连接尚未有可用连接。该连接要等到添加了具有 Avalon Memory Mapped Master 的 IP 后才会出现,这将在稍后的步骤中完成。
另外,请注意,将鼠标悬停在连接点上会显示一个工具提示,详细说明要连接的接口。在构建大型系统时,这非常有用。
添加其他 IP 核遵循相同的过程。要完成系统,请添加以下 IP 核,并为每个 IP 核连接时钟和复位接口。
4. 添加第二个小型 **On-Chip Memory (RAM or ROM)**,数据位宽为 32 位,内存大小为 16 字节。这个看似冗余的组件的重要性将在后面的步骤中解释。将此组件重命名为 **default_16b**。
添加并行 I/O
1. 添加一个 8 位输出的 **PIO (Parallel I/O)** 组件。将此组件重命名为 **led_pio**。
2. 将 **external_connection** 接口导出为 **led_pio**,方法是双击 **Export** 列中的 **external_connection** 接口旁的区域,然后编辑导出名称,使其显示为 **led_pio**。
3. 添加一个 1 位输入的 PIO 组件。将此组件重命名为 **button_pio**,并将 **external_connection** 接口导出为 **button_pio**。
4. 添加一个 4 位输入的 PIO 组件。将此组件重命名为 **switch_pio**,并将 **external_connection** 接口导出为 **switch_pio**。
添加系统 ID 外设
1. 添加一个 **System ID** 组件,并分配一个可识别的系统 ID 值(在 IP Catalog 搜索栏中键入“System ID”进行查找)。将 ID 值输入为 **0xde10de10**。
- 系统 ID 外设的目的是什么?
-
System ID 外设是一个简单的只读 IP 核,为 Qsys 系统提供唯一的标识符。包含 CPU(如 Nios® II 处理器)的 Qsys 系统使用系统 ID 核来验证编译的可执行程序是否针对目标 FPGA 中配置的实际硬件映像。如果可执行文件中预期的 ID 与 FPGA 中的系统 ID 核不匹配,则该软件可能无法正确执行,并且处理器可以采取适当的操作(例如,打印警告消息并停止)。
2. 将此组件重命名为 **system_id**。
添加 JTAG 主设备
1. 添加一个 **JTAG to Avalon Master Bridge**。保留默认实例名称 **master_0**。
2. 确保所有外设的时钟和复位都已连接。
3. JTAG 桥提供一个 Avalon 主设备,Qsys 会识别相似的接口并显示可能的连接。将 JTAG 桥主设备连接到其他外设的 **Avalon Memory Mapped Slave** 端口。
4. 将 **master_0 Reset Output** 连接到其他 IP 块的 **Reset Input** 端口。
分配基地址
Qsys 会为每个从设备添加一个初步的地址范围,但最初它们会重叠,在 **Messages** 窗格中产生错误。
通过以下方式解决地址范围重叠错误:
1. 将 **ocram_64k** 片上内存地址范围锁定在 **0x0000_0000** 开始,方法是单击 **Base** 列中地址旁边的锁定图标。
2. 单击 **System** 菜单,然后选择 **Assign Base Addresses**。观察所有 IP 块的新基地址,并且片上内存的地址没有改变。
通常,**Assign Base Addresses** 会通过将所有外设从最大地址范围到最小地址范围进行打包来创建最紧凑的地址映射。不能保证生成的地址在多次调用 **Assign Base Addresses** 命令后保持不变,但锁定的地址范围不会被 **Assign Base Addresses** 命令调整。为了方便后续教程,请确保每个 IP 的基地址与下图匹配;它们可以通过双击地址本身进行编辑。
3. 右键单击 **System Contents** 视图中的某个列名,然后选择 **Show Default Slave Column** 选项。向右滚动,并为 **default_16b** 片上内存勾选 **Default Slave** 复选框。
- 什么是“默认从设备”,为什么我需要它?
-
默认从设备是一个重要的概念。如果向未映射的地址范围发起了内存映射的读或写事务,Qsys 会将其路由到该互连区域中该主设备的默认从设备。如果您不指定默认从设备,那么 Qsys 默认会选择该互连上具有最大可用地址范围的外设。在大多数情况下,这会是某种形式的内存,因为它们通常是系统中地址范围最大的外设。
选择一个默认从设备允许设计者明确指定所有未映射的地址访问都应路由到该从设备。在本示例教程中,这些访问将路由到一个微小的片上内存,该内存未用于其他任何用途,从而消除了主内存或任何其他外设中数据可能损坏的可能性,并为设计者提供了一个空间来检查是否存在错误的访问。
该默认从设备可以通过各种响应来参数化,以应对错误的访问,例如:
- 从不响应(这实际上会将主设备保持在无限等待请求状态)
- 用特定的数据模式进行回复
- 生成 Avalon 错误响应
- 生成中断
- 生成复位
生成硬件
1. Qsys 系统已完成,现在是时候将其集成回 Intel Quartus 软件项目了。从 **文件** 菜单中,选择 **保存**,并使用名称 **soc_system.qsys**。
2. 保存完成后关闭保存窗口。然后,从 **Generate** 菜单中,选择 **Generate HDL...**。这将打开 Generate 窗口,取消选中 **Create block symbol file (.bsf)** 复选框,然后单击 **Generate** 按钮。
3. Qsys 将生成 IP 和互连的 HDL 文件。完成后,单击 **Close** 按钮,然后单击 Qsys 窗口底部的 **Finish** 按钮。这将关闭 Qsys,并显示有关将 **.qip** 文件添加到 Intel Quartus 软件项目的说明。
将 Qsys* 系统集成到 Intel Quartus 软件项目中
本节介绍如何将新生成的 Qsys 输出整合到现有的 **blink** 项目中。
1. 从 **Assignments** 菜单中,选择 **Settings** 来打开 **Settings** 窗口。
2. 在左侧的 **Category** 列表中选择 **Files**。单击 **File Name** 框右侧的 **...** 按钮来浏览文件。
3. 按照关闭 Qsys 时对话框中描述的路径(参见图 22)导航到 **.qip** 文件。
4. 选择 .qip 文件,然后单击 **Open** 按钮将文件添加到项目中。
5. 单击 **Apply** 按钮,然后单击 **Ok** 按钮关闭设置窗口。Qsys 会为生成的系统创建一个实例化模板文件,在本例中,它称为 **soc_system_inst.v**,位于 **blink** 项目文件夹中现在存在的 **soc_system** 文件夹内。使用此文件在顶层 HDL 文件中正确实例化 Qsys 生成的系统会很方便。
6. 单击 **Project Navigator** 下拉菜单,然后选择 **Files**。
7. 双击文件名打开 **blink.v** 文件。
8. 将 **blink.v** 中的现有代码替换为以下代码
//
// Copyright (c) 2017 Intel Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
// create module
module blink(
input clk, // 50MHz FPGA input clock
input [1:0] push_button, // KEY[1:0]
input [3:0] switch, // SW[3:0]
output [7:0] leds // LED[7:0]
);
// Create a power on reset pulse for clean system reset on entry into user mode
// We create this with the altera_std_synchronizer core
wire sync_dout;
altera_std_synchronizer #(
.depth (20)
) power_on_reset_std_sync_inst (
.clk (clk),
.reset_n (1'b1),
.din (1'b1),
.dout (sync_dout)
);
// Create a qsys system reset signal that is the logical AND of the power on
// reset pulse and the KEY[0] push button
wire qsys_system_reset;
assign qsys_system_reset = sync_dout & push_button[0];
/*
Qsys system instantiation template from soc_system/soc_system_inst.v:
soc_system u0 (
.button_pio_export (<connected-to-button_pio_export>), // button_pio.export
.clk_clk (<connected-to-clk_clk>), // clk.clk
.led_pio_export (<connected-to-led_pio_export>), // led_pio.export
.reset_reset_n (<connected-to-reset_reset_n>), // reset.reset_n
.switch_pio_export (<connected-to-switch_pio_export>) // switch_pio.export
);
*/
soc_system u0 (
.button_pio_export (push_button[1]), // button_pio.export
.clk_clk (clk), // clk.clk
.led_pio_export (leds), // led_pio.export
.reset_reset_n (qsys_system_reset), // reset.reset_n
.switch_pio_export (switch) // switch_pio.export
);
endmodule
- 这个设计中的同步器有什么作用?
-
当系统通电时,FPGA 从外部闪存设备进行配置,然后进入“用户”模式。当 FPGA 从配置模式释放到用户模式时,这是异步进行的,可能会违反设计时序约束并导致设计不稳定。同步器核提供了一个短暂且稳定的复位脉冲,该脉冲是同步释放的,允许设计在 FPGA 配置完成后干净地进入运行状态。如果您有兴趣查看一个可以提供上电复位功能的 Qsys 组件,请查看 这个公共 Git 仓库 中提供的 power_on_reset 组件。
9. 单击工具栏上的 **Start Analysis & Elaboration** 按钮,让 Intel Quartus 软件处理新的 HDL。当被问及是否要保存更改时,请选择 **Yes**。
Intel Quartus 软件处理 Qsys 生成的系统时,会有更多消息出现。此过程将识别 HDL 源文件中的任何错误或它们在项目中的配置方式,同时还会告知 Intel Quartus 软件我们已在我们顶层模块中声明的新顶层端口。
10. 新的顶层模块声明的输入和输出比原始设计多得多(例如 led_pio、button_pio 等),因此需要为这些输入和输出分配新的引脚约束,并可以移除过时的引脚约束。Terasic DE10-Nano 用户手册包含从中推导出这些引脚分配的图和原理图。
从 **Assignments** 菜单中,打开 **Pin Planner** 工具。通过右键单击 **Node Name**,选择 **Edit**,然后按 **Delete** 来删除过时的引脚(LED),该引脚来自之前的设计。
11. 按照下图分配新引脚。与图像匹配的重要列是 **Location, I/O Standard, Current Strength,** 和 **Slew Rate**。
12. 关闭 Pin Planner。
更新时序约束
1. 通过用以下代码替换 **blink.sdc** 的内容来更新 **blink.sdc** 中的时序约束,以匹配新声明的端口。通过双击 **blink.sdc** 文件打开它。
- 什么是 SDF 文件,我为什么需要它?
-
SDC 代表 Synopsys Design Constraints,它是一种行业标准格式,用于定义硬件(硅)设计的时序约束,例如设备的 Targe 频率和与外部外设的时序。SDC 文件为 Intel Quartus 提供了验证生成系统是否满足其时序要求的方法。
在 FPGA 的综合过程中,Intel Quartus 会调用一个名为 TimeQuest Timing Analyzer* 的设计工具,该工具读取时序约束文件,计算 FPGA 内部信号的时序,并将这些时序与 SDC 文件规定的时序要求进行比较。会生成一个报告,验证时序是否满足,以及/或识别未能满足时序要求且需要优化的信号。
2. 用以下代码替换 **blink.sdf** 中的现有代码
#
# Copyright (c) 2017 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# inform quartus that the clk port brings a 50MHz clock into our design so
# that timing closure on our design can be analyzed
create_clock -name clk -period "50MHz" [get_ports clk]
derive_clock_uncertainty
# inform quartus that the PIO inputs and outputs have no critical timing
# requirements. These signals are driving LEDs and reading discrete push button
# and switch inputs, there are no timing relationships that are critical for any
# of this
set_false_path -from [get_ports {switch[0]}] -to *
set_false_path -from [get_ports {switch[1]}] -to *
set_false_path -from [get_ports {switch[2]}] -to *
set_false_path -from [get_ports {switch[3]}] -to *
set_false_path -from * -to [get_ports {leds[0]}]
set_false_path -from * -to [get_ports {leds[1]}]
set_false_path -from * -to [get_ports {leds[2]}]
set_false_path -from * -to [get_ports {leds[3]}]
set_false_path -from * -to [get_ports {leds[4]}]
set_false_path -from * -to [get_ports {leds[5]}]
set_false_path -from * -to [get_ports {leds[6]}]
set_false_path -from * -to [get_ports {leds[7]}]
set_false_path -from [get_ports {push_button[0]}] -to *
set_false_path -from [get_ports {push_button[1]}] -to *
# Define timing constraints for the JTAG IO pins so that Quartus properly closes
# timing on these signal paths. Otherwise we could have unreliable JTAG
# communication with the device over the USB Blaser II connection.
# NOTE: the 'altera_reserved_tck' clock is automatically defined by Quartus
set_input_delay -clock altera_reserved_tck -clock_fall 3 [get_ports {altera_reserved_tdi}]
set_input_delay -clock altera_reserved_tck -clock_fall 3 [get_ports {altera_reserved_tms}]
set_output_delay -clock altera_reserved_tck 3 [get_ports {altera_reserved_tdo}]
3. 单击顶部工具栏中的 **Start Compilation** 按钮来编译整个设计。当被问及是否要保存更改时,请选择 **Yes**。
生成头文件
您的设计成功编译后,我们将进行最后一项活动,为下一个将在 Terasic DE10-Nano 板上使用此系统的教程做准备。由于我们在 Qsys 中定义了一个内存映射的嵌入式系统,并连接了一个 JTAG 主设备到多个从外设,因此我们需要知道从外设的基地址,以便我们能够与它们交互,执行读写事务。这些基地址信息在 Qsys 中捕获。您可以通过多种方式在 Qsys 中可视化它们,但对于软件开发人员或其他使用该系统编写代码的用户来说,它们并不方便。
每次生成 Qsys 系统时,Qsys 都会输出一个名为 *<your-system-name>.sopcinfo* 的数据库文件。Intel Quartus 软件提供了一个实用程序,可以将 SOPCINFO 数据库信息转换为一种可用的宏格式,该格式可用于各种目的,称为 **sopc-create-header-files**。**sopc-create-header-files** 的默认功能是从 Qsys 系统中每个主设备的角度创建 C 风格的头文件宏。我们将在 TCL Console 中执行此操作,该控制台位于默认 Intel Quartus 软件 GUI 的中间下方。
1. 如果看不到 TCL Console 窗格,可以通过选择 **View > Utility Windows > TCL Console** 菜单来打开它。
我们将首先创建一个目录来输出头文件,然后使用 **sopc-create-header-files** 创建默认的头文件输出,最后我们将从 FPGA fabric 中连接到 JTAG 主设备组件 master_0 的 FPGA 外设的头文件中提取基地址条目。将以下文本复制并粘贴到 tcl 控制台中。
# make a directory called ’qsys_headers’ to store the header files
file mkdir qsys_headers
# create a TCL variable SCHF_PATH to hold the path to the executable program
# sopc-create-header-files on your host PC using the environment variables
# provided by Quartus.
set SCHF_PATH [glob -join $quartus(quartus_rootpath) sopc_builder bin sopc-create-header-files]
# create a TCL variable BAT_PATH to hold the path to the Nios II Command Shell
# batch file on Windows platforms. The following code sequence will work on
# either Windows or Linux. For linux this variable will just be set to NULL.
set BAT_PATH {}
if {$tcl_platform(platform) == "windows"} {
set BAT_PATH [glob -join $quartus(quartus_rootpath) .. nios2eds {Nios II Command Shell.bat}]
}
# execute sopc-create-header-files to generate the header files
eval exec -ignorestderr ${BAT_PATH} ${SCHF_PATH} soc_system.sopcinfo --output-dir qsys_headers
# read the header file for master_0 into a TCL variable
set master_0_header [read [open [glob -join qsys_headers master_0.h] r]]
# output the C macro lines for the FPGA peripheral base addresses
foreach line [split ${master_0_header} "\n"] { \
if {[string match "*_BASE*" ${line}]} {puts ${line}}}
#
如果您已正确组装 Qsys 系统并正确执行了上述命令,您应该会在 TCL 控制台中看到以下输出,这些输出代表连接到 master_0(JTAG 主设备桥接组件)的五个 Qsys 外设的基地址定义。
好了!您已经设计并编译了您的第一个 Qsys 系统。这个基本设计旨在成为下一个教程的垫脚石。继续进行 **使用 System Console 调试 FPGA 硬件** 教程,在该教程中我们将演示如何使用 System Console 工具来编程 FPGA 并与此设计进行交互。