ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

melonDS 中 Teak DSP 定时器(Timer)模块深度解析:MMIO 布局、计数模式与 CoreTiming 源码实现

melonDS 中 Teak DSP 定时器(Timer)模块深度解析:MMIO 布局、计数模式与 CoreTiming 源码实现 虚拟化图形学桌面应用【免费下载链接】melonDSDS emulator, sorta项目地址https://gitcode.com/gh_mirrors/me/melonDS点击查看免费下载melonDS 通过内置的 Teakra 子项目仿真 3DS 的 Teak DSP音频 DSP 核心。本文聚焦 Teakra 中两个硬件定时器Timer 0 / Timer 1的完整技术细节以 timer.md 为骨架逐位拆解其 MMIO 寄存器布局与字段语义并结合 timer.cpp、mmio.cpp、core_timing.h 等源码还原真实行为说明计数模式、事件计数、中断上报与时钟节拍跳转机制的底层实现。读完本文你将能够独立理解 DSP 固件中定时器相关 MMIO 的读写逻辑并能对照源码验证仿真器对每个寄存器位字段的实际支持程度。定时器在 Teakra 仿真中的位置Teakra 是 melonDS 用于仿真 3DS DSPTeakLite 核心的内嵌子项目其源码位于 src/teakra。在 Teakra 的 MMIO 总览文档 mmio.md 中定时器被登记在基地址偏移0x0020处与 APBP、BTDMP、DMA、ICU 等外设并列。实际实例化可见 teakra.cppstd::arrayTimer, 2 timer{{{core_timing, 0}, {core_timing, 1}}};即每个 DSP 实例包含两个定时器Timer 0 与 Timer 1二者共享同一套 MMIO 布局仅基址相差0x10。两个定时器的中断分别上报到 ICU 的 IRQ 0xA 与 0x9见下文“中断与 ICU 联动”一节。MMIO 布局总览原文档 timer.md 给出的 MMIO 定义提取自 Lauterbach 的 Teak 调试器其中部分字段尚未经过完整测试。Timer 0 的寄存器布局如下Timer 0 -----------#------------#------------#------------#------------# |0x0020 | TM | GP| CS| BP|RES| MU| PC| CT| TP| | CM | TS | -----------#------------#------------#------------#------------# |0x0022 | | EW| -----------#------------#------------#------------#------------# |0x0024 | START_COUNT_L | -----------#------------#------------#------------#------------# |0x0026 | START_COUNT_H | -----------#------------#------------#------------#------------# |0x0028 | COUNTER_L | -----------#------------#------------#------------#------------# |0x002A | COUNTER_H | -----------#------------#------------#------------#------------# |0x002C | PWM_COUNTER_L | -----------#------------#------------#------------#------------# |0x002E | PWM_COUNTER_H | -----------#------------#------------#------------#------------#Timer 1 从0x0030开始与 Timer 0 完全相同的布局每寄存器偏移 0x10。为便于阅读下面将十六进制偏移统一换算为绝对 MMIO 地址偏移相对0x0000寄存器名Timer 0 偏移Timer 1 偏移字段说明TIMERx_CFG0x200x30TS / CM / TP / CT / PC / MU / RES / BP / CS / GP / TM配置寄存器16 位TIMERx_EW0x220x32EW事件计数减一 / 看门狗重载触发16 位TIMERx_SCL0x240x34START_COUNT_L计数器重启初值低 16 位TIMERx_SCH0x260x36START_COUNT_H计数器重启初值高 16 位TIMERx_CCL0x280x38COUNTER_L计数器当前值低 16 位只读反映TIMERx_CCH0x2A0x3ACOUNTER_H计数器当前值高 16 位只读反映TIMERx_SPWMCL0x2C0x3CPWM_COUNTER_LPWM 计数器重启初值低 16 位TIMERx_SPWMCH0x2E0x3EPWM_COUNTER_HPWM 计数器重启初值高 16 位在 mmio.cpp 中这些寄存器被一一映射为 MMIO Cell0x20/0x30是配置寄存器BitFieldCell0x22/0x32是 EW写 1 触发事件0x24~0x2A直接以RefCell绑定到start_low、start_high、counter_low、counter_high成员变量而 PWM 相关的0x2C/0x2E目前只是空的Cell()占位尚未接入真实逻辑。配置寄存器 TIMERx_CFG 字段语义原文档 timer.md 对0x20/0x30处 16 位配置寄存器的字段从高到低定义如下结合 mmio.cpp 的位槽实现可确认各字段的位宽与位置位Bit助记符含义0–1TS分频比例time scale0 → /11 → /22 → /43 → /162–4CM计数模式count mode详见下文5TP输出信号极性output signal polarity6CT置 1 清除输出信号7PC置 1 暂停计数器8MU置 1 使能 COUNTER_L/COUNTER_H 寄存器更新9RES置 1 重启计数器10BP置 1 使能断点请求breakpoint requests11CS时钟源0 → 内部时钟1 → 外部时钟12GP未定义原文标记为?13–14TM输出信号清除方式0 → 手动置 CT1 → 两个周期后2 → 四个周期后3 → 八个周期后15未使用—对照 mmio.cpp 的实现TS、CM、PC、MU四个字段通过RefSlot直接绑定到 timer.h 中的scale、count_mode、pause、update_mmio成员RES是一个写触发槽一旦被写入 1 就调用Timer::Restart()读回恒为 0而TP、CT、BP、CS、GP、TM目前均为空位槽BitFieldSlot{...}无读写回调即仿真器暂未对这几个字段建模——这与原文档“Some of them are untested”的标注一致。TS时间分频TS决定计数节拍相对 DSP 主时钟的分频倍数四种取值对应 /1、/2、/4、/16。在真实硬件上它决定定时器每秒“走几步”在仿真器中该值被保存在scale成员中而当前 timer.cpp 的Tick()含断言ASSERT(scale 0)意味着本仓库的仿真实现只支持 /1不分频场景。PC / MU暂停与读数使能PCpause置 1 后计数器冻结。Tick()、TickEvent()、GetMaxSkip()、Skip()的第一步都是检查pause为真则直接返回不再递减计数见 timer.cpp。MUupdate_mmio决定每次递减后是否把内部 32 位counter拆写进只读的COUNTER_L/COUNTER_H。UpdateMMIO()的入口即为if (!update_mmio) return;timer.cpp。这是仿真上的一个优化点固件若不需要实时读取计数值可关闭该字段以减少每次节拍的 MMIO 刷新开销。RES软件重启RES是写 1 触发write-1-to-trigger字段。写 1 后仿真器执行 timer.cpp 的Restart()把(start_high 16) | start_low装入counter自由运行模式除外并立即刷新 MMIO。原文档指出 START_COUNT_L/START_COUNT_H 是“重启时装入计数器的初值”正是该函数的直接实现。计数模式CM与看门狗CM字段宽 3 位原文档定义了 7 种模式CM 值模式行为简述0single count单次计数计数到 0 停止1auto restart自动重启到 0 后装入 START_COUNT 初值继续2free running自由运行到 0 后回绕到 0xFFFFFFFF 继续3event count事件计数由 EW 写 1 驱动递减4watchdog mode 1看门狗模式 15watchdog mode 2看门狗模式 26watchdog mode 3看门狗模式 3仿真器侧只实现了前四种。在 timer.h 中它们被定义为一个枚举enum class CountMode : u16 { Single 0, AutoRestart 1, FreeRunning 2, EventCount 3, };对应的行为逻辑集中在 timer.cppSingle单次计数counter每节拍减一减到 0 即触发中断处理函数之后不再递减AutoRestart自动重启减到 0 时调用Restart()重新装载(start_high 16) | start_low并触发中断FreeRunning自由运行减到 0 时把counter回绕为0xFFFFFFFF并触发中断初值寄存器在回绕时被忽略EventCount事件计数不受主时钟节拍驱动Tick()直接返回改由TickEvent()在 EW 写 1 时递减一次计数到 0 同样触发中断。原文档中列出的 watchdog mode 1/2/3 对应EW字段“在看门狗模式下重载看门狗”的语义但当前仓库的CountMode枚举中并无对应值且mmio.cpp的位槽仅使用 2 位宽存储 CMRefSlot(2, 3, timer[i].count_mode)中 3 为位宽、值为 0–7 可表示全部 7 种模式因此可以推断本版本仿真器尚未实现三种看门狗模式的运行逻辑仅保留了 MMIO 层面的位宽表达。COUNTER 与 PWM_COUNTER 寄存器COUNTER_L / COUNTER_H0x28/0x2A、0x38/0x3A32 位计数器当前值的低/高半字。它们是只读反映寄存器内部维护u32 counter仅在update_mmioMU1时经UpdateMMIO()同步到counter_low/counter_hightimer.cpp。原文档称其为“计数器的值”从源码看其可读性受 MU 位门控。START_COUNT_L / START_COUNT_H0x24/0x26、0x34/0x36可读写初值寄存器直接映射start_low/start_high供 AutoRestart 与 RES 重启时装载。PWM_COUNTER_L / PWM_COUNTER_H0x2C/0x2E、0x3C/0x3E原文档定义为“PWM 计数器重启初值”。当前 mmio.cpp 仅以空Cell()占位读写均无效果PWM 功能尚未仿真。中断与 ICU 联动两个定时器在 Teakra 启动时注册中断处理函数teakra.cpptimer[0].SetInterruptHandler([this]() { icu.TriggerSingle(0xA); }); timer[1].SetInterruptHandler([this]() { icu.TriggerSingle(0x9); });即 Timer 0 计数到 0 时向 ICU 上报 IRQ 0xATimer 1 上报 IRQ 0x9。ICUInterrupt Control Unit随后按 icu.h 的逻辑判断是否使能并转发给处理器最终由解释器在指令边界检查regs.ip[i]并以regs.pc 0x0006 i * 8跳转到中断向量见 interpreter.h。定时器的中断处理函数由外部注入std::functionvoid()见 timer.h硬件定时器本体并不直接持有 ICU 引用保持了模块间低耦合。节拍驱动的仿真实现Tick 与 CoreTiming定时器是 Teakra 全局节拍系统CoreTiming的参与者。Timer继承自CoreTiming::Callbackstimer.h在构造时调用core_timing.RegisterCallbacks(this)完成注册timer.cpp。core_timing.h 定义了两种驱动方式逐拍 Tick解释器每执行一条指令后调用core_timing.Tick()向所有回调广播一次节拍interpreter.h批量 Skip当处理器处于 idle空闲等待中断状态时解释器调用core_timing.Skip(maximum)interpreter.h。Skip先对所有回调询问GetMaxSkip()取最小值——保证“跳到下一个会发生中断/翻转的节拍之前”再统一调用各回调的Skip(ticks)一次性批量推进。定时器对这两种驱动分别实现为Tick()单拍递减并处理到 0 时的重启/回绕/中断timer.cppGetMaxSkip() / Skip()批量推进。GetMaxSkip依据当前模式给出最多可安全跳过的节拍数——AutoRestart 返回初值、FreeRunning 返回0xFFFFFFFF、Single 在 counter0 时返回Infinitytimer.cppSkip则一次性减去对应节拍数并重算翻转点timer.cpp。这套机制保证了定时器在批量仿真下仍然能在正确的节拍上触发中断是定时器精度与仿真性能之间的关键平衡点。事件计数模式EventCount在GetMaxSkip中返回Infinity因为它不随主时钟推进不会影响批量跳转的边界计算。保存/恢复状态定时器参与 melonDS 的存档Savestate机制。DoSavestatetimer.cpp以TKt0/TKt1区分两个定时器对应 Timer 0 / Timer 1依次保存update_mmio、pause、count_mode、scale以及start_high、start_low、counter、counter_high、counter_low共 9 项状态确保跨存档恢复后定时器的配置与计数值保持一致。Reset()timer.cpp则把定时器复位为单次计数模式、不分频、无暂停、MMIO 更新关闭、计数器与初值全部清零。定时器与 PMU电源管理单元的关系在 Teakra 的电源管理文档 pmu.md 中Timer 0 / Timer 1 被用作模块恢复recovery的计时源T0x/T1x位分别使能“模块 x 在 Timer 0 / Timer 1 上恢复”且在 PMU 的时钟源选择字段中0 表示 Timer 0、1 表示 Timer 1。这解释了硬件上定时器的典型用途——除了产生周期中断外还可配合 PMU 实现模块的定时唤醒/恢复调度是 DSP 低功耗流程的重要一环。从源码结构看mmio.cpp 中定时器与 PMU 各自独立映射二者在仿真器中的联动依赖固件通过 MMIO 分别配置。小结当前仿真覆盖度与注意事项汇总本仓库src/teakra对定时器的实现程度已实现TS仅 /1、CM 前四种计数模式Single / AutoRestart / FreeRunning / EventCount、PC 暂停、MU 读数使能、RES 写 1 重启、START_COUNT/COUNTER 寄存器的读写与同步、EW 事件计数触发、节拍驱动的 Tick/Skip 批量推进、双定时器的独立中断上报IRQ 0xA / 0x9、存档支持。未实现 / 占位TP、CT、BP、CS、GP、TM 六个字段为空位槽PWM_COUNTER 寄存器为空 Cell三种 watchdog 模式未纳入CountMode枚举scale仅支持 0/1。原文档 timer.md 本身也注明 MMIO 定义“部分未测试”。因此当你在阅读 DSP 固件代码或编写新的 Teakra 测试时若涉及上述未实现字段应将其视为真实硬件的既有语义、而非当前仿真器可观察到的行为——这正是继续完善 timer.cpp 与 mmio.cpp 中对应位槽的切入点。赞分享虚拟化图形学桌面应用【免费下载链接】melonDSDS emulator, sorta项目地址https://gitcode.com/gh_mirrors/me/melonDS点击查看免费下载相关推荐Teak DSP 片上仿真模块OCEM调试寄存器详解melonDS/Teakra 项目中的 MMIO 布局与程序/数据断点机制Teak DSP 片上仿真模块OCEM调试寄存器详解melonDS/Teakra 项目中的 MMIO 布局与程序/数据断点机制 本指南以 melonDS虚拟化图形学桌面应用melonDS Teak DSP 电源管理单元PMUMMIO 寄存器详解melonDS Teak DSP 电源管理单元PMUMMIO 寄存器详解 导读 本文基于 melonDS 仓库中 Teakra 子项目的硬件文档 pmu.m虚拟化图形学桌面应用melonDS 内嵌 Teak DSP 的 SIO 串行接口外设MMIO 寄存器布局与数据移位协议详解melonDS 内嵌 Teak DSP 的 SIO 串行接口外设MMIO 寄存器布局与数据移位协议详解 导读 melonDS 项目中内嵌了 teakra ——虚拟化图形学桌面应用上一篇如何3分钟搞定国家中小学智慧教育平台电子课本下载这个免费工具太强了下一篇3分钟搞定电子课本下载智慧教育平台资源解析工具完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表