引き続き、ARM TCM (Tightly-Coupled Memory) handling in Linuxも読んでいく。
もともと、Linux Kernelのソースコードの一部なので、GPLv2扱いになる(はずの認識)。
https://www.kernel.org/doc/html/latest/index.html
Licensing documentation
The following describes the license of the Linux kernel source code (GPLv2), how to properly mark the license of individual files in the source tree, as well as links to the full license text.
https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-licensing
ARM TCM (Tightly-Coupled Memory) handling in Linux
Written by Linus Walleij linus.walleij@stericsson.com
Some ARM SoCs have a so-called TCM (Tightly-Coupled Memory). This is usually just a few (4-64) KiB of RAM inside the ARM processor.
ARM SoCの中には、TCM(Tightly-Coupled Memory 密結合メモリ)と呼ばれるものを持つものもある。TCMはARM processor内の、通常非常に小さい(4-64) KiBサイズのRAMである。
Due to being embedded inside the CPU, the TCM has a Harvard-architecture, so there is an ITCM (instruction TCM) and a DTCM (data TCM). The DTCM can not contain any instructions, but the ITCM can actually contain data. The size of DTCM or ITCM is minimum 4KiB so the typical minimum configuration is 4KiB ITCM and 4KiB DTCM.
CPU内部に組み込まれているために、TCMはハーバードアーキテクチャを持っています。そのため、ITCM(instruction TCM)と、DTCM(data TCM)があります。 DTCMは命令を含むことができませんが、ITCMは実際にデータを含むこともできます。DTCMとITCMのサイズは、最小4KiBなので、一般的な最小構成は、4KiB ITCMと4Kib DTCMです。
ARM CPUs have special registers to read out status, physical location and size of TCM memories. arch/arm/include/asm/cputype.h defines a CPUID_TCM register that you can read out from the system control coprocessor. Documentation from ARM can be found at http://infocenter.arm.com, search for “TCM Status Register” to see documents for all CPUs. Reading this register you can determine if ITCM (bits 1-0) and/or DTCM (bit 17-16) is present in the machine.
ARM CPUはTCM memoriyの位置やサイズに関する、状態を読み出す特殊なレジスタを有している。 arch/arm/include/asm/cputype.hでは、system control coprocessorから読み出すためのCPUID_TCM registerが定義されている。
ARM社からのドキュメントは、http://infocenter.arm.com から見つける事ができ、 “TCM Status Register” はすべてのCPUに対するドキュメントで見つけられる。
このレジスタを読み出す事で、machineにおいてITCM(bits 1-0) あるいは/または DTCM(bit 17-16)が有効であるかどうかを判断する事ができる。
There is further a TCM region register (search for “TCM Region Registers” at the ARM site) that can report and modify the location size of TCM memories at runtime. This is used to read out and modify TCM location and size. Notice that this is not a MMU table: you actually move the physical location of the TCM around. At the place you put it, it will mask any underlying RAM from the CPU so it is usually wise not to overlap any physical RAM with the TCM.
さらに、実行時にTCM memoryの場所/サイズを報告したり変更したりできるTCM region registerがある(ARM社サイトで、"TCM Region Registers"で検索)。
これは、TCMの場所とサイズを読み取って変更するために用いられる。これがMMU tableではないことに注意してください。実際には、TCM周辺の物理的な位置を移動させます。配置した場所では、それはCPUからのRAMがマスクされるため、通常は物理RAMとTCMを重複させないことが賢明です。
The TCM memory can then be remapped to another address again using the MMU, but notice that the TCM if often used in situations where the MMU is turned off. To avoid confusion the current Linux implementation will map the TCM 1 to 1 from physical to virtual memory in the location specified by the kernel. Currently Linux will map ITCM to 0xfffe0000 and on, and DTCM to 0xfffe8000 and on, supporting a maximum of 32KiB of ITCM and 32KiB of DTCM.
TCM memoryは、MMUを再度利用する事で別のアドレスにremappingすることもできます。しかし、MMUが無効化されているシチュエーションでもTCMが利用されることをに注意してください。この混乱を避けるために、現時点のLinux実装では、TCMは物理アドレスと仮想アドレスを1:1でマッピングします。現在のLinuxでは、ITCMが0xfff3000、DTCMが0xfffe8000にマッピングされ、32KiB ITCMと32KiBのDTCMが最大サイズになります。
Newer versions of the region registers also support dividing these TCMs in two separate banks, so for example an 8KiB ITCM is divided into two 4KiB banks with its own control registers. The idea is to be able to lock and hide one of the banks for use by the secure world (TrustZone).
region registerの新しいバージョンでは、TCMを2つのバンクに分割する機能も有している。例えばば、8KiBのITCMをコントロールレジスタによって4KiBのバングに分割できる。この考え方は、セキュアワールド(TrustZone)において、バンクを参照可能にするあるいは隠ぺいできるようにする。
TCM is used for a few things:
・FIQ and other interrupt handlers that need deterministic timing and cannot wait for cache misses.
・Idle loops where all external RAM is set to self-refresh retention mode, so only on-chip RAM is accessible by the CPU and then we hang inside ITCM waiting for an interrupt.
・Other operations which implies shutting off or reconfiguring the external RAM controller.
TCMはいくつかの目的で用いられる。
- 決定的なタイミングやキャッシュミスを待つことのできないような、FIQやほかの割り込みハンドラー
- 外部RAMがセルフリフレッシュretaion modeにセットされているようなIdle loop。しかたって、CPUはon chip RAMだけアクセス可能であり、ICTMの中で割り込みを待ちます。
- 外部RAMコントローラーが、終了しているあるいは再設定するための何らかの処理。
There is an interface for using TCM on the ARM architecture in . Using this interface it is possible to:
・Define the physical address and size of ITCM and DTCM.
・Tag functions to be compiled into ITCM.
・Tag data and constants to be allocated to DTCM and ITCM.
・Have the remaining TCM RAM added to a special allocation pool with gen_pool_create() and gen_pool_add() and provice tcm_alloc() and tcm_free() for this memory. Such a heap is great for things like saving device state when shutting off device power domains.
ARM architectureにおけるTCMを利用するためのインターフェイスは、に記載されている。このインタフェイスを用いることで以下のことが可能になります。
- ITCMとDTCMの物理的なアドレスと位置の定義。
- ITCMにコンパイルするfunctionのためのTag
- DTCMとITCMに配置するデータと定義のためのTag。
- gen_pool_create()とgen_pool_add()を用いて、特殊なallocation poolを残りのTCM RAMに割り当てる。このようなヒープは、デバイスの電源ドメインをシャットダウンするときにデバイスの状態を保持するのに適している。
A machine that has TCM memory shall select HAVE_TCM from arch/arm/Kconfig for itself. Code that needs to use TCM shall #include
MachineがTCM mamoryを有している場合には、自ら、arch/arm/KconfigでHAVE_TCMを選択する必要がある。TCMを使おうとするソースコードは、$include を利用する。
Functions to go into itcm can be tagged like this:
itcm上に実装したい関数は、このようなtag付けをする。
int __tcmfunc foo(int bar);
Since these are marked to become long_calls and you may want to have functions called locally inside the TCM without wasting space, there is also the __tcmlocalfunc prefix that will make the call relative.
これらは、long_callcになるようにマークされている。スペースを無駄にせずTCM内部で関数を呼び出すために、呼び出しを相対的にする__tcmlocalfunc prefixもある、
Variables to go into dtcm can be tagged like this:
DTCM内に定義したい変数は、このようなtag付けをする。
int __tcmdata foo;
Constants can be tagged like this:
定数は、このようなtag付けをする。
int __tcmconst foo;
To put assembler into TCM just use:
TCMにアセンブラを置きたい場合には以下のようにする。
.section ".tcm.text" or .section ".tcm.data"
respectively.
以上です。
Example code:
# 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);
}
}