Arm Coreのおさらい

# はじめに

本記事はARM cortex MシリーズのCPUについての情報を記載するものである。本人用メモなので誤っていたりする場合がある。
参照性が良いようにスクリーンショットを多様しているが、参照先が更新されている可能性があるので、参照先も確認すること。

# Arm cortex Mシリーズとは

32bit RISC CPUで、MCU相当のCPUである。
シリーズはCortexMxシリーズとあり、最新はCortexM33、M23である(Armv8-Mアーキテクチャ)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/7cd35912-2d21-4dd1-5dad-a167922210bc.png)


# CPUとArchitecture

CPUとArchitectureは以下記事が詳細

ARMv8のM33からTrustZoneに対応したことが分かる。

https://evt1.com/docs/armv6-m/

アーキテクチャマニュアルはここからダウンロードする

https://developer.arm.com/documentation/ddi0553/latest/

# CPUレジスタ

* 全体像
https://qiita.com/tom_S/items/52e4afdb379dff2cf18a

* CPUレジスタ
https://developer.arm.com/documentation/107656/0101/Registers/Registers-in-the-register-bank

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/7ba00cd5-bc95-4975-8171-eeee2363c881.png)

[引用](https://developer.arm.com/documentation/107656/0101/Registers)

## R0 - R12 register

レジスタ R0 から R12 は汎用レジスタです。最初の 8 つ (R0-R7) は、ロー レジスタとも呼ばれます。命令オペコードのビット数が限られているため、多くの 16 ビット命令はロー レジスタにしかアクセスできません。ハイ レジスタ (R8-R12) は、32 ビット命令や、命令などの一部の 16 ビット命令で使用できます。


## R13, SP

R13 はスタック ポインタ (SP) です。これは、命令PUSHなどでスタック メモリにアクセスするために使用されますPOP。プロセッサが新しいデータをスタックにプッシュすると、スタック ポインタが減分されてから、メモリの場所にデータが書き込まれます。物理的には、2 つまたは 4 つの異なるスタック ポインタが存在する可能性があります。

セキュリティ拡張機能が実装されていない場合は、スタック ポインターが 2 つあります。

メインスタックポインタ（一般にMSPまたはSP_Mainと呼ばれる）
MSP はリセット時に使用されるデフォルトのスタック ポインターであり、すべての例外処理に使用されます。
プロセス スタック ポインタ (PSP または SP_Process とも呼ばれる)
PSP は、スレッド モードでのみ使用できる代替スタック ポインターであり、通常はオペレーティング システム (OS) のアプリケーション タスクに使用されます。
スタック ポインターの選択は、CONTROL と呼ばれる特殊レジスターによって決定されます。CONTROL レジスターの SPSEL フィールドは、次のようにスレッド モード スタック操作のスタック ポインターを選択するようにプログラムできます。

MSP を選択するには、SPSEL を 0 に設定します。
PSPを選択するには、SPSELを1に設定します。


## R14、リンクレジスタ(LR)

R14 はリンク レジスタ (LR) とも呼ばれます。これは関数またはサブルーチンを呼び出すときに戻りアドレスを保持します。

関数またはサブルーチンの終了時に、呼び出し先関数は呼び出し元関数に戻り、LR の値をプログラム カウンター (PC) にロードすることで処理を再開できます。関数またはサブルーチンの呼び出しが行われると、LR の値が自動的に更新されます。

呼び出し先関数が別の関数を呼び出す必要がある場合、LR の値をスタックに保存する必要があります。そうしないと、関数呼び出しが行われたときに LR の現在の値が失われます。


## R15、プログラムカウンタ(PC)

R15 はプログラム カウンター (PC) です。読み取りと書き込みが可能です。読み取りでは現在の命令アドレス + 4 が返され、PC への書き込み (たとえば、データ処理命令の使用) では分岐操作が発生します。

すべての命令はハーフワードまたはワード アドレスに揃える必要があるため、PC の最下位ビット (LSB) は常にゼロです。ただし、分岐命令とメモリ読み取り命令を使用して PC を更新する場合は、Thumb 状態を示すために新しい PC 値の LSB を 1 に設定する必要があります。PC の LSB が 1 に設定されていない場合は、UsageFault または HardFault 障害例外が発生します。

# 特殊目的レジスタ

## プログラムステータスレジスタ

プログラム ステータス レジスタは 32 ビットのレジスタであり、次のように分割されます。

### アプリケーション プログラム ステータス レジスタ (APSR)
条件分岐や、キャリー付き減算などの特殊なフラグを必要とする命令操作に必要なさまざまな ALU フラグが含まれています。
### 割り込みプログラムステータスレジスタ (IPSR)
現在の割り込みまたは例外の状態情報が含まれます。
### 実行プログラム ステータス レジスタ (EPSR)
実行状態情報が含まれます。EPSR には、If-Then (IT) 命令の状態ビット (T) と実行状態ビット、または中断されたロード複数命令またはストア複数命令の割り込み可能継続可能命令 (ICI) フィールドが含まれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/4ccec5a9-f0bb-fde5-2583-3d006423a2b1.png)

## 例外マスクレジスタ

Armv8-M アーキテクチャでは、次の 3 種類の例外および割り込みマスク レジスタを使用できます。

* 1ビット例外マスクレジスタ、PRIMASK
* 1ビットの障害マスクレジスタ、FAULTMASK
* 8ビットの基本優先度マスクレジスタ、BASEPRI

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/5429c0b2-8a32-b795-c0f6-c1755ec4ebf7.png)


各例外 (割り込みを含む) には優先度レベルがあります。数値が小さいほど優先度が高く、数値が大きいほど優先度が低くなります。これらの特殊目的のマスク レジスタは、優先度レベルに基づいて例外をマスクするために使用されます。これらのマスク レジスタは、プロセッサ内の例外処理の優先度ブースト方式で重要な役割を果たします。これらのレジスタの使用例としては、タイミングが重要なタスクに影響を与える可能性がある場合に例外を無効にすることが挙げられます。

### PRIMASK
PRIMASK : PRIMASK レジスタは 1 ビットの割り込みマスク レジスタです。設定すると、マスク不可能割り込み (NMI) と HardFault 例外を除くすべての例外 (割り込みを含む) がブロックされます。これは、実質的に現在の例外の優先度レベルが 0 であることを意味します。0 は、プログラム可能な例外または割り込みの最高レベルであることに注意してください。
   ※(RXのIフラグ)

### FAULTMASK
FAULTMASK : PRIMASK レジスタと同様に、FAULTMASK レジスタも HardFault 例外を含むすべての例外をブロックします。これは、実質的に現在の例外の優先度レベルが -1 であることを意味します。

### BASEPRI
BASEPRI : 柔軟な割り込みマスクを可能にするために、Armv8-M アーキテクチャは、優先度レベルに基づいて例外と割り込みをマスクする BASEPRI レジスタを提供します。BASEPRI レジスタの幅は、実装されている優先度レベルの数によって異なります。BASEPRI が 0 に設定されている場合、無効であることを意味します。BASEPRI が 0 以外の値に設定されている場合、同じまたはより低い優先度レベルの例外はブロックされ、より高い優先度レベルの例外は許可されます。

これらレジスタ設定用のAPIがCMSIS core APIとして用意されている

|CMSIS-CORE関数名|使用法|
|--|--|
|__get_BASEPRI(void)|BASEPRIレジスターを読む|
|__get_PRIMASK (void)|PRIMASKレジスタの読み取り|
|__get_FAULTMASK (void)|FAULTMASKレジスタの読み取り|
|__set_BASEPRI(uint32_t basePri)|BASEPRIに新しい値を設定する|
|__set_BASEPRI_MAX(uint32_t basePri)|新しい値によってBASEPRI優先度レベルが上がる場合にのみ、指定された値をベース優先度レジスタに割り当てます。|
|__set_PRIMASK(uint32_t priMask)|PRIMASKに新しい値を設定する|
|__set_FAULTMASK(uint32_t faultMask)|FAULTMASKに新しい値を設定する|
|__disable_irq (void)|PRIMASKを設定して割り込みを無効にする|
|__enable_irq (void)|PRIMASKをクリアして割り込みを有効にする|


### 参考：CMSIS Core

https://arm-software.github.io/CMSIS_6/latest/General/index.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/0fd067bb-2346-7407-14b4-e5c9ba5fbd65.png)

# Controlレジスタ

ユーザスタックや特権モード等を切り替えるレジスタ
OSのKernelで切り替えたりチェックしたりするはず。

https://developer.arm.com/documentation/107656/0101/Registers/Special-purpose-registers/CONTROL-register

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/9e2c5053-3a2a-1c60-cd60-871dc5fc7b73.png)


# バリア命令

参照：https://www.aps-web.jp/academy/ca/227/

MCUはパイプラインの関係でメモリアクセスの順序が保証されない問題がある。

RX MCUではread , calc と読み出してその値を使用した演算をダミーで行う必要があった。
ARMコアではメモリアクセスが完了するまで待つ命令を用意している(3種類ある。)

|No|	命令名称	|アセンブリ命令|
|--|--|--|
|1	|データ同期バリア	|DSB|
|2	|データメモリバリア	|DMB|
|3	|命令同期バリア	|ISB|

### DMB命令の動作

DMB命令以前に存在するすべてのメモリアクセスが、先にアクセス完了することを保障します。メモリアクセス命令以外の「ADD r0,r1,r2」命令は実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/cec9e6e8-f555-574f-bbc8-aadf77732338.png)

### DSB命令の動作 ※これよく使う

DSB命令以前に存在するすべてのメモリアクセスが完了するまで、以後の命令を実行しないように、プロセッサを待機状態にします。メモリアクセス命令と「ADD r0,r1,r2」命令も実行しません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/b1ffc709-0bee-cab3-98ac-8c0066f738bc.png)

### ISB命令の動作 ※これも使った(使っていた条件は忘れた)

Armプロセッサのパイプラインがフラッシュ（破棄）されることを保障します。パイプラインステージの命令が破棄され、命令はメモリシステムから再度フェッチされます。

 ※RXでは無条件JumpしてCacheをクリアしていた。。

# NVIC(nested vectored interrupt controller)

https://developer.arm.com/documentation/107706/0100/Exceptions-and-interrupts-overview/Exception-types?lang=en

## 例外の種類

優先度マイナスはH/W割り込み。 OSを使う場合`PendSV`を覚えておく。

PendSVはコンテキストスイッチ用に使われるS/W割り込みみたいなもの。割り込みを抜けてからPendSV割り込みが発生するのでコンテキストスイッチに都合が良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/a2e389e2-1058-15bc-ca4a-96d75eaf1587.png)

## VTORレジスタ

https://developer.arm.com/documentation/107706/0100/Exceptions-and-interrupts-overview/Vector-table?lang=en

RXで言うINTBレジスタ。VTORレジスタの値の番地から相対で割り込みルーチンの飛び先を登録する。
VTORレジスタの初期値はベンダ実装依存。
VTORレジスタの値変更後と前でそれぞれテーブルを用意しておく必要がある。
(Resetがかかるときと移動後で参照する領域が異なるので)


# Arm coreのアセンブラはどうやって調べる？

Arm coreのアセンブラは上記に出てきたアーキテクチャマニュアルに記載されている

https://developer.arm.com/documentation/ddi0553/latest/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233064/5c734d40-f9b1-789c-d7a2-9b6a0c0c9717.png)

# ビットバンド

ARM特有？(RXでは無い)のメモリアクセス。

RAMやレジスタの各ビットがアドレス単位で指定できるエイリアス領域を持っており、そのエイリアスを変更することにより、オリジナルのビットが自動的に変更されるというものです。

メリット：RXではR/M/W中に割り込みが入る問題がある。ARMのビットバンドを使えば1命令なのでこの問題が発生しない。

https://www.aps-web.jp/academy/cm/256/

# TrustZone

CortexM33から搭載。もともとCortexAシリーズにあったものをシュリンクしたもの。

リンクはCortexAのもの。モニタがあるとかMPUがあるとか違う。(Aはあり、Mはなし)

https://www.aps-web.jp/academy/ca/231/

# データ長の表現

 - 32ビットデータ ： ワード
 - 16ビットデータ ： ハーフワード
 - 8ビットデータ　： バイト