本教程演示了如何使用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进行配置。
假定开发人员已经具有以下背景知识:
在开始本教程之前,请注意演示项目准备需要以下内容:
在Intel SoC FPGA中,HPS逻辑与FPGA结构通过AXI(Advanced eXtensible Interface)桥进行连接。为了实现HPS逻辑与FPGA 结构的通信,需要通过使用Intel系统集成工具Qsys添加HPS组件来进行系统设计。从HPS组件的AXI主端口,HPS可以访问那些连接到AXI主端口的内存映射从端口。
HPS包含以下HPS-FPGA AXI桥
下图显示了FPGA结构和L3与HPS互连的AXI桥的框图。每个主(M)接口和从(S)接口显示其数据宽度。括号中标注了每个互连的时钟域。
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。
术语GHRD是黄金硬件参考设计(Golden Hardware Reference Design)的简称。友晶科技为DE10-Standard开发板提供的GRD项目位于CD文件夹中: CD-ROM\Demonstration\SOC_FPGA\ DE10_Standard_GHRD。
本项目由以下组成部分组成:
MPU所看到的SoC FPGA部分的系统外围设备的内存映射从轻量级的HPS-to-FPGA到FPGA的基础地址0xFF20_0000开始。MPU可以通过Qsys中的地址偏移来访问这些外围设备。用户可以打开GHRD项目与Quartus II软件。然后打开soc_system。qsys文件与qsys工具。下图列出了连接到轻量级HPS-to-FPGA的外围设备的地址映射。
这些外设的所有Avalon引出信号( Avalon Conduit signals)都连接到DE10-Standard板上SoC FPGA的I/O引脚,如图所示
在实际的应用过程中,友晶科技提供的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工程目中的一下文件复制到工程目录下:
因为其中的文件DE10_Standard_GHRD.v后面将会被指定为顶层文件,所以需要将其更改为与工程名My_GHRD相同的My_GHRD.v文件,同时为了与工程名保持协调,我们也将文件DE10_Standard_GHRD.sdc更改为与工程名My_GHRD相同的My_GHRD.sdc。最终工程目录文件夹如下图所示:
首先,建立一个名为:My_GHRD的工程。打开软件Quartus Prime Standard Edition,点击:File→New Project Wizard…,进入工程创建引导界面。
点击引导界面下方的:Next,进入Directory, Name, Top-Level Entity设置界面。
点击右侧按钮:…选择你的工程目录E:/work/Quartus/My_GHRD。之后设置工程名:My_GHRD。此时软件将自动把顶层设计entity name设置为工程名:My_GHRD。继续点击:Next,进入Project Type设置界面。
在Project Type界面选择:Empty Project选项,继续点击:Next,进入Add Files设置界面。
在Add Files设置界面,点击右侧按钮:…,添加工程目录下的文件:My_GHRD.v和soc_system.qsys以及文件夹hps_isw_handoff和ip下的所有文件进入工程。
然后点击:Next,进入Family, Device & Board Settings设置界面。
在Family, Device & Board Settings设置界面,我们要设置我们所用的SoC FPGA型号:5CSXFC6D6F31C6N。为了能够快速锁定我们要选择的SoC FPGA型号,我们可以点击:Device下拉列表,选择:Cyclone V SX Extended Features,以缩小选型范围。
然后,选择Available devices下的5CSXFC6D6F31C6。继续点击:Next,进入EDA Tool Settings设置界面。
在EDA Tool Settings设置界面下,我们保留默认设置。之后点击:Fnish,完成工程创建,退出工程创建引导界面。
由于我们需要在DE10_Standard_GHRD工程的基础上更改Qsys的设计来实现对LEDR9的单独控制,所以我们需要在Qsys中添加一个1位的PIO端口来实现对LEDR9的控制,同时需要将原来用于同时控制LEDR0-9的10位PIO端口的改为9位的PIO端口。
依次点击:Tools→Qsys,启动Qsys,之后选中要打开的soc_system.qsys文件。
接下来,在打开的soc_system.qsys文件添加一个1位的PIO端口。在左上角的IP Catalog的搜索框中搜索“ PIO ”,之后双击下面列表中的“ PIO(Parallel I/O) ”。
此时将弹出“ PIO(Parallel I/O) ”设置界面。将PIO的数据宽度Width设置为:1,数据输入输出方向Direction设置为:Output。最后点击右下角的Finish按钮退出设置。
现在,新的Qsys设计中将会出现我们新添加的PIO端口pio_0,我们可以对其进行重命名。光标移动到在端口名pio_0处,右键选择“Rename”,重命名为:led_pio9。点击空白处保存命名。
之后我们需要对led_pio9组件的信号进行连接,将光标放置到下图中led_pio9组件中clk信号所在行的白点处,点击后可以将led_pio9组件中clk信号与clk_0组件的clk信号进行连接,可以表示为:led_pio9.clk—clk_0.clk。使用类似的操作,将led_pio9组件的其它信号分别与其它组件进行如下连接,最终的连接关系如下:
如下图:
其中led_pio9有一个信号led_pio9.external_connection需要进行对外引出。光标置于信号led_pio9.external_connection处右击选择:Connections:led_pio9.external_connection→Export as: led_pio9_external_connection。
到此,添加一个1位的PIO端口led_pio9的操作已经完成。
接下来需要将原来控制LEDR0-9的数据宽度为10位的PIO端口led_pio更改为只用控制LEDR0-8的数据宽度为9位的PIO端口led_pio。双击组件led_pio,将数据宽度Width(1-32 bits)设置为:9,关闭Parameters窗口将自动保存更改。
现在,我们要为我们新添加的PIO端口led_pio9分配基地址,同时更新Qsys设计中其它组件的基地址。依次点击:System→Assign Base Addresses。
可以看出,至此,我们需要在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列方框中的“√”去掉。
之后进行Qsys设计中的文件生成工作。点击右下角的Generate HDL…按钮。
在弹出的“Generation”窗口中,确认Output Directory下Path所指定的生成文件的输出目录在工程目录下。然后点击Generate按钮。
接下来会弹出“Save System Completed”窗口,点击右下角的Close按钮。
接下来会弹出的“Generate”窗口,等待几分钟,文件生成工作完成后,右下角的“Close”按钮会高亮显示。此时,点击“Close”按钮。
最后,又会退回到Qsys设计页面,点击右下角的Finish按钮,退出Qsys设计。
在返回Quartus Prime Standard Edition主界面后,会提示我们把更新后的.qip和.sip文件重新手动添加到工程中来。点击提示窗口右下角的Close按钮。
现在,我们来手动添加更新后的.qip和.sip文件。右击Project Navigator窗口中工程名:My_GHRD。点击Settings…选项。
接下来会弹出Category界面,点击右侧的…按钮。
此时会弹出Select File界面,在指定的目录下找到生成的soc_system.qip文件并选中,然后点击右下角的“打开”按钮。
最后,可以在返回的Catagory界面中可以看到生成的soc_system.qip文件已经被成功添加到工程中。
在上述界面中可以看到文件: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按钮退出该界面。
至此,工程My_GHRD中Qsys部分的设计已经完成。接下来我们需要对工程中的硬件描述文件.v文件进行修改。其实,我们在DE10_Standard_GHRD工程的基础上只用对My_GHRD中顶层实体文件My_GHRD.v中各个模块的引脚及其连接关系进行描述即可。
在此之前,我们先指定我们的顶层实体文件My_GHRD.v。点击Project Navigator窗口中Hierachy按钮,会弹出下拉列表,我们点击File选项。
找到工程中的My_GHRD.v,右击选择Set as Top-Level Entity。
我们双击Project Navigator窗口中工程名My_GHRD,此时软件将会打开工程的顶层实体文件My_GHRD.v
对顶层实体文件My_GHRD.v的修改主要有以下各处:
第一,顶层实体模块的命名须与工程名My_GHRD保持一致。
第二,在模块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]上面的。这样,总体的更改就是下面这样的:
现在,我们可以对工程My_GHRD进行编译操作。首先是进行工程的分析与综合,依次点击:Processing→Start→Start Analysis & Synthesis。
工程的分析综合通过后,主界面中左侧的Tasks窗口下的Analysis & Synthesis栏前面会展示一个绿色的“√”,后面会显示分析综合所用的时间。
上面分析综合成功后,下一步需要进行Fitter(Place & Route),这里进行的是关于FPGA内部的布局布线操作。在此之前我们需要对我们模块中设计的输入输出引脚进行引脚分配。引脚分配的方法有很多种,读者可以自行学习。这里由于我们定义了大量的引脚,而且我们有现成的DE10_Standard_GHRD工程可以利用,我们只需将该工程里面的引脚分配信息转移进入我们的My_GHRD工程就可以了。
我们现在打开DE10_Standard_GHRD工程。进入工程后,我们点击:Assignments→Pin Planner,进入引脚分配界面。
在该界面下,点击:File→Export…。
之后会弹出Export窗口。我们需要选择我们要导出的文件格式为:.tcl。
然后,点击右下角的Export按钮。
最后,依次点击引脚分配界面和DE10_Standard_GHRD工程界面右上角的关闭按钮,退出DE10_Standard_GHRD工程。
我们现在已经得到了我们需要的引脚分配文件DE10_Standard_GHRD.tcl,现在我们需要把它从DE10_Standard_GHRD工程目录下复制到My_GHRD工程目录下。为了与My_GHRD工程名保持一致,将其改名为:My_GHRD.tcl。
接下来,我们需要利用得到的引脚分配文件My_GHRD.tcl对工程My_GHRD中的引脚进行分配。点击:Tools→Tcl Scripts…。
在弹出的Tcl Scripts窗口下,点击右侧的Add to Project…按钮。
在弹出的Add to Project窗口下,选中我们的引脚分配文件My_GHRD.tcl,并点击右下角的打开按钮。
此时,软件又自动退回到Tcl Scripts窗口下。在Libraries栏中找到我们的引脚分配文件My_GHRD.tcl,点击下方的Run按钮来加载我们的引脚分配文件。
在后面弹出的界面中点击OK按钮,退回到Tcl Scripts窗口。
最后点击到Tcl Scripts窗口下方的Close按钮退出到Tcl Scripts窗口,回到工程主界面。
此时,点击主界面的:Assignments→Pin Planner进入引脚分配界面。
在引脚分配界面中,可以看到,引脚大部分已经被分配完成,未分配的引脚暂时不用担心它们,我们还没有用到。
现在,我们开始对工程进行布局布线。点击:Processing→Start→Start Fitter。
工程的布局布线通过后,主界面中左侧的Tasks窗口下的Fitter(Place & Route)栏前面会展示一个绿色的“√”,后面会显示布局布线所用的时间。
到此,基本上我们可以判断在逻辑上我们的设计已经没有问题了,接下来我们是可以进行工程的全编译了。但是,在实际的应用设计中我们还要考虑对信号的时序进行约束,尤其是具有 SDRAM DDR3这种高速存取器件的设计中。对于时序约束,我们可以利用DE10_Standard_GHRD工程中的时序约束文件:DE10_Standard_GHRD.sdc。同样的,将其复制到My_GHRD工程目录下,改名为:My_GHRD.sdc。
将时序约束文件添加进入工程,右击Project Navigator窗口下的工程名:My_GHRD,点击:Setting…按钮。
接下来会弹出Category界面,点击右侧的…按钮。
此时会弹出Select File界面,进入工程主目录下,点击右下角的文件类型选择按钮,选中:Script Files( * .tcl*.sdc*.pdc*.qip)一项。
下找到时序约束文件My_GHRD.sdc并选中,然后点击右下角的“打开”按钮。
最后,可以在返回的Catagory界面中可以看到时序约束文件:My_GHRD.sdc文件已经被成功添加到工程中。依次点击右下角的Apply按钮和OK按钮退出该界面。
此时已经回到工程主界面,我们可以进行工程的全编译了。点击Processing→Start Compilation。
本节将介绍如何设计一个ARM C程序来控制PIO控制器led_pio。SoC EDS用于编译C项目。为了让ARM程序控制PIO组件led_pio,我们需要led_pio地址。使用Linux内置驱动程序’ /dev/mem '和mmap system-call可以将led_pio组件的物理基址映射到Linux应用软件可以直接访问的虚拟地址。
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工程目录下。
接下来,使用记事本打开批处理文件:generate_hps_qsys_header.sh,确认文件中红线标注的路径下有应用程序:sopc-create-header-files.exe。其中,路径:/cygdrive/d/…表示D盘…。如果该路径下没有上述应用程序,读者需自行更改其中的路径。
注意,如果后面在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/swinfo2header−−swinfo<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
}
一定注意,我们之间的Quartus安装路径是不同的,你需要根据自己的安装路径指定应用程序的路径,以便软件能够找到它们。
然后,启动SoC EDS命令shell
在SoC EDS Command Shell中,转到My_GHRD工程目录下。其中我的工程目录为:E:\work\Quartus\My_GHRD。读者根据自己的工程目录路径设置,为了输入方便,可以使用Tab键自动补全功能。注意:在E盘,路径就是:/cygdrive/e/…。
执行批处理文件:generate_hps_qsys_header.sh by
此时,会在My_GHRD工程目录下生成HPS头文件:hps_0.h。
现在,我们已经得到所需的头文件:hps_0.h。我们可以用一些代码查看编辑软件查看一下它里面的内容,我这里用的代码查看编辑软件是Source Insight。可以看到,生成的头文件:hps_0.h中已经为我们新增的PIO模块led_pio9生成了一系列宏定义。在头文件中,led_pio9的基地址由一个常量LED_PIO9_BASE表示,led_pio9的数据宽度由一个常量LED_PIO9_DATA_WIDTH表示,这两个常量将会在后面的C语言代码中被使用。
本部分将描述如何将pio_led的物理地址映射为应用程序软件可以访问的虚拟地址。下图显示了C程序派生led_pio基址的虚拟地址。
首先,open 通过系统调用打开内存设备驱动程序" /dev/mem ",然后mmap通过系统调用用于将HPS物理地址映射为由void指针变量virtual_base表示的虚拟地址。代码将外围区域的物理基地址(HW_REGS_BASE = 0xfc000000)映射为一个基于虚拟地址的virtual_base。对于任何外围区域的控制器,用户可以通过其相对于外围区域的偏移量与基于的虚拟地址virtual_base进行叠加来计算任何外围区域的控制器的虚拟地址。根据这个规则,可以通过将下面两个偏移地址添加到virtual_base来计算led_pio的虚拟地址:
第一个偏移地址是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中。
第二个偏移地址是0x110,在头文件hps_0.h中定义为:LED_PIO_BASE
LED_PIO的虚拟地址由一个void指针变量h2p_lw_led_addr表示。应用程序可以直接使用指针变量来访问控制器中的寄存器LED_PIO。
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;
这条语句会将void指针赋值给uint32_t指针,所以C编译器知道写一个32位的值0x000001ff到虚拟地址h2p_lw_led_addr里面。
在友晶科技提供的例程中,又一个应用程序例程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 ) );
对于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
但是为了使LEDR9随着LEDR1-8的移位进行闪烁,我们可以设置一个int变量n,让其随着LEDR1-8移位进行+1操作。我们对变量n进行取余操作后,将余值写入虚拟地址h2p_lw_led9_addr所指向的地址空间中即可。
*(uint32_t *)h2p_lw_led9_addr = n%2;
n = n+1;
最终,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的内容如下:该程序包括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=arm−linux−gnueabihf−CFLAGS=−g−Wall−D(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/
使用ls命令查看HPS_FPGA_LED目录下的文件,可以发现其中含有根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED。
ls
我们需要清除根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED。现在执行make clean命令。
make clean
再次使用ls命令,可以看到根据原来的main.c文件编译出的main.o文件和可执行文件:HPS_FPGA_LED已经被清除了。
ls
现在,使用make命令根据更改后的main.c文件编译出main.o文件和可执行文件:HPS_FPGA_LED。
make
整个操作过程如下图所示:
至此,我们已经完成了硬件设计和应用程序设计两方面的设计工作。接下来就可以分别将包含有硬件逻辑信息的文件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系统启动。
连接好各种线缆后,打开DE10_Standard开发板上的串口。
首先,确认开发板连接到电脑上的端口号。打开:控制面板→设备管理器→端口,可以看到:USB Serial Port(COM9),这个COM9就是电脑为我们的开发板串口分配的端口号。
打开串口工具SecureCRT
设置以下参数:
硬件逻辑下载的方式有很多种。可以通过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链。
在Hardware Setup界面下,双击:DE-SoC,然后点击:Close按钮退出该界面。如果该界面下没有显示DE-SoC选项,在确保开发板上电,且JTAG线缆连接无误的情况下,关闭该界面重新进入几次即可。
退回Quartus Prime Programmer Standard Edition界面后,点击:Auto Detect按钮,自动检测JTAG链上的器件。
此时会弹出Select Device界面,提示我们选择我们所用的SoC FPGA型号,选择:5CXFC6D6。然后点击右下角OK按钮退出该界面。
此时,Quartus Prime Programmer Standard Edition界面将会显示我们JTAG链上的器件。
选中:5CXFC6D6,然后点击左侧的:Change File…按钮,为5CXFC6D6添加配置文件。
在弹出的Select Programming File界面下,找到My_GHRD工程目录下的:output_files/My_GHRD.sof并选中,然后点击:Open。
返回Quartus Prime Programmer Standard Edition界面后,找到5CSXFC6D6F31所在行,勾选:Program/Configure。然后,点击Start按钮开始配置FPGA。
最后,在Process栏可以看到配置进度。此处显示:100%(Successful)表明FPGA配置成功。
在完成FPGA的硬件逻辑配置后,我们就可以将应用程序发送到开发板中执行了。
我们采用网口文件传输方式将应用程序可执行文件HPS_FPGA_LED发送开发板的Linux系统中。
在使用网口传输文件时,我们需要设置开发板和电脑在同一个网段下。首先,我们要确认电脑的IP地址。打开:控制面板→网络与共享中心,点击左侧:更改适配器设置。
接下来,我们需要查看电脑网络的状态信息,因为我们的电脑和开发板是通过网线进行连接的,我们应该查看本地连接的状态信息。选中本地连接,右击选择:状态。
点击:详细信息。
在网络连接详细信息界面中,可以看到,本地连接的IP地址(IPv4地址)为:192.168.137.1。之后就可以退出查看IP地址所打开的一系列界面了。
接下来,我们需要设置开发板的IP地址和电脑本地连接的IP地址在同一网段,即:192.168.137.*。我们暂且将其设定为:192.168.137.3。
进入串口工具SecureCRT中,输入命令:ifconfig
,查看当前开发板的网络端口号及其IP地址信息。可以看到,开发板的网络端口号为:eth0,IP地址为:192.168.137.3。这已经是我们要设定的IP地址了。
当然,如果我们是第一次在开发板上运行友晶科技提供的镜像,这时启动的Linux系统中的网络IP地址并非如此,我们需要对其进行设定。IP地址设定有两种方法:一种是设定临时的静态IP地址;一种是设定永久的静态IP地址。前者在关闭开发板后IP地址将自动失效;后者的IP地址是永久生效的,除非再次更改。
我们这里只设定临时静态IP地址。在串口工具中输入命令:ifconfig eht0 192.168.137.3
,即可设定IP地址。可以再次运行命令:ifconfig确认IP地址设定是否成功。
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用户主目录下。
在串口工具SecureCRT中,输入命令ls可以查看当前所在的root用户主目录下已经存在可执行文件HPS_FPGA_LED。
至此,使用网口进行文件传输已经完成。
当然,我们也可以采用比较通用的FTP服务通过网口进行传输,读者可以自行学习。
文件执行前我们需要更改可执行文件HPS_FPGA_LED的权限。在串口工具中输入命令:chmod 777 HPS_FPGA_LED
。然后,输入命令:./HPS_FPGA_LED
执行可执行文件。
按下键盘的:Ctrl + C可以终止程序运行。