根据一些教程,我们将在bootlaoder的开头禁用MMU和I / D-Caches。如果我理解正确,它的目的是直接在程序中使用物理地址,所以如果我错了请纠正我。谢谢!
其次,我们这样做是为了禁用MMU和缓存:
mrc P15,0,R0,C1,C0,0
bic R0,R0,#0x00002300 @ clear bits 13,9:8
bic R0,R0,#0x00000087 @ clear bits 7,2:0
orr R0,R0,#0x00000002 @ set bit 2(A)Align
orr R0,R0,#0x00001000 @ set bit 12(I)I-Cache
mcr P15,0,R0,C1,C0,0
D-Cache,MMU和数据地址对齐故障检查已被清除位2:0禁用,但为什么我们在下列仪器中立即启用位2?为确保此操作有效?
最后一个问题是为什么D-cache被禁用但I-caches能够?加快仪器进程?
答案 0 :(得分:9)
最后一个问题是为什么D-cache被禁用但I-caches能够?加快仪器进程?
MMU具有确定哪些内存区域可缓存的设置。如果您没有打开mmu但是您打开了数据缓存(如果可能),那么您将无法安全地与外围设备通信。如果您读取uart状态寄存器,例如通过缓存,就像任何其他数据操作一样,无论该状态是什么,都会保留在缓存中以供后续读取,直到该缓存行被驱逐并且您再获得一次实际操作寄存器。让我们假设您有一些代码可以轮询uart状态寄存器,等待rx缓冲区中的字符。如果第一次读取显示没有字符,那么该状态进入缓存,您将永远保持循环,因为您永远不会再次与状态寄存器通信,您只需获取寄存器的缓存副本。如果那里有一个字符然后该状态也被缓存,你读取rx寄存器,也许做一些事情,如果再次回来,如果状态没有从数据缓存中逐出,那么你会得到陈旧状态,显示有一个字符,你的rx缓冲区读取可能也可能不会被缓存,因此你可能会得到缓存中的陈旧值,你可能会得到一个陈旧的值或者你读到时外围设备所做的任何事情,并且没有新值或你可能获得一个新值,但在这些情况下你得到的是对外围设备的正确访问。当mmu打开时,您使用mmu将该外围设备使用的地址空间标记为非(数据) - 可缓存,并且您没有此问题。关闭mmu后,您需要关闭arm系统的数据缓存。
启用I-cache是可以的,因为指令只读取指令...对于裸机应用程序来说没问题,例如,如果您使用的是具有读取干扰潜力的闪存(spi或i2c闪烁)。问题是这个应用程序是一个引导加载程序,所以你必须要特别小心。例如,您的引导加载程序在地址0x8000处有一些代码,它至少运行一次,然后您选择将其用作引导加载程序,引导加载程序可能位于地址0x10000000,允许您在0x8000加载新程序,此加载使用数据访问所以它不通过指令缓存。因此,有可能指令缓存具有上次在0x8000区域时的部分或全部代码,并且当您在0x8000分支到引导加载的代码时,您将从缓存中获取旧程序或者令人讨厌的混合旧程序和新程序的缓存和未缓存的部分。因此,如果您的引导加载程序允许启用i-cache,则需要在分支到引导加载的代码之前使缓存无效。
最后,如果您或使用此引导加载程序的任何人想要使用jtag,那么您会遇到同样的问题,但更糟糕的是,当您告诉时,不会通过i-cache的数据周期将新程序写入ram然后运行新程序的jtag调试器将获得1)只有新程序,2)新程序和来自缓存的旧程序片段的混合3)来自缓存的旧程序。
所以d-cache在没有mmu的情况下很糟糕,因为ram,外围设备等都没有.i-cache是一种自担风险的用途,你可以减轻这种情况,除了jtag是时候用于调试。
如果您在(外部)闪存中有疑虑或已确认读取干扰,那么我建议打开i-cache,使用紧密循环将应用程序复制到ram,分支到ram副本并在那里运行,关闭i-cache(或者使用时请自担风险)并且不要再次触摸闪光灯,当然也不会对小区域进行大量读取访问。像命令行解析器一样的紧密uart轮询循环是一个非常适合读取干扰的地方。
答案 1 :(得分:2)
您没有指定您正在使用哪个ARM。功能可能因ARM而异(ARM9和ARM Cortex A15之间存在巨大差距)。
在给定代码中,第2位被清除然后设置,但无关紧要,因为这些更改是在R0中完成的。在CP15寄存器中写入之前,ARM行为没有变化(由指令mcr P15, 0, R0, C1, C0, 0
完成)。
关于d-cache / i-cache启用,这只是一个选择问题,没有要求。在我使用的产品上,引导加载程序启用了L1 I-cache,D-cache,L2缓存和MMU(并且它在跳转到Linux之前禁用所有这些东西)。如果在引导加载程序中使用缓存和MMU,请务必遵循有关缓存失效和内存障碍的ARM文档(根据您的实际ARM Core)。