ARM技术论坛
直播中

张静

7年用户 1459经验值
私信 关注
[问答]

在ARM里为什么D-cache被禁用而I-cache却可以开呢

      MMU可以决定哪些内存区域可以缓存,哪些不可以。如果你没有打开MMU,但你打开了数据缓存(如果可能的话,对于一些硬件压根从硬件上就不支持),那么你就不能安全地与外围设备交互。例如,如果你读取uart状态寄存器,就像其他数据操作一样,要经过缓存,无论该状态是什么,都会留在缓存中供以后的读取,直到该缓存行被驱逐,你才能再获得一次机会来读取实际硬件寄存器。比如说,你有一些代码在轮询uart状态寄存器,等待rx缓冲区中的有字符。如果第一次读取显示没有字符,那么这个状态就会进入缓存,你将永远停留在这个循环中,因为你将永远无法再与状态寄存器对话,你将只是得到寄存器的缓存副本。如果里面有一个字符,那么这个状态也会被缓存起来,你读取rx寄存器,也许会做一些事情,如果当你再次回来时,如果状态还没有被从数据缓存中驱逐,那么你就会得到显示有一个字符的陈旧状态。你读取的rx缓冲区可能是也可能不是被缓存的,所以你可能在缓存中得到陈旧的值,你可能得到一个陈旧的值或任何外设在你读取时没有新的值,或者你可能得到一个新的值,但在这些情况下你得不到的是对外设的正确访问。当mmu打开时,你用mmu把外设使用的地址空间标记为不可(数据)缓存,你就不会有这个问题了。在mmu关闭的情况下,你需要关闭arm系统的DCache。
     特别要说明cache的命中与不命中取决于地址,以前有这个地址就会命中,boot的代码一直向后执行,地址一直递增一般不会命中。即使命中也没关系,只要cache和你内存或者flash的代码一样就不存在问题。所以把I-cache打开一般情况是可以的,因为指令的获取只需要读取指令......对于裸机应用来说是可以的。
      但是例如你的引导程序在地址0x8000有一些代码,它至少要运行一次,然后你选择把它作为引导程序,引导程序可能在比如地址0x10000000允许你在0x8000加载一个新的程序,这个加载使用数据访问,所以它不通过指令缓存。所以有可能指令缓存中有一些或所有你上次在0x8000区域的代码,当你在0x8000处分支到启动加载的代码时,你会得到缓存中的旧程序或旧程序和新程序的讨厌的混合部分,这些部分被缓存和不被缓存。因此,如果你的引导程序允许i-cache开启,你需要在分支到引导的代码之前使cache失效。说到底就是代码已经变了但是没有通过cache,比如我们反复运行0x8000这个地址,但是第一次运行0x8000的时候代码是从nand加载的,第二次是从emmc加载的,只要没有断电,cahce是感知不到你这样玩的。
       最后,如果你或使用这个引导程序的人想使用jtag,那么你也有同样的问题,但更糟糕的是,没有经过i-cache的数据周期被用来将新的程序写入ram,当你告诉jtag调试器然后运行新的程序时,你会得到1)只有新的程序,2)新程序和来自cache的旧程序片段的混合,3)来自cache的旧程序。
所以,d-cache在没有mmu的情况下是不好的,因为有一些东西不在ram里,外围设备等。i-cache是一种使用风险自负的东西,除了jtag用于调试的时候,你可以减轻风险。
       如果你有顾虑或者确认你的(外部)flash有读扰,那么我建议打开i-cache,用一个紧密的循环把你的程序拷贝到ram,分支到ram拷贝并在那里运行,关闭i-cache(或者使用风险自负),不要再碰flash,当然不要对小区域进行重读访问。一个紧密的uart轮询循环,就像你为一个命令行分析器所做的那样,是一个非常好的被读取干扰的地方。

原作者:张飞online

回帖(1)

刘辉

2022-5-24 11:58:42
举报

更多回帖

发帖
×
20
完善资料,
赚取积分