HPS SoC和FPGA联合使用例程

本教程演示了如何使用HPS/ARM与FPGA进行通信。我们将为DE10标准开发板介绍如何根据官方的DE10_Standard_GHRD工程开发出自己的My_GRHD工程。之后,我们会在My_GHRD工程上运行我们自己改造过后的HPS_FPGA_LED应用程序。该应用程序会控制连接到DE10标准开发板上FPGA部分的A的10个LED灯闪烁。其中,LEDR0将会通过闪烁模拟板子的心跳,LEDR1-8将会按照一定频率进行左移右移循环闪烁60次,LEDR9将会在LEDR1-8的每一次循环后闪烁一次。例程中HPS通过Lightweight HPS-to-FPGA Bridge控制FPGA部分连接的LED灯。而且,例程中FPGA部分是由HPS通过HPS中FPGA manager进行配置。

背景要求

假定开发人员已经具有以下背景知识:

FPGA RTL级设计
  • 基本的Quartus Prime操作技能
  • 基本的RTL编程技能
  • 基本的Qsys操作技能
  • 关于Memory-Mapped 接口的知识
C语言设计
  • 基本的SoC(Embedded Design Suite)操作技能
  • C语言编程和编译技巧
  • 使用配套的image镜像文件为DE10-Standard板创建Linux引导SD卡的技能
  • 在DE10-Standard板上从SD卡引导Linux的技能以及拷贝文件进入DE10-Standard板上Linux文件系统的基本Linux命令操作技能

系统要求

在开始本教程之前,请注意演示项目准备需要以下内容:

友晶DE10-Standard FPGA板,包括
  • 用于串口中断通信的Mini USB线缆
  • 存储空间4G以上Micros SD卡
  • Micros SD卡读卡器
一台x86架构的PC机
  • 装有64位Windows 7操作系统
  • 配有一个USB端口
  • 装有Quartus Prime 16.1或以上版本
  • 装有SoC EDS 16.1 或以上版本
  • 装有Win32 Disk Imager软件

Intel SoC FPGA的AXI 桥

在Intel SoC FPGA中,HPS逻辑与FPGA结构通过AXI(Advanced eXtensible Interface)桥进行连接。为了实现HPS逻辑与FPGA 结构的通信,需要通过使用Intel系统集成工具Qsys添加HPS组件来进行系统设计。从HPS组件的AXI主端口,HPS可以访问那些连接到AXI主端口的内存映射从端口。

HPS包含以下HPS-FPGA AXI桥

  • FPGA-to-HPS桥
  • HPS-to-FPGA桥
  • 轻量级的HPS-to-FPGA桥

下图显示了FPGA结构和L3与HPS互连的AXI桥的框图。每个主(M)接口和从(S)接口显示其数据宽度。括号中标注了每个互连的时钟域。
HPS SoC和FPGA联合使用例程_第1张图片
HPS-to-FPGA桥由3级(L3)主开关控制,轻量级控制HPS-to-FPGA桥由L3从外围从开关控制。

FPGA-to-HPS桥接控制L3主开关,允许任何在FPGA结构的主实现(implemented)去访问在HPS中大多数从实现。

所有三个桥都包含全局程序员视图GPV寄存器。GPV寄存器控制网桥的行为。通过轻量级的HPS-to-FPGA桥访问可以所有三个桥的GPV寄存器。

这个例程向用户介绍了如何使用HPS/ARM与FPGA进行通信。这个与DE10-Standard板上ARM C程序配套的GHRD工程,演示了HPS/ARM程序如何控制连接到FPGA部分的红色LED。

GRHD工程

术语GHRD是黄金硬件参考设计(Golden Hardware Reference Design)的简称。友晶科技为DE10-Standard开发板提供的GRD项目位于CD文件夹中: CD-ROM\Demonstration\SOC_FPGA\ DE10_Standard_GHRD。

本项目由以下组成部分组成:

  • ARM Cortex™-A9 MPCore HPS
  • 四个用户按钮输入
  • 十个用户DIP开关输入
  • 10个用户I/O用于LED输出
  • 64KB片上存储器
  • • JTAG to Avalon master bridges
  • Interrupt capturer for use with System Console
  • System ID

MPU所看到的SoC FPGA部分的系统外围设备的内存映射从轻量级的HPS-to-FPGA到FPGA的基础地址0xFF20_0000开始。MPU可以通过Qsys中的地址偏移来访问这些外围设备。用户可以打开GHRD项目与Quartus II软件。然后打开soc_system。qsys文件与qsys工具。下图列出了连接到轻量级HPS-to-FPGA的外围设备的地址映射。
HPS SoC和FPGA联合使用例程_第2张图片
这些外设的所有Avalon引出信号( Avalon Conduit signals)都连接到DE10-Standard板上SoC FPGA的I/O引脚,如图所示
HPS SoC和FPGA联合使用例程_第3张图片

My_GHRD工程

在实际的应用过程中,友晶科技提供的DE10_Standard_GHRD工程并不是十分契合我们的开发需要,这可能包括外围设备的冗余和不足。下面为了实现本教程开篇所定的目标,我们需要根据DE10_Standard_GHRD工程进行修改。其中友晶科技提供的DE10_Standard_GHRD工程实现的是LEDR0通过闪烁模拟板子的心跳,LEDR1-9按照一定频率进行左移右移循环闪烁60次;我们的目标是LEDR0通过闪烁模拟板子的心跳,LEDR1-8按照一定频率进行左移右移循环闪烁60次,LEDR9在LEDR1-8的每一次循环后闪烁一次。可以看出,我们只需要将DE10_Standard_GHRD工程中的LEDR9的设计进行更改就可以打到我们的目的。下面正式开始更改操作。

准备设计文件

新建文件夹:E:\work\Quartus\My_GHRD,然后将DE10_Standard_GHRD工程目中的一下文件复制到工程目录下:

  • 文件夹:hps_isw_handoff
  • 文件夹:ip
  • 文件:DE10_Standard_GHRD.v
  • 文件:DE10_Standard_GHRD.sdc
  • 文件:generate_hps_qsys_header.sh
  • 文件:hps_common_board_info.xml
  • 文件:soc_system.qsys
  • 文件:soc_system_board_info.xml

因为其中的文件DE10_Standard_GHRD.v后面将会被指定为顶层文件,所以需要将其更改为与工程名My_GHRD相同的My_GHRD.v文件,同时为了与工程名保持协调,我们也将文件DE10_Standard_GHRD.sdc更改为与工程名My_GHRD相同的My_GHRD.sdc。最终工程目录文件夹如下图所示:
HPS SoC和FPGA联合使用例程_第4张图片

创建My_GHRD工程

首先,建立一个名为:My_GHRD的工程。打开软件Quartus Prime Standard Edition,点击:File→New Project Wizard…,进入工程创建引导界面。
HPS SoC和FPGA联合使用例程_第5张图片
点击引导界面下方的:Next,进入Directory, Name, Top-Level Entity设置界面。
HPS SoC和FPGA联合使用例程_第6张图片
点击右侧按钮:…选择你的工程目录E:/work/Quartus/My_GHRD。之后设置工程名:My_GHRD。此时软件将自动把顶层设计entity name设置为工程名:My_GHRD。继续点击:Next,进入Project Type设置界面。
HPS SoC和FPGA联合使用例程_第7张图片
在Project Type界面选择:Empty Project选项,继续点击:Next,进入Add Files设置界面。
在Add Files设置界面,点击右侧按钮:…,添加工程目录下的文件:My_GHRD.v和soc_system.qsys以及文件夹hps_isw_handoff和ip下的所有文件进入工程。
HPS SoC和FPGA联合使用例程_第8张图片
HPS SoC和FPGA联合使用例程_第9张图片
然后点击:Next,进入Family, Device & Board Settings设置界面。

在Family, Device & Board Settings设置界面,我们要设置我们所用的SoC FPGA型号:5CSXFC6D6F31C6N。为了能够快速锁定我们要选择的SoC FPGA型号,我们可以点击:Device下拉列表,选择:Cyclone V SX Extended Features,以缩小选型范围。
HPS SoC和FPGA联合使用例程_第10张图片
然后,选择Available devices下的5CSXFC6D6F31C6。继续点击:Next,进入EDA Tool Settings设置界面。
HPS SoC和FPGA联合使用例程_第11张图片
在EDA Tool Settings设置界面下,我们保留默认设置。之后点击:Fnish,完成工程创建,退出工程创建引导界面。

Qsys设计

由于我们需要在DE10_Standard_GHRD工程的基础上更改Qsys的设计来实现对LEDR9的单独控制,所以我们需要在Qsys中添加一个1位的PIO端口来实现对LEDR9的控制,同时需要将原来用于同时控制LEDR0-9的10位PIO端口的改为9位的PIO端口。

依次点击:Tools→Qsys,启动Qsys,之后选中要打开的soc_system.qsys文件。
HPS SoC和FPGA联合使用例程_第12张图片
HPS SoC和FPGA联合使用例程_第13张图片
接下来,在打开的soc_system.qsys文件添加一个1位的PIO端口。在左上角的IP Catalog的搜索框中搜索“ PIO ”,之后双击下面列表中的“ PIO(Parallel I/O) ”。HPS SoC和FPGA联合使用例程_第14张图片
此时将弹出“ PIO(Parallel I/O) ”设置界面。将PIO的数据宽度Width设置为:1,数据输入输出方向Direction设置为:Output。最后点击右下角的Finish按钮退出设置。
HPS SoC和FPGA联合使用例程_第15张图片
现在,新的Qsys设计中将会出现我们新添加的PIO端口pio_0,我们可以对其进行重命名。光标移动到在端口名pio_0处,右键选择“Rename”,重命名为:led_pio9。点击空白处保存命名。
HPS SoC和FPGA联合使用例程_第16张图片
之后我们需要对led_pio9组件的信号进行连接,将光标放置到下图中led_pio9组件中clk信号所在行的白点处,点击后可以将led_pio9组件中clk信号与clk_0组件的clk信号进行连接,可以表示为:led_pio9.clk—clk_0.clk。使用类似的操作,将led_pio9组件的其它信号分别与其它组件进行如下连接,最终的连接关系如下:

  • led_pio9.clk—clk_0.clk
  • led_pio9.reset—clk_0.clk_reset
  • led_pio9.s1—mm_bridge_0.m0—fpga_only_master.master

如下图:
HPS SoC和FPGA联合使用例程_第17张图片
其中led_pio9有一个信号led_pio9.external_connection需要进行对外引出。光标置于信号led_pio9.external_connection处右击选择:Connections:led_pio9.external_connection→Export as: led_pio9_external_connection。
HPS SoC和FPGA联合使用例程_第18张图片
到此,添加一个1位的PIO端口led_pio9的操作已经完成。

接下来需要将原来控制LEDR0-9的数据宽度为10位的PIO端口led_pio更改为只用控制LEDR0-8的数据宽度为9位的PIO端口led_pio。双击组件led_pio,将数据宽度Width(1-32 bits)设置为:9,关闭Parameters窗口将自动保存更改。
HPS SoC和FPGA联合使用例程_第19张图片
现在,我们要为我们新添加的PIO端口led_pio9分配基地址,同时更新Qsys设计中其它组件的基地址。依次点击:System→Assign Base Addresses。
HPS SoC和FPGA联合使用例程_第20张图片
可以看出,至此,我们需要在Qsys设计中做的更改已经完成了。现在,只要重新生成相应的HDL文件,更新Qsys设计就可以结束Qsys部分的设计了。但是后面对工程编译时软件将会输出错误提示:
Error (10228): Verilog HDL error at soc_system_sysid_qsys.v(34): module “soc_system_sysid_qsys” cannot be declared more than once。
更令人遗憾的是,目前本人还没有找到解决上述错误信息的办法。有一个无奈的办法就是:将Qsys设计中的soc_system_sysid_qsys组件删除。这不会影响Qsys设计的功能,关于该组件功能的详细介绍,读者可以自行查找,此处不再赘述。
删除soc_system_sysid_qsys组件的办法是:将soc_system_sysid_qsys组件sysid_qsys的Use列方框中的“√”去掉。
HPS SoC和FPGA联合使用例程_第21张图片
之后进行Qsys设计中的文件生成工作。点击右下角的Generate HDL…按钮。
HPS SoC和FPGA联合使用例程_第22张图片
在弹出的“Generation”窗口中,确认Output Directory下Path所指定的生成文件的输出目录在工程目录下。然后点击Generate按钮。
HPS SoC和FPGA联合使用例程_第23张图片
接下来会弹出“Save System Completed”窗口,点击右下角的Close按钮。
HPS SoC和FPGA联合使用例程_第24张图片
接下来会弹出的“Generate”窗口,等待几分钟,文件生成工作完成后,右下角的“Close”按钮会高亮显示。此时,点击“Close”按钮。
HPS SoC和FPGA联合使用例程_第25张图片
最后,又会退回到Qsys设计页面,点击右下角的Finish按钮,退出Qsys设计。

在返回Quartus Prime Standard Edition主界面后,会提示我们把更新后的.qip和.sip文件重新手动添加到工程中来。点击提示窗口右下角的Close按钮。
HPS SoC和FPGA联合使用例程_第26张图片
现在,我们来手动添加更新后的.qip和.sip文件。右击Project Navigator窗口中工程名:My_GHRD。点击Settings…选项。
HPS SoC和FPGA联合使用例程_第27张图片
接下来会弹出Category界面,点击右侧的…按钮。
HPS SoC和FPGA联合使用例程_第28张图片
此时会弹出Select File界面,在指定的目录下找到生成的soc_system.qip文件并选中,然后点击右下角的“打开”按钮。
HPS SoC和FPGA联合使用例程_第29张图片
最后,可以在返回的Catagory界面中可以看到生成的soc_system.qip文件已经被成功添加到工程中。
HPS SoC和FPGA联合使用例程_第30张图片

在上述界面中可以看到文件:hps_reset_bb.qip和hps_reset_bb.v,这一组文件的定义了模块:hps_reset。但是,由于 文件:hps_reset.qip和hps_reset.v这一组文件同样也定义了模块:hps_reset,所以hps_reset_bb.qip和hps_reset_bb.v的加入会导致模块:hps_reset的重复定义。

现在我们需要将文件:hps_reset_bb.qip和hps_reset_bb.v进行移除。选中文件:hps_reset_bb.qip和hps_reset_bb.v,点击右侧的Remove按钮移除文件。最后,点击右下角的OK按钮退出该界面。
HPS SoC和FPGA联合使用例程_第31张图片

至此,工程My_GHRD中Qsys部分的设计已经完成。接下来我们需要对工程中的硬件描述文件.v文件进行修改。其实,我们在DE10_Standard_GHRD工程的基础上只用对My_GHRD中顶层实体文件My_GHRD.v中各个模块的引脚及其连接关系进行描述即可。

在此之前,我们先指定我们的顶层实体文件My_GHRD.v。点击Project Navigator窗口中Hierachy按钮,会弹出下拉列表,我们点击File选项。
HPS SoC和FPGA联合使用例程_第32张图片
找到工程中的My_GHRD.v,右击选择Set as Top-Level Entity。
HPS SoC和FPGA联合使用例程_第33张图片
我们双击Project Navigator窗口中工程名My_GHRD,此时软件将会打开工程的顶层实体文件My_GHRD.v
HPS SoC和FPGA联合使用例程_第34张图片

硬件描述设计

对顶层实体文件My_GHRD.v的修改主要有以下各处:
第一,顶层实体模块的命名须与工程名My_GHRD保持一致。
HPS SoC和FPGA联合使用例程_第35张图片
第二,在模块soc_system u0中对新引出的PIO端口led_pio9_external_connection_export进行声明。可以发现,我们声明端口led_pio9_external_connection_export的同时需要为其指定一个wire类型的变量进行连接。对照led_pio_external_connection_export连接到wire类型的变量fpga_led_internal上,我们新定义了一个wire类型的变量fpga_led9_internal与端口led_pio9_external_connection_export进行连接。当然,wire类型的变量fpga_led9_internal可以理解为是一条导线,它的一端接到了模块soc_system u0中对新引出的PIO端口led_pio9_external_connection_export上,两外一端也是要为它连接适当的端口的。我们新增端口led_pio9_external_connection_export的目的就是能够对LED灯LEDR9进行控制,所以,不难想到,wire类型的变量fpga_led9_internal另一端一定是要连接到端口LEDR[9]上面的。这样,总体的更改就是下面这样的:

  • 将原来与LEDR[9:1]连接的变量wire [8:0] fpga_led_internal改为:与LEDR[8:1]连接的wire
    [7:0] fpga_led_internal
  • 定义新的变量wire fpga_led9_internal与LEDR[9]连接
  • 将新的变量wire fpga_led9_internal另一端与模块soc_system u0中的端口led_pio9_external_connection_export进行连接

HPS SoC和FPGA联合使用例程_第36张图片
HPS SoC和FPGA联合使用例程_第37张图片

My_GHRD工程编译

现在,我们可以对工程My_GHRD进行编译操作。首先是进行工程的分析与综合,依次点击:Processing→Start→Start Analysis & Synthesis。
HPS SoC和FPGA联合使用例程_第38张图片
工程的分析综合通过后,主界面中左侧的Tasks窗口下的Analysis & Synthesis栏前面会展示一个绿色的“√”,后面会显示分析综合所用的时间。
HPS SoC和FPGA联合使用例程_第39张图片
上面分析综合成功后,下一步需要进行Fitter(Place & Route),这里进行的是关于FPGA内部的布局布线操作。在此之前我们需要对我们模块中设计的输入输出引脚进行引脚分配。引脚分配的方法有很多种,读者可以自行学习。这里由于我们定义了大量的引脚,而且我们有现成的DE10_Standard_GHRD工程可以利用,我们只需将该工程里面的引脚分配信息转移进入我们的My_GHRD工程就可以了。

我们现在打开DE10_Standard_GHRD工程。进入工程后,我们点击:Assignments→Pin Planner,进入引脚分配界面。
HPS SoC和FPGA联合使用例程_第40张图片
在该界面下,点击:File→Export…。
HPS SoC和FPGA联合使用例程_第41张图片
之后会弹出Export窗口。我们需要选择我们要导出的文件格式为:.tcl。
HPS SoC和FPGA联合使用例程_第42张图片
然后,点击右下角的Export按钮。
HPS SoC和FPGA联合使用例程_第43张图片
最后,依次点击引脚分配界面和DE10_Standard_GHRD工程界面右上角的关闭按钮,退出DE10_Standard_GHRD工程。

我们现在已经得到了我们需要的引脚分配文件DE10_Standard_GHRD.tcl,现在我们需要把它从DE10_Standard_GHRD工程目录下复制到My_GHRD工程目录下。为了与My_GHRD工程名保持一致,将其改名为:My_GHRD.tcl。
HPS SoC和FPGA联合使用例程_第44张图片
接下来,我们需要利用得到的引脚分配文件My_GHRD.tcl对工程My_GHRD中的引脚进行分配。点击:Tools→Tcl Scripts…。
HPS SoC和FPGA联合使用例程_第45张图片
在弹出的Tcl Scripts窗口下,点击右侧的Add to Project…按钮。
HPS SoC和FPGA联合使用例程_第46张图片
在弹出的Add to Project窗口下,选中我们的引脚分配文件My_GHRD.tcl,并点击右下角的打开按钮。
HPS SoC和FPGA联合使用例程_第47张图片
此时,软件又自动退回到Tcl Scripts窗口下。在Libraries栏中找到我们的引脚分配文件My_GHRD.tcl,点击下方的Run按钮来加载我们的引脚分配文件。
HPS SoC和FPGA联合使用例程_第48张图片

在后面弹出的界面中点击OK按钮,退回到Tcl Scripts窗口。
HPS SoC和FPGA联合使用例程_第49张图片
最后点击到Tcl Scripts窗口下方的Close按钮退出到Tcl Scripts窗口,回到工程主界面。
HPS SoC和FPGA联合使用例程_第50张图片
此时,点击主界面的:Assignments→Pin Planner进入引脚分配界面。
HPS SoC和FPGA联合使用例程_第51张图片
在引脚分配界面中,可以看到,引脚大部分已经被分配完成,未分配的引脚暂时不用担心它们,我们还没有用到。
HPS SoC和FPGA联合使用例程_第52张图片
现在,我们开始对工程进行布局布线。点击:Processing→Start→Start Fitter。
HPS SoC和FPGA联合使用例程_第53张图片
工程的布局布线通过后,主界面中左侧的Tasks窗口下的Fitter(Place & Route)栏前面会展示一个绿色的“√”,后面会显示布局布线所用的时间。
HPS SoC和FPGA联合使用例程_第54张图片
到此,基本上我们可以判断在逻辑上我们的设计已经没有问题了,接下来我们是可以进行工程的全编译了。但是,在实际的应用设计中我们还要考虑对信号的时序进行约束,尤其是具有 SDRAM DDR3这种高速存取器件的设计中。对于时序约束,我们可以利用DE10_Standard_GHRD工程中的时序约束文件:DE10_Standard_GHRD.sdc。同样的,将其复制到My_GHRD工程目录下,改名为:My_GHRD.sdc。
HPS SoC和FPGA联合使用例程_第55张图片
将时序约束文件添加进入工程,右击Project Navigator窗口下的工程名:My_GHRD,点击:Setting…按钮。
HPS SoC和FPGA联合使用例程_第56张图片
接下来会弹出Category界面,点击右侧的…按钮。
HPS SoC和FPGA联合使用例程_第57张图片
此时会弹出Select File界面,进入工程主目录下,点击右下角的文件类型选择按钮,选中:Script Files( * .tcl*.sdc*.pdc*.qip)一项。
HPS SoC和FPGA联合使用例程_第58张图片
下找到时序约束文件My_GHRD.sdc并选中,然后点击右下角的“打开”按钮。
HPS SoC和FPGA联合使用例程_第59张图片
最后,可以在返回的Catagory界面中可以看到时序约束文件:My_GHRD.sdc文件已经被成功添加到工程中。依次点击右下角的Apply按钮和OK按钮退出该界面。
HPS SoC和FPGA联合使用例程_第60张图片
此时已经回到工程主界面,我们可以进行工程的全编译了。点击Processing→Start Compilation。
HPS SoC和FPGA联合使用例程_第61张图片

C语言设计

本节将介绍如何设计一个ARM C程序来控制PIO控制器led_pio。SoC EDS用于编译C项目。为了让ARM程序控制PIO组件led_pio,我们需要led_pio地址。使用Linux内置驱动程序’ /dev/mem '和mmap system-call可以将led_pio组件的物理基址映射到Linux应用软件可以直接访问的虚拟地址。

HPS头文件

ARM C程序需要led_pio的组件信息,因为程序会尝试控制组件。这部分将会描述如何使用一个给定的Linux shell批处理文件将Qsys HPS信息提取到一个头文件中,这个头文件稍后将包含在C程序中。

上面提到的批处理文件名为:generate_hps_qsys_header.sh。它与DE10_Standard_GHRD工程位于同一个文件夹中。现在,我们要为工程My_GHRD生成头文件。首先,从DE10_Standard_GHRD工程目录下复制文件:generate_hps_qsys_header.sh到My_GHRD工程目录下。
HPS SoC和FPGA联合使用例程_第62张图片
接下来,使用记事本打开批处理文件:generate_hps_qsys_header.sh,确认文件中红线标注的路径下有应用程序:sopc-create-header-files.exe。其中,路径:/cygdrive/d/…表示D盘…。如果该路径下没有上述应用程序,读者需自行更改其中的路径。
HPS SoC和FPGA联合使用例程_第63张图片
注意,如果后面在SoC EDS命令shell中运行generate_hps_qsys_header.sh时,提示出现错误。(其实这个错误当时解决后忘记进行记录,好像是提示执行文件sopc-create-header-files的过程中sopcinfo2swinfo还是swinfo2header找不到之类的。)我们需要在文件sopc-create-header-files中锁定错误出现的行。对无法找到的应用程序路径进行指定。最终文件sopc-create-header-files中对应部分应该为:

cmd="sopcinfo2swinfo --input=$sopc_design_file --output=$swinfo_tmp_fname ${sopcinfo2swinfo_args[@]}"
/cygdrive/d/intelFPGA/Quartus16.1/quartus/sopc_builder/bin/sopcinfo2swinfo --input="$sopc_design_file" --output="$swinfo_tmp_fname" ${sopcinfo2swinfo_args[@]} || {
    echo "$PN: $cmd failed"
    exit 1
}

cmd=“swinfo2header --swinfo s w i n f o t m p f n a m e < / s p a n > − − s o p c < s p a n c l a s s = " t o k e n v a r i a b l e " > swinfo_tmp_fname --sopc swinfotmpfname</span>sopc<spanclass="tokenvariable">sopc_design_file s w i n f o 2 h e a d e r a r g s [ @ ] < / s p a n > " < / s p a n > / c y g d r i v e / d / i n t e l F P G A / Q u a r t u s 16.1 / q u a r t u s / s o p c b u i l d e r / b i n / s w i n f o 2 h e a d e r − − s w i n f o < s p a n c l a s s = " t o k e n s t r i n g " > " < s p a n c l a s s = " t o k e n v a r i a b l e " > {swinfo2header_args[@]}" /cygdrive/d/intelFPGA/Quartus16.1/quartus/sopc_builder/bin/swinfo2header --swinfo " swinfo2headerargs[@]</span>"</span>/cygdrive/d/intelFPGA/Quartus16.1/quartus/sopcbuilder/bin/swinfo2headerswinfo<spanclass="tokenstring">"<spanclass="tokenvariable">swinfo_tmp_fname --sopc s o p c d e s i g n f i l e < / s p a n > " < / s p a n > < s p a n c l a s s = " t o k e n s t r i n g " > " < s p a n c l a s s = " t o k e n v a r i a b l e " > sopc_design_file" " sopcdesignfile</span>"</span><spanclass="tokenstring">"<spanclass="tokenvariable">{swinfo2header_args[@]} || {
echo P N < / s p a n > : < s p a n c l a s s = " t o k e n v a r i a b l e " > PN: PN</span>:<spanclass="tokenvariable">cmd failed”
exit 1
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

一定注意,我们之间的Quartus安装路径是不同的,你需要根据自己的安装路径指定应用程序的路径,以便软件能够找到它们。

然后,启动SoC EDS命令shell
HPS SoC和FPGA联合使用例程_第64张图片
在SoC EDS Command Shell中,转到My_GHRD工程目录下。其中我的工程目录为:E:\work\Quartus\My_GHRD。读者根据自己的工程目录路径设置,为了输入方便,可以使用Tab键自动补全功能。注意:在E盘,路径就是:/cygdrive/e/…。

HPS SoC和FPGA联合使用例程_第65张图片
执行批处理文件:generate_hps_qsys_header.sh by
HPS SoC和FPGA联合使用例程_第66张图片
此时,会在My_GHRD工程目录下生成HPS头文件:hps_0.h。
HPS SoC和FPGA联合使用例程_第67张图片
现在,我们已经得到所需的头文件:hps_0.h。我们可以用一些代码查看编辑软件查看一下它里面的内容,我这里用的代码查看编辑软件是Source Insight。可以看到,生成的头文件:hps_0.h中已经为我们新增的PIO模块led_pio9生成了一系列宏定义。在头文件中,led_pio9的基地址由一个常量LED_PIO9_BASE表示,led_pio9的数据宽度由一个常量LED_PIO9_DATA_WIDTH表示,这两个常量将会在后面的C语言代码中被使用。
HPS SoC和FPGA联合使用例程_第68张图片

LED_PIO地址映射

本部分将描述如何将pio_led的物理地址映射为应用程序软件可以访问的虚拟地址。下图显示了C程序派生led_pio基址的虚拟地址。
HPS SoC和FPGA联合使用例程_第69张图片
首先,open 通过系统调用打开内存设备驱动程序" /dev/mem ",然后mmap通过系统调用用于将HPS物理地址映射为由void指针变量virtual_base表示的虚拟地址。代码将外围区域的物理基地址(HW_REGS_BASE = 0xfc000000)映射为一个基于虚拟地址的virtual_base。对于任何外围区域的控制器,用户可以通过其相对于外围区域的偏移量与基于的虚拟地址virtual_base进行叠加来计算任何外围区域的控制器的虚拟地址。根据这个规则,可以通过将下面两个偏移地址添加到virtual_base来计算led_pio的虚拟地址:

  • 轻量级的HPS-to- fpga AXI总线相对于HPS基址的偏移地址
  • Pio_led 相对 于 Lightweight HPS-to-FPGA AXI 总线的偏移地址

第一个偏移地址是0xff200000,在头文件hps.h中定义为常量ALT_LWFPGASLVS_OFST。头文件hps.h是SoC EDS中的头文件。位于Quartus安装文件夹:D:\intelFPGA\SoCEDS16.1\embedded\ip\altera\hps\altera_hps\hwlib\include\soc_cv_av\socal中。
HPS SoC和FPGA联合使用例程_第70张图片
第二个偏移地址是0x110,在头文件hps_0.h中定义为:LED_PIO_BASE
HPS SoC和FPGA联合使用例程_第71张图片
LED_PIO的虚拟地址由一个void指针变量h2p_lw_led_addr表示。应用程序可以直接使用指针变量来访问控制器中的寄存器LED_PIO。

控制LED

C程序员需要理解LED_PIO的PIO核的寄存器映射,然后才能控制它。下图显示了PIO核的寄存器映射。每个寄存器的宽度是32位。详情可以参阅PIO Core数据手册。对于led控制,我们只需要将输出值写入相对于基于地址0x10040的偏移地址为0寄存器。因为DE10-Standard板上的LED是高电平触发的,将值0x00000000写入偏移地址为0的寄存器将关闭所有9个红色led。DE10-Standard上有10个红色led,其中9个连接到这个控制器上。最后一个LED (LED0)用于模拟FPGA心跳。将值0x000001ff写入偏移地址为0寄存器将打开所有9个红色led。在C程序中,向pio_led的0偏移寄存器写入值0x000001ff可以表示为:

*(uint32_t *)h2p_lw_led_addr = 0x000001ff;

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

这条语句会将void指针赋值给uint32_t指针,所以C编译器知道写一个32位的值0x000001ff到虚拟地址h2p_lw_led_addr里面。
HPS SoC和FPGA联合使用例程_第72张图片

主程序

在友晶科技提供的例程中,又一个应用程序例程HPS_FPGA_LED。它的功能为:控制LEDR1-9进行LED移位闪烁操作,完成60次循环移位闪烁后,程序终止。我们的设计目标是:控制LEDR1-8进行LED移位闪烁操作,LEDR9随着LEDR1-8每一次移位而闪烁一次,完成60次循环移位闪烁后,程序终止。例程HPS_FPGA_LED的功能与我们的设计目标非常接近,所以我们可以在例程HPS_FPGA_LED的设计基础上进行更改。

首先,在光盘目录下找到例程HPS_FPGA_LED所在的文件夹:DE10-Standard_v.1.2.8_SystemCD\Demonstration\SoC_FPGA\HPS_FPGA_LED。将应用程序目录HPS_FPGA_LED复制到My_GHRD工程目录下,并把My_GHRD工程目录下的hps_0.h放到应用程序目录HPS_FPGA_LED下。

现在就可以开始根据我们的目标修改应用程序目录HPS_FPGA_LED下的:main.c。

我们要在主程序中设置一个void指针变量h2p_lw_led9_addr来存放新增的PIO组件LED_PIO9的虚拟地址。

h2p_lw_led9_addr=virtual_base + ( ( unsigned long  )( ALT_LWFPGASLVS_OFST + LED_PIO9_BASE ) & ( unsigned long)( HW_REGS_MASK ) );

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

对于LEDR9的控制,我们只需要在虚拟地址h2p_lw_led9_addr所指向的地址空间中写入0x1点亮LEDR9、写入0x0熄灭LEDR9。

*(uint32_t *)h2p_lw_led9_addr = 0x1;   	//点亮LEDR9
*(uint32_t *)h2p_lw_led9_addr = 0x0;	//熄灭LEDR9

 
 
   
   
   
   
 
 
   
   
   
   
  • 1
  • 2

但是为了使LEDR9随着LEDR1-8的移位进行闪烁,我们可以设置一个int变量n,让其随着LEDR1-8移位进行+1操作。我们对变量n进行取余操作后,将余值写入虚拟地址h2p_lw_led9_addr所指向的地址空间中即可。

*(uint32_t *)h2p_lw_led9_addr = n%2;
n = n+1;

 
 
   
   
   
   
 
 
   
   
   
   
  • 1
  • 2

最终,main.c文件内容如下:

/* This program demonstrate how to use hps communicate with FPGA through light AXI Bridge. uses should program the FPGA by GHRD project before executing the program refer to user manual chapter 7 for details about the demo */

#include
#include
#include
#include
#include “hwlib.h”
#include “socal/socal.h”
#include “socal/hps.h”
#include “socal/alt_gpio.h”
#include “hps_0.h”

#define HW_REGS_BASE ( ALT_STM_OFST )
#define HW_REGS_SPAN ( 0x04000000 )
#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )

int main() {

void *virtual_base;
int fd;
int loop_count;
int led_direction;
int led_mask;
void *h2p_lw_led_addr;

// map the address space for the LED registers into user space so we can interact with them.
// we'll actually map in the entire CSR span of the HPS since we want to access various registers within that span

if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {
	printf( "ERROR: could not open \"/dev/mem\"...\n" );
	return( 1 );
}

virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );

if( virtual_base == MAP_FAILED ) {
	printf( "ERROR: mmap() failed...\n" );
	close( fd );
	return( 1 );
}

h2p_lw_led_addr=virtual_base + ( ( unsigned long  )( ALT_LWFPGASLVS_OFST + LED_PIO_BASE ) & ( unsigned long)( HW_REGS_MASK ) );


// toggle the LEDs a bit

loop_count = 0;
led_mask = 0x01;
led_direction = 0; // 0: left to right direction
while( loop_count < 60 ) {
	
	// control led
	*(uint32_t *)h2p_lw_led_addr = ~led_mask; 

	// wait 100ms
	usleep( 100*1000 );
	
	// update led mask
	if (led_direction == 0){
		led_mask <<= 1;
		if (led_mask == (0x01 << (LED_PIO_DATA_WIDTH-1)))
			 led_direction = 1;
	}else{
		led_mask >>= 1;
		if (led_mask == 0x01){ 
			led_direction = 0;
			loop_count++;
		}
	}
	
} // while


// clean up our memory mapping and exit

if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) {
	printf( "ERROR: munmap() failed...\n" );
	close( fd );
	return( 1 );
}

close( fd );

return( 0 );

}

Makefile与编译

Makefile的内容如下:该程序包括SoC EDS提供的头文件。在Makefile中,还指定了ARM-linux交叉编译。

\-----------------------------------------------\

TARGET = HPS_FPGA_LED

ALT_DEVICE_FAMILY ?= soc_cv_av
SOCEDS_ROOT ?= $(SOCEDS_DEST_ROOT)
HWLIBS_ROOT = ( S O C E D S R O O T ) / i p / a l t e r a / h p s / a l t e r a h p s / h w l i b C R O S S C O M P I L E = a r m − l i n u x − g n u e a b i h f − C F L A G S = − g − W a l l − D (SOCEDS_ROOT)/ip/altera/hps/altera_hps/hwlib CROSS_COMPILE = arm-linux-gnueabihf- CFLAGS = -g -Wall -D (SOCEDSROOT)/ip/altera/hps/alterahps/hwlibCROSSCOMPILE=armlinuxgnueabihfCFLAGS=gWallD(ALT_DEVICE_FAMILY) -I ( H W L I B S R O O T ) / i n c l u d e / (HWLIBS_ROOT)/include/ (HWLIBSROOT)/include/(ALT_DEVICE_FAMILY) -I$(HWLIBS_ROOT)/include/
LDFLAGS = -g -Wall
CC = $(CROSS_COMPILE)gcc
ARCH= arm

build: $(TARGET)
$(TARGET): main.o
$(CC) $(LDFLAGS) $^ -o $@
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
rm -f $(TARGET) *.a *.o *~
\-----------------------------------------------\

要编译这个应用程序HPS_FPGA_LED,在SoC EDS Command Shell中进入应用程序目录HPS_FPGA_LED下。

cd HPS_FPGA_LED/

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

使用ls命令查看HPS_FPGA_LED目录下的文件,可以发现其中含有根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED。

ls

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

我们需要清除根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED。现在执行make clean命令。

make clean

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

再次使用ls命令,可以看到根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED已经被清除了。

ls

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

现在,使用make命令根据更改后的main.c文件编译出main.o文件和可执行文件:HPS_FPGA_LED。

make

 
 
   
   
   
   
 
 
   
   
   
   
  • 1

整个操作过程如下图所示:
HPS SoC和FPGA联合使用例程_第73张图片
至此,我们已经完成了硬件设计和应用程序设计两方面的设计工作。接下来就可以分别将包含有硬件逻辑信息的文件My_GHRD.sof和可执行文件HPS_FPGA_LED下载到DE10_Standard开发板上执行了。

系统测试

现在,我们需要在DE10_Standard开发板上进行系统测试,验证我们之前硬件逻辑设计和应用程序设计。这部分主要包括硬件逻辑的下载和应用程序的运行,硬件逻辑信息包含在文件My_GHRD.sof中,我们需要用它对FPGA进行编程;应用程序的运行其实就是在开发板上执行可执行文件HPS_FPGA_LED。

启动开发板

在进行硬件逻辑的下载和应用程序的运行之前,我们首先要对开发板进行各种线缆的连接和启动模式的配置。其中,线缆的连接主要包括:电源线、USB Blaster II线、串口线和网口线。配置模式的选择就是将开关SW10的MSEL[4:0]引脚配置为:01010,开关如下图所示,使得开发板可以从SD卡引导Linux系统启动。
HPS SoC和FPGA联合使用例程_第74张图片
HPS SoC和FPGA联合使用例程_第75张图片
连接好各种线缆后,打开DE10_Standard开发板上的串口。
首先,确认开发板连接到电脑上的端口号。打开:控制面板→设备管理器→端口,可以看到:USB Serial Port(COM9),这个COM9就是电脑为我们的开发板串口分配的端口号。
HPS SoC和FPGA联合使用例程_第76张图片
HPS SoC和FPGA联合使用例程_第77张图片
打开串口工具SecureCRT
HPS SoC和FPGA联合使用例程_第78张图片
设置以下参数:

  • Protocol:Serial
  • Port:COM9 USB Serial Port
  • Baud rate:115200
  • Data bits:8
  • Parity:None
  • Stop bits:1
    点击:Connect按钮。
    HPS SoC和FPGA联合使用例程_第79张图片
    点击SecureCRT主界面左侧的:Session Manager。选中:serial-com9,右击选中:Rename。重命名为:DE10-SoC。
    HPS SoC和FPGA联合使用例程_第80张图片
    按下开发板的电源开关,启动开发板。此时会在串口工具SecureCRT中看到开发板启动信息。等待提示输入用户名登录命令。在用户登录处键入:root,回车。用户登录密码处不用填写,直接回车。
    HPS SoC和FPGA联合使用例程_第81张图片
    到此,开发板启动的相关工作已经完成。
硬件逻辑下载

硬件逻辑下载的方式有很多种。可以通过USB Blaster II缆线将包含有硬件逻辑信息的文件My_GHRD.sof下载到SoC FPGA的FPGA上;也可以通过USB Blaster II缆线将包含有硬件逻辑信息的文件My_GHRD.jic下载到EPCS中;还可以通过串口或者网口将有硬件逻辑信息的文件soc_sytem.rbf和设备树覆盖文件fpga.dtbo放到SD卡的Linux系统中,由Linux对FPGA进行配置。

这里为了方便,我们采用通过JTAG缆线将包含有硬件逻辑信息的文件My_GHRD.sof下载到SoC FPGA的FPGA上的方式对FPGA进行配置。启动Quartus Prime Programmer。
在这里插入图片描述
进入Quartus Prime Programmer Standard Edition界面后,点击:Hardware Setup…按钮,检测下载用于My_GHRD.sof的JTAG链。
HPS SoC和FPGA联合使用例程_第82张图片
在Hardware Setup界面下,双击:DE-SoC,然后点击:Close按钮退出该界面。如果该界面下没有显示DE-SoC选项,在确保开发板上电,且JTAG线缆连接无误的情况下,关闭该界面重新进入几次即可。HPS SoC和FPGA联合使用例程_第83张图片
退回Quartus Prime Programmer Standard Edition界面后,点击:Auto Detect按钮,自动检测JTAG链上的器件。
HPS SoC和FPGA联合使用例程_第84张图片
此时会弹出Select Device界面,提示我们选择我们所用的SoC FPGA型号,选择:5CXFC6D6。然后点击右下角OK按钮退出该界面。
HPS SoC和FPGA联合使用例程_第85张图片
此时,Quartus Prime Programmer Standard Edition界面将会显示我们JTAG链上的器件。
HPS SoC和FPGA联合使用例程_第86张图片
选中:5CXFC6D6,然后点击左侧的:Change File…按钮,为5CXFC6D6添加配置文件。
HPS SoC和FPGA联合使用例程_第87张图片
在弹出的Select Programming File界面下,找到My_GHRD工程目录下的:output_files/My_GHRD.sof并选中,然后点击:Open。
HPS SoC和FPGA联合使用例程_第88张图片
返回Quartus Prime Programmer Standard Edition界面后,找到5CSXFC6D6F31所在行,勾选:Program/Configure。然后,点击Start按钮开始配置FPGA。
HPS SoC和FPGA联合使用例程_第89张图片
最后,在Process栏可以看到配置进度。此处显示:100%(Successful)表明FPGA配置成功。
HPS SoC和FPGA联合使用例程_第90张图片

应用程序运行

在完成FPGA的硬件逻辑配置后,我们就可以将应用程序发送到开发板中执行了。

文件传输

我们采用网口文件传输方式将应用程序可执行文件HPS_FPGA_LED发送开发板的Linux系统中。

在使用网口传输文件时,我们需要设置开发板和电脑在同一个网段下。首先,我们要确认电脑的IP地址。打开:控制面板→网络与共享中心,点击左侧:更改适配器设置。
HPS SoC和FPGA联合使用例程_第91张图片
接下来,我们需要查看电脑网络的状态信息,因为我们的电脑和开发板是通过网线进行连接的,我们应该查看本地连接的状态信息。选中本地连接,右击选择:状态。
HPS SoC和FPGA联合使用例程_第92张图片
点击:详细信息。
HPS SoC和FPGA联合使用例程_第93张图片
在网络连接详细信息界面中,可以看到,本地连接的IP地址(IPv4地址)为:192.168.137.1。之后就可以退出查看IP地址所打开的一系列界面了。
HPS SoC和FPGA联合使用例程_第94张图片
接下来,我们需要设置开发板的IP地址和电脑本地连接的IP地址在同一网段,即:192.168.137.*。我们暂且将其设定为:192.168.137.3。

进入串口工具SecureCRT中,输入命令:ifconfig,查看当前开发板的网络端口号及其IP地址信息。可以看到,开发板的网络端口号为:eth0,IP地址为:192.168.137.3。这已经是我们要设定的IP地址了。
HPS SoC和FPGA联合使用例程_第95张图片

当然,如果我们是第一次在开发板上运行友晶科技提供的镜像,这时启动的Linux系统中的网络IP地址并非如此,我们需要对其进行设定。IP地址设定有两种方法:一种是设定临时的静态IP地址;一种是设定永久的静态IP地址。前者在关闭开发板后IP地址将自动失效;后者的IP地址是永久生效的,除非再次更改。

我们这里只设定临时静态IP地址。在串口工具中输入命令:ifconfig eht0 192.168.137.3,即可设定IP地址。可以再次运行命令:ifconfig确认IP地址设定是否成功。

HPS SoC和FPGA联合使用例程_第96张图片
IP地址设置完成后,我们就可以进行文件传输了。进入SoC EDS Command Shell中,进入应用程序所在的目录下。输入:scp HPS_FPGA_LED [email protected]:~/,回车。此时将会启动文件传输,接着在:Are you sure you want to continue connecting ?后面输入:yes,可执行文件HPS_FPGA_LED将会自动传输到开发板上Linux系统的root用户主目录下。
HPS SoC和FPGA联合使用例程_第97张图片
在串口工具SecureCRT中,输入命令ls可以查看当前所在的root用户主目录下已经存在可执行文件HPS_FPGA_LED。
HPS SoC和FPGA联合使用例程_第98张图片
至此,使用网口进行文件传输已经完成。

当然,我们也可以采用比较通用的FTP服务通过网口进行传输,读者可以自行学习。

文件执行

文件执行前我们需要更改可执行文件HPS_FPGA_LED的权限。在串口工具中输入命令:chmod 777 HPS_FPGA_LED。然后,输入命令:./HPS_FPGA_LED执行可执行文件。
HPS SoC和FPGA联合使用例程_第99张图片
按下键盘的:Ctrl + C可以终止程序运行。
HPS SoC和FPGA联合使用例程_第100张图片

你可能感兴趣的:(FPGA,fpga,嵌入式)