Agilex 5 FPGA到HPS中断配置:GHRD修改与设备树映射
2026/9/7 2:25:58 网站建设 项目流程

上一篇我们把HPS侧的驱动骨架和设备树思路理清了,这一篇进入到让很多人卡壳的环节:怎么修改Agilex 5的GHRD工程,把FPGA侧真正产生的中断信号,接到HPS的GIC上。这块内容之所以绕,是因为Platform Designer里满屏的总线接口,而中断号又牵扯到ARM GIC的SPI映射,两套概念叠在一起,很容易让人彻底懵掉。如果你手头有一块带HPS的Agilex 5开发板,Quartus环境已经装好,这篇就把Quartus侧该动的文件、该配的参数、该避的坑一次讲完。整篇按我自己踩过坑之后的路径来走,照着做,至少能让你在半天内把中断链路点亮。

1. 动手前先把中断链路图画清楚

1.1 中断从FPGA到Linux驱动到底走了哪条路

很多纯FPGA出身的同学第一次接触SoC FPGA,会觉得“中断”这件事很简单:FPGA里拉一根信号线出来,连到ARM的GPIO上,上升沿触发一下不就行了?这句话放在单片机时代没毛病,但在Agilex 5这种带硬核处理器系统的芯片上,你真的把一根信号直接接到HPS的某个引脚上,那叫“外部中断”,一般用来接按键、外设控制器之类的低速信号。而FPGA逻辑内部产生的、需要高实时性的事件,走的是另一条专用通路。

这条专用通路是这样的:FPGA逻辑里的某个模块产生一个脉冲或者电平信号,这个信号先进入HPS硬核系统内部,经过中断控制器(GIC,Generic Interrupt Controller)的SPI中断通道,再由GIC统一分发给CPU核,最后Linux内核里注册的中断处理函数(ISR)被调用。也就是说,你最终要做的事情是把FPGA侧的一根逻辑信号,映射到GIC的一个SPI中断编号上。这个映射关系是整个实验的核心,后面所有工程修改、设备树配置,都是在为这条链路铺路。

1.2 GHRD工程在这里面扮演什么角色

GHRD全称是Golden Hardware Reference Design,翻译过来就是“黄金硬件参考设计”。Intel为每一代SoC FPGA都提供了这样一套默认的FPGA工程,里面已经把HPS硬核系统、DDR、时钟、复位、以及常见的低速外设都搭好了。它的价值在于,你不用从零开始创建HPS子系统,也不用自己纠结HPS的几百个引脚哪些能连到FPGA哪些必须走固定管脚,GHRD已经帮你把这些都验证过了。

我们要做的,是在GHRD的基础上做“小改”:把HPS组件里预留的FPGA-to-HPS中断接口使能出来,然后把FPGA侧我们自己添加的中断源挂到这条中断线上。为什么要改GHRD而不是自己新建工程?因为对于第一次接触Agilex 5的人来说,从零搭一个HPS系统需要处理时钟、复位、DDR配置、引脚分配这一大堆事情,任何一个环节没配好,板子直接起不来,你会分不清是硬件问题还是软件问题。而在GHRD基础上改,至少能保证工程主体是好的,出了问题大概率集中在你改动的部分。

这里要特意提醒一句:Agilex 5必须使用Quartus Prime Pro版本,不能像Cyclone系列那样用Lite版。我第一次用Agilex 5的时候,图省事用旧版Pro打开GHRD工程,结果器件型号列表里压根找不到A5系列,折腾了半天才发现是版本不匹配。建议直接用官网当前支持Agilex 5的最新Pro版本,配套的GHRD工程也尽量从官方渠道同步下载。

2. 修改前的工程准备与整体规划

2.1 搭建工程前的环境检查清单

在碰GHRD工程之前,先把下面几项确认一遍,否则改到一半到处报错会很影响心态。

第一,Quartus Prime Pro版本要匹配你手里的Agilex 5器件型号,一般用24.1之后的版本兼容性比较好。第二,GHRD工程建议优先用开发板厂商提供的版本,因为厂商会针对自家板卡的DDR颗粒、PHY配置、启动方式做适配,直接用Intel原版GHRD虽然也能跑,但有时会因为DDR配置参数不同导致系统起不来。第三,电脑内存和硬盘要预留充足,Agilex 5的全编译比Cyclone V要重不少,整个工程编译完占用空间轻松超过10GB,内存低于16GB的话编译时间会非常难看。

还有一点,建议先对原始GHRD工程做一次完整编译。这一步有两个目的:一是验证工具链、工程路径、License没问题;二是让Quartus把IP核的生成文件全部缓存好,后面第二次增量编译会快很多。第一次全编译大概需要四十分钟到一小时,正好可以利用这个时间把工程目录结构摸清楚。

2.2 GHRD工程里哪些文件是我们要动的

打开GHRD工程后,你会看到典型的SoC FPGA工程结构。顶层是一个叫top或者ghrd的Verilog/VHDL文件,里面例化了一个Platform Designer(早期叫Qsys)生成的子系统,通常命名为sys_socsoc_inst。这个子系统就是整个HPS系统的灵魂,所有组件都在里面。

实际需要修改的内容集中在两处:一处是Platform Designer生成的子系统文件(后缀是.qsys),另一处是HPS组件内部的配置。顶层的HDL文件一般不需要动,因为我们只是往子系统里加一个中断源,不会改变顶层对外接口。SDC时序约束文件大概率也不用动,因为F2H中断信号是异步信号,GIC内部会做同步处理,不需要我们在FPGA侧给它加时序约束。

真正要摸清的文件就三个:.qsys工程文件、顶层HDL文件的例化接口列表、以及GHRD自带的设备树源码(.dtsi)。设备树虽然严格来说属于软件侧,但在修改GHRD工程时,最好把设备树同步放在手边对照,因为你改动硬件映射的同时,软件侧的IRQ编号可能也要跟着变。

3. 在Platform Designer里把中断源挂上去

3.1 使能HPS的FPGA-to-HPS中断接口

双击打开GHRD里的.qsys文件,进入Platform Designer界面。在System Contents视图里找到HPS组件,名字可能是hps或者soc_inst_hps之类的。双击它进入HPS配置窗口,这里面选项非常多,但我们要找的核心是“FPGA Interfaces”这一类配置项。

在HPS配置窗口中找到FPGA-to-HPS相关的设置区域,里面通常会有一个“Interrupts”选项,默认可能是0或者只使能了很少的几条线。这里把它改成你需要的中断线数量,最少1条就够。Agilex 5的HPS一般支持多条FPGA-to-HPS中断线,编号从0开始,比如f2h_irq0f2h_irq1,我们先用一条线做验证,所以使能1条即可。

这里容易犯一个错:很多新手以为F2H中断线的编号就是GIC的中断号,配置成f2h_irq0就以为Linux里中断号是0。这是完全错误的。F2H中断线只是一个“物理通道编号”,真正起作用的是它映射到的GIC SPI中断号,两者之间的关系必须查HPS技术参考手册里的中断映射表。我后面专门用一节来讲这个映射关系,千万不要凭感觉跳过去。

3.2 添加一个实验用的中断源

为了让实验简单可控,我建议在FPGA侧放一个最简单的PIO核(Parallel I/O)作为中断源,而不是一开始就接你自己的业务逻辑。PIO核的好处是它是Platform Designer自带的组件,配置极其简单,而且它能直接产生一个可配置的中断输出信号,非常适合做链路验证。

在Platform Designer左侧IP目录里搜索“PIO”,添加一个Parallel I/O核。配置宽度可以设为1位或32位,方向设为输入或输出都可以,但如果想让HPS软件能读写它,就保持默认的双向或输出方向。重点在“Interrupt”选项卡,勾选上“Generate IRQ”,触发方式建议先选“Level high”(高电平触发)。原因我后面再说,电平触发对初调来说更容易排查。

添加完PIO核后,回到System Contents视图,你会看到新出现的一个组件节点。现在要做的连线有两根:一根是PIO的avalon_slave接口,要连到HPS能访问的AXI桥上,这样Linux驱动才能读写PIO寄存器;另一根是PIO的irq输出接口,要连到HPS组件的f2h_irq0上。

关于avalon_slave怎么连,我的建议是找个现成的参考。GHRD工程里一般已经有几个挂在HPS-to-FPGA桥上的外设,比如uarti2c之类的,照着它们连,把PIO也挂到同一层互联网络上就行。不熟悉AXI互联的同学可能会在这里犯晕,记住一句话:你只需要让HPS侧的master能“看到”PIO的slave接口,连接通路自然就通了。

3.3 重新生成Platform Designer系统

连线完成后,回到Platform Designer主界面,在Generate菜单里执行生成操作(Generate HDL)。这里注意选择生成语言,Verilog或VHDL都可以,但要和GHRD工程顶层语言保持一致,否则后面综合会报模块语言不匹配的问题。

生成过程会输出一大批文件,包括子系统的HDL代码、IP核的仿真模型、以及一个更新的.qip文件。这个.qip文件是Quartus工程引用IP核的入口,Platform Designer生成完会自动更新它。这个步骤完成后,Platform Designer这边的工作就算告一段落了,接下来回到Quartus主工程,准备开始编译。

这里分享一个我自己的经验:在生成之后,如果有条件,先不要着急全编译,而是打开一下顶层文件,确认新生成的子系统模块名、参数列表没有变化。因为这个改动只是内部加了逻辑,按理说顶层接口不会变,但万一你用的GHRD版本比较旧,个别参数名有差异,早点发现总比编译到一半报错强。

4. 中断号的推导:软件和硬件之间最关键的那层纸

4.1 GIC中断类型与F2H中断的映射规则

ARM架构的GIC把中断分成三类:SGI(软件触发中断)、PPI(私有外设中断)、SPI(共享外设中断)。FPGA侧过来的中断全部属于SPI,因为它们不是某个CPU核私有的,而是可以路由给任意核处理。

在设备树里描述SPI中断时,标准写法是interrupts = <0 N 4>,其中第一个0表示SPI类型(对应的宏定义是GIC_SPI),第二个N是SPI编号,第三个4表示触发类型,4代表高电平触发。这里最关键的N,就是我们要查表得到的值。Platform Designer里你连的是f2h_irq0,设备树里却不能写0,而是要写这个物理通道对应的GIC SPI编号。

这个SPI编号是多少?没有任何统一规律,不同芯片型号、不同HPS版本都可能不一样。有人会想着网上搜“Agilex 5 f2h_irq0 中断号”直接抄,我只能说碰运气可以,但正规做法应该是去查你手头那颗器件对应的HPS Technical Reference Manual里的Interrupt Mapping表,搜关键词“FPGA-to-HPS Interrupts”,里面会有一张详细的表格。

4.2 中断号计算示例与设备树核对方法

为了把流程讲清楚,这里假设查表的结果是:f2h_irq0对应GIC SPI编号160(这个数字仅为演示,不同器件请以手册实际值为准)。那么设备树里我们的中断控制器节点应该写:

&gic { interrupts = <0 160 4>; };

如果是在你自己的外设节点里引用中断,则像这样写:

my_interrupt_device: my_interrupt_device@10000000 { compatible = "my-irq-device"; reg = <0x10000000 0x100>; interrupt-parent = <&intc>; interrupts = <0 160 4>; };

这里有个容易混淆的点:设备树里填的是SPI ID,而你在Linux运行后通过/proc/interrupts看到的中断号,往往不是这个数字。因为内核内部会把SPI ID转换成全局IRQ number,中间会有一个偏移。所以不要看到/proc/interrupts里显示的数字和设备树不一致就慌,这是正常的。你更应该关注的是,触发中断后,/proc/interrupts里对应那一行的计数有没有增加。

我不止一次遇到有人在这里卡住,明明FPGA侧信号已经翻转了,驱动里request_irq也成功了,但中断就是不进来。排查到最后,绝大多数都是设备树里SPI编号填错了,或者根本没查手册,随便填了一个网上看来的值。所以这一步,值得多花半小时认真查表、对照官方设备树,省得后面Debug一整天。

5. 综合、生成RBF与烧写验证

5.1 从Platform Designer回到Quartus全流程编译

Platform Designer生成完成后,回到Quartus主界面,直接执行完整编译流程:Analysis & Synthesis、Fitter、Assembler。如果前面配置没有明显错误,这一步一般不会卡太久,因为GHRD工程本身已经很成熟,我们只是在子系统内部加了一个PIO核和一条中断线而已。

编译过程中最可能出现的问题,是顶层HDL文件里例化系统子系统的模块端口没有更新。按常理,内部加东西不影响顶层接口,但如果Platform Designer生成时自动帮你改了一些参数绑定,而顶层HDL是手动维护的旧版本,就会报“port not found”之类的错误。解决办法很简单:对比一下新的子系统模块定义和顶层HDL里例化的参数列表,把它们对齐即可。

编译通过后,会生成.sof文件,这是用于JTAG在线调试的FPGA配置文件。到这里,硬件侧的初步工作就算完成了,但真正的验证还没开始。

5.2 生成RBF镜像以及两种验证方式

如果你用的是带HPS的开发板,想真正验证“FPGA发中断、Linux收中断”,有两种路径:一是JTAG直接烧写.sof,二是把FPGA镜像固化到启动介质里,让系统上电时自动配置FPGA,再启动HPS。第一种适合快速调试,第二种适合正式验证。

JTAG方式比较简单,用Quartus Programmer直接把.sof下载进去就行。但注意一点:在SoC FPGA平台上,JTAG下载FPGA配置时要看开发板的设计,有的板子允许HPS继续运行,有的板子会在配置FPGA时连带复位HPS,所以如果你是通过JTAG连接来调试Linux,最好先把HPS侧的相关驱动和脚本准备好,再开始烧写。

固化方式则需要用Convert Programming Files工具把.sof转换成.rbf文件。在Quartus的File菜单里打开Convert Programming Files,Output format选择“Raw Binary File (.rbf)”,然后在Input files里添加生成的.sof,转换完成后再把.rbf放到启动分区,或者烧入QSPI Flash。具体怎么放,取决于你开发板的启动方式和U-Boot环境变量,这里就不展开了。

还有一条更灵活的路:如果不想动启动介质,可以在Linux启动后用FPGA Manager框架在线加载.rbf。设备树里配好fpga-region节点,然后在用户空间通过/sys/class/fpga_manager/fpga0/load接口写入固件,这样也能完成FPGA配置。这种方式对调试阶段特别友好,不用反复拔插SD卡或重新烧Flash。

5.3 验证链路是否打通

配置完成后,在Linux侧加载相应驱动模块,然后触发一次FPGA中断。最简单的方法是:在PIO核的某个输入引脚上接一个按键,或者直接在FPGA逻辑里写一个1Hz的脉冲发生器,接到PIO的输入通道上。当PIO检测到电平变化时,它会产生中断信号并沿F2H中断线送入GIC。

验证时重点关注/proc/interrupts里你注册的中断号那一行,正常情况下每秒应该增加一次计数。如果计数稳定增长,说明整条链路已经通了:FPGA逻辑 -> PIO中断 -> F2H中断线 -> GIC SPI -> Linux ISR。到这一步,这个实验最艰难的部分就结束了,接下来你就可以把自己的业务逻辑挂到这个中断通路上。

6. 常见问题与排查技巧实录

现象可能原因排查方向
中断完全不触发,/proc/interrupts无计数F2H中断线没使能,或设备树SPI编号错误回Platform Designer确认f2h_irq线已使能;对照TRM核实SPI号
request_irq返回-EINVAL中断号超出有效范围,或设备树格式错误检查interrupts属性三个字段是否都正确;确认SPI号有效性
中断一触发就死机或打印风暴电平触发中断但ISR里没清中断源先把PIO配成边沿触发,或确保ISR里读/写清中断寄存器
编译报错f2h_irq端口不存在HPS组件没有使能对应的FPGA-to-HPS中断接口重新进HPS配置界面,把Interrupts数量改成大于0
加载rbf后系统起不来镜像配置模式或启动地址不对先用JTAG方式验证sof正常,再检查rbf转换时的配置模式
烧写后还是老逻辑在跑启动介质里的FPGA镜像没更新确认rbf确实覆盖到了启动分区;检查U-Boot里加载地址

我再补充几条关于实际调试的细节。

第一,初次做这个实验时,强烈建议先把PIO配置成“边沿触发”,而不是“电平触发”。电平触发在调试初期有风险:如果ISR里忘了清中断源,GIC会认为中断一直有效,导致中断风暴,直接把Linux打挂。而边沿触发至少能保证每产生一次边沿只触发一次,系统不容易死掉。等链路完全相通了,再按实际业务需求改成电平触发。

第二,排查中断问题时,不要一开始就怀疑是Quartus工程的问题。更高效的做法是先用一个简单的FPGA逻辑,比如固定分频器产生1Hz脉冲,把PIO输入拉起来,然后在Linux侧看/proc/interrupts。如果连这么简单的信号都触发不了,再回头查配置;如果触发了,说明链路没问题,问题出在你的业务逻辑上。这种“最小复现”的思路能帮你快速切割问题域。

第三,关于SPI编号,我吃过一次亏。当时拿着一份网上找的旧版映射表,对着Platform Designer的连线配了个自以为正确的编号,结果怎么都触发不了。后来老老实实去查当颗器件的勘误手册和官方设备树,才发现旧版表格里这个中断号在新器件上有更新。所以,务必以你手头开发板配套的官方文档和官方Linux内核dtsi为准,不要盲目相信网上的二手信息。

第四,Agilex 5的调试还有一个特点,就是SDM(System Debug Manager)会在启动早期接管器件配置。如果你遇到FPGA配置失败导致整板起不来的情况,可以先通过JTAG把.sof烧进去,排除FPGA侧问题,再回过头来排查启动介质里的镜像格式和加载地址。这样能避免把硬件启动问题和软件配置问题混在一起。

最后再说一个我自己的习惯:拿到新板子,我从来不会一上来就接复杂业务逻辑。第一次做中断实验,我一定只放一个1Hz脉冲发生器,配合一个最简单的驱动,先确保整条链路是通的。这一步看起来多花半小时,实际上能帮你省掉后面以天计的定位时间。中断实验不像流水灯,它跨了FPGA和ARM两个世界,任何一个环节出问题,表象都可能是“没反应”。先把最小链路打通,再往上加东西,这是我认为最稳妥的节奏。

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

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

立即咨询