当前位置: 首页 > news >正文

一个经典嵌入式问题:如何安全读取 64 位计时器

在一个嵌入式系统中,需要读取一个持续自增的 64-bit 硬件计时器。

由于系统只能通过 32-bit MMIO register 访问该计时器,所以硬件把它拆成两个 32-bit 寄存器:

#define TIMER_LOW_ADDR 0x40001000 #define TIMER_HIGH_ADDR 0x40001010

其中:

TIMER_LOW 表示 timer[31:0] TIMER_HIGH 表示 timer[63:32]

计时器会持续递增。当低 32 位从:

0xFFFFFFFF -> 0x00000000

发生回绕时,高 32 位会加 1。

请实现:

uint64_t read_timer(void);

要求安全地返回一个一致的 64-bit timer 值。

这个题的考点主要是读取高低位时可能发生的race condition. 考虑以下情况:

硬件寄存器一开始的值比如
HIGH = 0x00000001
LOW = 0xffffffff

先读取HIGH: 读到 0x00000001

然后在读 LOW 之前,timer tick 了一次:

HIGH = 0x00000002
LOW = 0x00000000

再读LOW: 读到 0x00000000

如果直接拼:

0x00000001_00000000

这就是错的,因为真实时间已经至少是:

0x00000002_00000000

同样的,如果我们先读LOW: 读到 0xffffffff

然后在读 HIGH 之前,timer tick 了一次:

HIGH = 0x00000002
LOW = 0x00000000

再读HIGH: 读到 0x00000002

如果直接拼:

0x00000002_ffffffff

这也是错的,因为我们的LOW已经wrap, 应该读成:

0x00000002_00000000

解决核心是多读一次 HIGH,并比较两次 HIGH 的值。

如果 hi1 == hi2,说明在读取 LOW 的前后,HIGH 没有变化,也就是 LOW 没有发生 wrap-around 进位。因此 LOW 是在同一个 high epoch 下读取到的,可以安全拼接:

(hi1 << 32) | low

如果 hi1 != hi2,说明在两次读取 HIGH 之间,LOW 发生了 overflow,导致 HIGH 被更新。此时读到的 LOW 可能属于新的 high epoch,而第一次 HIGH 属于旧的 epoch,所以组合结果不一致,需要重新读取,直到两次 HIGH 相等。

为什么不多读一次 LOW?

因为 LOW 一直在变。多读 LOW 只能告诉你“时间过了”,但不能可靠判断有没有跨过0xffffffff -> 0x00000000这个边界。

真正有意义的标志是:HIGH 有没有变化。 因为 HIGH 只在 LOW wrap 时变化。

有了核心思路,实现就变得很简单。

#define TIMER_LOW_ADDR 0x40001000u #define TIMER_HIGH_ADDR 0x40001010u #define LOW32_REG (*(volatile uint32_t *)TIMER_LOW_ADDR) #define HIGH32_REG (*(volatile uint32_t *)TIMER_HIGH_ADDR) uint64_t read_timer(void) { uint32_t hi1, hi2, lo; do { hi1 = HIGH32_REG; lo = LOW32_REG; hi2 = HIGH32_REG; } while (hi1 != hi2); return (((uint64_t)hi1 << 32) | lo); }

代码的细节有几点要注意:

  1. 访问MMIO 寄存器

有了 MMIO memory address 后,需要把地址 cast 成对应宽度的指针:

(volatile uint32_t *)TIMER_LOW_ADDR

这里uint32_t *表示一次读取 32-bit;volatile表示这个地址背后的值可能被硬件随时改变,编译器不能缓存、合并或省略读取。

然后通过解引用读取寄存器值:

*(volatile uint32_t *)TIMER_LOW_ADDR

所以通常写成宏:

#define LOW32_REG (*(volatile uint32_t *)TIMER_LOW_ADDR)

2. 拼接 64-bit 结果

高 32 位要先 cast 成uint64_t,再左移 32 bit:

((uint64_t)hi1 << 32)

然后用 bitwise OR 拼接低 32 位:

return (((uint64_t)hi1 << 32) | lo);
http://www.rkmt.cn/news/1309635.html

相关文章:

  • 5分钟掌握Fillinger:告别Illustrator繁琐填充,开启智能设计新时代
  • 大语言模型快速上手指南:从零到一构建LLM应用实践
  • 食品配方优化新范式,NotebookLM+质谱数据联动建模全流程拆解
  • LLM 的(较少为人知的)崛起应用
  • 基于全域数学0-1-∞体系的1.237宇宙临界常数及时空超导统一理论
  • Supabase 自建:开源的 Firebase 替代品,带数据库的后端服务
  • Plain Craft Launcher网络架构解密:从请求异常到高性能启动器的进阶之路
  • AI冲击下程序员大批失业,为啥做网安反而越混越吃香?
  • ComfyUI ControlNet Aux终极指南:新手必学的图像预处理完整解决方案
  • AzurLaneAutoScript:碧蓝航线智能自动化助手终极指南
  • AI-Git-Narrator:基于LLM的Git提交历史自动化分析与文档生成工具
  • 物联网设备网络无缝切换与多网融合:exnetif模块实战指南
  • Draft-classic:云原生开发中Kubernetes部署的快速原型工具
  • 游戏存档管理终极指南:告别背包焦虑的5大解决方案
  • 告别Claude Code封号烦恼,通过Taotoken稳定使用编程助手
  • 你的QQ音乐文件突然不能播放了?这个开源工具能一键搞定
  • 3分钟高效解密RPG游戏资源:浏览器端专业解密工具完全指南
  • 立讯精密16年市值增79倍,王来春身价涨超千亿,收购京西国际补齐短板!
  • STM32驱动段码屏实战:手把手教你用HT1621B做个简易电子钟(附完整代码)
  • 2026上海徐汇区黄金回收指南|就近门店+上门服务随心选+实时回收价格对比 - 速递信息
  • 基于Godot 4的银河恶魔城游戏系统框架设计与实现
  • Telegram机器人ChatGPT回复格式化:Markdown转HTML中间件实践
  • 世毫九实验室技术报告 TR-007:认知的自指重构与递归对抗式教学
  • 2026年4月有实力的空气能工程公司推荐,工厂蒸发冷空调空气能/学校空气能/学校常温型空气能,空气能定制厂家口碑推荐 - 品牌推荐师
  • RK3566(泰山派)实战:D310T9362V1SPEC触摸屏驱动从零适配与调试(竖屏)
  • 俄语AI资源导航库:构建本土化学习生态与高效利用指南
  • 企业级应用如何利用Taotoken实现多模型智能客服
  • GitHub Actions自动化:从Issue到PR的智能转换工作流实践
  • 思源笔记 Steve Tools — 一个功能强大的个人工具集
  • MTKClient:联发科芯片深度调试与固件分析技术方案