Linux 中 ARM TCM(紧耦合内存)处理¶
作者:Linus Walleij <linus.walleij@stericsson.com>
一些 ARM SoC 具有所谓的 TCM(紧耦合内存)。这通常只是 ARM 处理器内部的少量(4-64)KiB 的 RAM。
由于嵌入在 CPU 内部,TCM 具有哈佛架构,因此存在 ITCM(指令 TCM)和 DTCM(数据 TCM)。DTCM 不能包含任何指令,但 ITCM 实际上可以包含数据。DTCM 或 ITCM 的大小最少为 4KiB,因此典型的最小配置为 4KiB ITCM 和 4KiB DTCM。
ARM CPU 具有特殊的寄存器来读取 TCM 内存的状态、物理位置和大小。arch/arm/include/asm/cputype.h 定义了一个 CPUID_TCM 寄存器,您可以从系统控制协处理器读取该寄存器。可以在 http://infocenter.arm.com 上找到 ARM 的文档,搜索“TCM Status Register”以查看所有 CPU 的文档。读取此寄存器可以确定机器中是否存在 ITCM(位 1-0)和/或 DTCM(位 17-16)。
此外,还有一个 TCM 区域寄存器(在 ARM 站点搜索“TCM Region Registers”),它可以报告和修改 TCM 内存在运行时的位置大小。这用于读取和修改 TCM 的位置和大小。请注意,这不是 MMU 表:您实际上是在移动 TCM 的物理位置。在您放置它的位置,它将屏蔽 CPU 的任何底层 RAM,因此通常明智的做法是不要使任何物理 RAM 与 TCM 重叠。
然后可以使用 MMU 将 TCM 内存再次重新映射到另一个地址,但请注意,TCM 通常用于关闭 MMU 的情况。为避免混淆,当前的 Linux 实现会将 TCM 从内核指定的位置以 1 对 1 的方式从物理内存映射到虚拟内存。目前,Linux 会将 ITCM 映射到 0xfffe0000 及以上,将 DTCM 映射到 0xfffe8000 及以上,最多支持 32KiB 的 ITCM 和 32KiB 的 DTCM。
较新版本的区域寄存器还支持将这些 TCM 分为两个单独的库,例如,一个 8KiB 的 ITCM 分为两个 4KiB 的库,每个库都有自己的控制寄存器。这样做的目的是能够锁定和隐藏其中一个库,供安全世界 (TrustZone) 使用。
TCM 用于以下几个方面
需要确定性定时且不能等待缓存未命中的 FIQ 和其他中断处理程序。
空闲循环,其中所有外部 RAM 设置为自刷新保留模式,因此只有片上 RAM 可供 CPU 访问,然后我们挂在 ITCM 内等待中断。
其他操作,这意味着关闭或重新配置外部 RAM 控制器。
在 <asm/tcm.h> 中有一个在 ARM 架构上使用 TCM 的接口。使用此接口可以
定义 ITCM 和 DTCM 的物理地址和大小。
标记要编译到 ITCM 中的函数。
标记要分配给 DTCM 和 ITCM 的数据和常量。
将剩余的 TCM RAM 添加到具有
gen_pool_create()
和gen_pool_add()
的特殊分配池,并为此内存提供 tcm_alloc() 和 tcm_free()。这样的堆非常适合在关闭设备电源域时保存设备状态之类的事情。
具有 TCM 内存的机器应从 arch/arm/Kconfig 中为其自身选择 HAVE_TCM。需要使用 TCM 的代码应 #include <asm/tcm.h>
进入 itcm 的函数可以这样标记:int __tcmfunc foo(int bar);
由于这些函数被标记为 long_calls,并且您可能希望在 TCM 内部本地调用函数而不会浪费空间,因此还有 __tcmlocalfunc 前缀,它将使调用相对。
进入 dtcm 的变量可以这样标记
int __tcmdata foo;
常量可以这样标记
int __tcmconst foo;
要将汇编程序放入 TCM 中,只需使用
.section ".tcm.text" or .section ".tcm.data"
分别。
示例代码
#include <asm/tcm.h>
/* Uninitialized data */
static u32 __tcmdata tcmvar;
/* Initialized data */
static u32 __tcmdata tcmassigned = 0x2BADBABEU;
/* Constant */
static const u32 __tcmconst tcmconst = 0xCAFEBABEU;
static void __tcmlocalfunc tcm_to_tcm(void)
{
int i;
for (i = 0; i < 100; i++)
tcmvar ++;
}
static void __tcmfunc hello_tcm(void)
{
/* Some abstract code that runs in ITCM */
int i;
for (i = 0; i < 100; i++) {
tcmvar ++;
}
tcm_to_tcm();
}
static void __init test_tcm(void)
{
u32 *tcmem;
int i;
hello_tcm();
printk("Hello TCM executed from ITCM RAM\n");
printk("TCM variable from testrun: %u @ %p\n", tcmvar, &tcmvar);
tcmvar = 0xDEADBEEFU;
printk("TCM variable: 0x%x @ %p\n", tcmvar, &tcmvar);
printk("TCM assigned variable: 0x%x @ %p\n", tcmassigned, &tcmassigned);
printk("TCM constant: 0x%x @ %p\n", tcmconst, &tcmconst);
/* Allocate some TCM memory from the pool */
tcmem = tcm_alloc(20);
if (tcmem) {
printk("TCM Allocated 20 bytes of TCM @ %p\n", tcmem);
tcmem[0] = 0xDEADBEEFU;
tcmem[1] = 0x2BADBABEU;
tcmem[2] = 0xCAFEBABEU;
tcmem[3] = 0xDEADBEEFU;
tcmem[4] = 0x2BADBABEU;
for (i = 0; i < 5; i++)
printk("TCM tcmem[%d] = %08x\n", i, tcmem[i]);
tcm_free(tcmem, 20);
}
}