【飞腾平台实时Linux方案系列】第二十篇 - 飞腾平台实时Linux驱动开发与优化。
一、简介:国产芯 + 实时驱动 = 工业自主可控的“最后一公里”
-
飞腾芯片(FT-1500A/FT-2000/FT-D2000) 已批量应用于能源、矿山、轨道交通等关键基础设施。
-
痛点:
-
官方驱动仅保证“能跑”,中断延迟 80~120 μs,无法满足 SIL 2 级 PLC 周期 1 ms 的要求;
-
国产化审核要求“源代码可审查 + 可重现编译”,闭源商业驱动直接出局。
-
-
价值:
掌握“飞腾平台实时 Linux 驱动开发规范”,即可把中断延迟压到 < 20 μs,抖动 < 5 μs,让国产化 RT-Linux 真正达到工业硬核指标。
二、核心概念:5 个关键词先搞懂
| 关键词 | 一句话 | 飞腾平台注意事项 |
|---|---|---|
| PREEMPT_RT | Linux 实时补丁,将自旋锁变互斥锁、支持线程化中断 | 飞腾官方 kernel 已合并 RT,只需打开 CONFIG_PREEMPT_RT=y |
| 线程化 IRQ | 把中断上半部变成实时线程,可设置优先级 99 | 飞腾 GICv3 支持,需 irqthread boot 参数 |
| 设备树 (DT) | 描述硬件连接,替代硬编码 | 飞腾使用 ACPI/DTS 双路,工业板卡一般走 DTS |
| 内存映射 I/O | 通过 ioremap 访问寄存器 | FT-2000 外设地址 32 bit,注意 ioremap_32be() |
| 实时互斥锁 | rt_mutex 支持优先级继承,防止反转 | 驱动中锁资源必须用 rt_mutex 而非 raw_spinlock |
三、环境准备:10 分钟搭好“飞腾驱动实验室”
1. 硬件
-
FT-2000/4 工业评估板(PCIe 3.0 ×4、2×千兆网口)
-
自制 IO 卡:FPGA 基于 Xilinx Artix-7,走 PCIe x1(示例设备)
2. 软件
| 组件 | 版本 | 获取 |
|---|---|---|
| Ubuntu Server | 22.04 (ARM64) | 飞腾官方 ISO |
| kernel | 5.15.71-rt53 | 已在飞腾仓库合并 RT |
| 交叉工具链 | gcc-11-aarch64-linux-gnu | sudo apt install gcc-aarch64-linux-gnu |
| 调试工具 | ftrace、trace-cmd、rt-tests | apt install trace-cmd rt-tests |
3. 一键编译内核(可复制)
#!/bin/bash
# build_ft_rt.sh
git clone https://gitee.com/phytium/kernel/linux-5.15-rt.git
cd linux-5.15-rt
cp arch/arm64/configs/phytium_defconfig .config
./scripts/config -e CONFIG_PREEMPT_RT
./scripts/config -e CONFIG_FTRACE
./scripts/config -e CONFIG_DYNAMIC_FTRACE
make -j$(nproc) bindeb-pkg
sudo dpkg -i ../linux-*.deb
重启选新内核即可。
四、应用场景(300 字)
某 220 kV 变电站国产化改造,需用飞腾 FT-2000 替代原国外 x86 PLC。系统通过 PCIe 扩展卡采集 96 路断路器位置信号,控制 48 路合闸线圈,要求闭环周期 ≤ 1 ms,抖动 ≤ 50 μs,SIL 2 认证。传统 Linux 驱动中断延迟 120 μs,无法满足;采用本文“线程化中断 + 优先级继承”优化后,中断延迟稳定在 18 μs,任务调度抖动 5 μs,周期抖动 38 μs,一次性通过型式试验。现场运行 6 个月无丢帧,远程后台可实时查看每一路 SOE(事件顺序记录)时间戳,误差 < 200 μs,完全满足电网故障录波要求。
五、实际案例与步骤: PCIe-IO 卡驱动全流程
示例驱动:
ft-pcie-io.ko功能:寄存器读写、中断上报、用户空间实时接口
5.1 设备树片段(DTS)
// ft2000-pcie-io.dts
/ {
pcie@60000000 {
pcie-io@0 {
compatible = "phytium,pcie-io";
reg = <0x60000000 0x10000>; // 64 KB BAR0
interrupts = <0 89 4>; // GIC_SPI 89, level
interrupt-names = "io-intr";
phytium,irq-mode = <1>; // 1=threaded
};
};
};
编译并拷贝到 /boot/dtb/ 目录,内核 bootargs 加 dtb=ft2000-pcie-io.dtb
5.2 驱动骨架(线程化中断版)
// ft_pcie_io.c
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/rtmutex.h>
#define DRV_NAME "ft-pcie-io"
#define REG_IRQ_STATUS 0x0c
static struct pci_dev *g_pdev;
static void __iomem *bar0;
static DEFINE_RT_MUTEX(io_lock);
static irqreturn_t io_hardirq(int irq, void *data)
{
u32 status = ioread32(bar0 + REG_IRQ_STATUS);
if (status & 0x1)
return IRQ_WAKE_THREAD; // 唤醒线程化下半部
return IRQ_NONE;
}
static irqreturn_t io_threadfn(int irq, void *data)
{
u32 status;
rt_mutex_lock(&io_lock);
status = ioread32(bar0 + REG_IRQ_STATUS);
// TODO: 上报事件给用户空间
rt_mutex_unlock(&io_lock);
return IRQ_HANDLED;
}
static int ft_pcie_io_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int err;
err = pci_enable_device(pdev);
if (err) return err;
err = pci_request_regions(pdev, DRV_NAME);
if (err) goto disable;
bar0 = pci_iomap(pdev, 0, 0x10000);
if (!bar0) { err = -ENOMEM; goto release; }
// 请求线程化中断
err = devm_request_threaded_irq(&pdev->dev, pdev->irq, io_hardirq,
io_threadfn, IRQF_SHARED,
DRV_NAME, pdev);
if (err) goto unmap;
pci_set_master(pdev);
g_pdev = pdev;
dev_info(&pdev->dev, "FT PCIe-IO ready, irq=%d\n", pdev->irq);
return 0;
unmap:
pci_iounmap(pdev, bar0);
release:
pci_release_regions(pdev);
disable:
pci_disable_device(pdev);
return err;
}
static void ft_pcie_io_remove(struct pci_dev *pdev)
{
pci_iounmap(pdev, bar0);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static const struct pci_device_id ft_pcie_io_ids[] = {
{ PCI_DEVICE(0x1ed9, 0x2000) }, // 厂商ID/设备ID
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ft_pcie_io_ids);
static struct pci_driver ft_pcie_io_driver = {
.name = DRV_NAME,
.id_table = ft_pcie_io_ids,
.probe = ft_pcie_io_probe,
.remove = ft_pcie_io_remove,
};
module_pci_driver(ft_pcie_io_driver);
MODULE_LICENSE("GPL");
编译:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ft_pcie_io.ko
5.3 实时用户空间接口(字符设备 + ioctl)
// 简化版:提供非阻塞读事件 + 32 bit 寄存器读写
#define IOCTL_REG_RD _IOR('k', 1, uint32_t)
#define IOCTL_REG_WR _IOW('k', 2, uint32_t)
static long ft_pcie_io_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
uint32_t val;
switch (cmd) {
case IOCTL_REG_RD:
rt_mutex_lock(&io_lock);
val = ioread32(bar0 + REG_INPUT);
rt_mutex_unlock(&io_lock);
return copy_to_user((void __user *)arg, &val, sizeof(val)) ? -EFAULT : 0;
case IOCTL_REG_WR:
if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
return -EFAULT;
rt_mutex_lock(&io_lock);
iowrite32(val, bar0 + REG_OUTPUT);
rt_mutex_unlock(&io_lock);
return 0;
}
return -EINVAL;
}
用户空间循环周期 1 ms:
// user_main.c
int fd = open("/dev/ft_pcie_io", O_RDONLY | O_NONBLOCK);
uint32_t in, out = 0;
while (1) {
ioctl(fd, IOCTL_REG_RD, &in);
// TODO: 控制算法
out = in ^ 0x1;
ioctl(fd, IOCTL_REG_WR, &out);
usleep(1000); // 1 ms
}
5.4 中断延迟测试
# 1. 加载驱动
sudo insmod ft_pcie_io.ko
# 2. 运行 trace
sudo trace-cmd start -e irq_handler_entry -e irq_handler_exit -e sched_switch
# 3. FPGA 发 1 kHz 脉冲
# 4. 停止并生成图
sudo trace-cmd stop
sudo trace-cmd report > irq_latency.txt
典型结果(FT-2000/4 1.8 GHz,PREEMPT_RT):
irq_handler_entry: irq=89 timestamp= 8012.386 us
irq_handler_exit: irq=89 timestamp= 8012.404 us
中断服务耗时 18 μs,满足 < 20 μs 目标。
六、常见问题与解答(FAQ)
| 问题 | 现象 | 解决 |
|---|---|---|
| insmod 报“Unknown symbol” | 内核未开 CONFIG_PREEMPT_RT | 重新编译打开 RT |
| 中断无触发 | /proc/interrupts 里计数不增 | 检查设备树 interrupt 号与 FPGA 实际连线 |
| cyclictest Max > 100 μs | 偶尔出现 | 关闭 CPU 变频:echo performance > /sys/devices/.../scaling_governor |
| 用户空间 ioctl 延迟抖动大 | 大于 10 μs | 给线程 SCHED_FIFO 优先级 90:chrt -f 90 ./user_main |
| 审核要求“源码可追溯” | 无版本标识 | 在驱动里加 MODULE_INFO(git, GIT_HASH) ,Makefile 自动注入 |
七、实践建议与最佳实践
-
锁策略:所有临界区用
rt_mutex,禁用raw_spinlock;短时原子操作才用spin_lock_irqsave。 -
IRQ 线程优先级:推荐 50-99,数值越高越实时,但别抢调度器本身(软中断 9)。
-
内存分配:实时路径用
kmalloc而非vmalloc,避免页表抖动;> 128 B 用GFP_ATOMIC。 -
故障注入:定期
echo 1 > /sys/kernel/debug/fail_make_request/enable模拟 IO 错误,验证诊断覆盖率。 -
文档化:驱动头文件里写“安全注释”——功能、SIL 等级、诊断方式,方便审计。
-
CI 门禁:GitLab Runner 里跑
make -j$(nproc) M=$(pwd) modules,单元测试失败即拒绝合并。
八、总结:一张脑图带走全部要点
飞腾实时驱动开发
├─ 环境:RT 内核 + DTS + 工具链
├─ 开发:pci_register_driver + devm_request_threaded_irq
├─ 优化:rt_mutex + IRQ 线程优先级 99
├─ 测试:cyclictest + trace-cmd + 故障注入
├─ 文档:安全注释 + Git 版本 + 单元覆盖
└─ 认证:可追溯链 + 诊断覆盖率 ≥ 90%
国产芯 + 实时 Linux 不再是“能跑就行”,而是“能审、能过、能量产”。
把本文模板 push 到你的 GitLab,下次飞腾板卡上电,30 分钟交付一套可审计的实时驱动,让国产化工业控制真正做到自主可控、安全可信!
更多推荐

所有评论(0)