You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FPGA寄存器的Linux时钟源适配问题咨询

问题分析与解决方案

使用clocksource是实现FPGA时钟作为系统时钟源的正确方案,但你的驱动存在几个关键缺陷,导致计时偏差和调度器panic,以下是具体问题和修复方法:

核心问题1:寄存器读取的原子性缺失

FPGA时钟的秒寄存器和亚秒寄存器是独立更新的,两次readl_relaxed之间可能发生秒进位(比如亚秒寄存器溢出导致秒寄存器加1),这会读取到不一致的秒/亚秒组合,造成时间回退——而clocksource要求时钟必须单调递增,时间回退会直接触发调度器panic。

修复方法:循环读取直到秒寄存器值稳定,确保两次读取的秒值一致:

static u64 _FPGA_TIME_clocksource_read(void)
{
    u64 counter;
    u32 seconds, subseconds, seconds_check;

    do {
        seconds = readl_relaxed(g_timerInfo.reg + FPGA_TIME_ADDR_CTRL_TIME_SECONDS);
        subseconds = readl_relaxed(g_timerInfo.reg + FPGA_TIME_ADDR_INFO_TIME_SUBSECONDS);
        seconds_check = readl_relaxed(g_timerInfo.reg + FPGA_TIME_ADDR_CTRL_TIME_SECONDS);
    } while (seconds != seconds_check);

    // 正确转换:1秒 = 100,000,000个10ns tick
    counter = (u64)seconds * 100000000ULL;
    counter += subseconds;

    return counter;
}

核心问题2:时间转换逻辑错误

你的代码中subseconds << 1是完全错误的:FPGA的亚秒寄存器以10纳秒步长递增,而你注册的clocksource频率是100MHz(即每个tick对应10纳秒),因此亚秒寄存器的值直接就是低32位的tick数,秒寄存器需要转换为对应的tick数(1秒=100,000,000 ticks),组合后才是正确的计数器值。之前的位运算会导致时间计算严重偏差。

核心问题3:Sched_clock注册不当

你使用CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK宏来注册调度器时钟,但这个宏是针对ARM全局定时器的专用配置,不适用于你的FPGA时钟。此外,sched_clock要求读取操作极高效,若原子性读取逻辑未优化,可能引发额外的panic。

修复方法:

  • 移除依赖CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK的代码块
  • 若需要注册调度器时钟,使用通用的sched_clock_register接口,但确保读取函数足够高效(比如使用汇编优化或确保寄存器访问无延迟)

核心问题4:驱动框架不规范

你直接使用of_iomap和全局变量的方式初始化,未遵循Linux平台驱动的标准框架,可能导致初始化时机错误或资源泄漏。

修复方法:改为标准platform_driver结构,示例如下:

static struct platform_driver fpga_time_driver = {
    .driver = {
        .name = FPGA_TIME_DRIVER_NAME,
        .of_match_table = fpga_time_of_match,
    },
};

static const struct of_device_id fpga_time_of_match[] = {
    { .compatible = "your,fpga-time" },
    { },
};
MODULE_DEVICE_TABLE(of, fpga_time_of_match);

module_platform_driver_probe(fpga_time_driver, FPGA_TIME_probe);

在probe函数中完成iomap和clocksource注册:

static int FPGA_TIME_probe(struct platform_device *pdev)
{
    struct resource *res;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    g_timerInfo.reg = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(g_timerInfo.reg))
        return PTR_ERR(g_timerInfo.reg);

    return timer_clocksource_init();
}

用户态POSIX API访问说明

当clocksource正确注册并成为系统首选时钟源(调整FPGA_TIME_TIMER_RATING为300以上,高于默认时钟源的评级),内核的时间系统会自动基于该时钟运行。用户态应用直接使用标准POSIX API(如clock_gettime(CLOCK_REALTIME, ...)、gettimeofday())即可获取同步后的时间,无需直接访问硬件寄存器。

若需要将FPGA时钟作为系统实时时钟(RTC)使用,还可以额外注册一个RTC驱动,将FPGA的秒/亚秒寄存器映射为RTC时间,进一步完善系统时间的持久化和同步机制。

内容的提问来源于stack exchange,提问作者Ryan Sherlock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:36:00