ステップバイステップ！リアルタイムOSを評価ボードへ移植 ～ TOPPERS/ASP3をNXP FRDM-MCXN947へ～ 第2回

## 本記事について 

リアルタイムOSを市販のマイコン評価ボードに移植する手順をステップバイステップでポイント毎に動作を確認しながら説明します。移植するリアルタイムOSはTOPPERS/ASP3です。 

下記構成で複数回に分けての投稿を予定しています。 

:::note 
本記事の投稿予定

[第0回 本記事の趣旨、TOPPERS/ASP3と評価ボードの説明](https://qiita.com/Debuggy_24/items/0b42d50c4744238bb3a1)

[第1回 準備　ハードウェアの入手、ツールのインストールなど](https://qiita.com/Debuggy_24/items/e6a8c788979df97b4b67)

第2回 とりあえず動くものを移植 

第3回 細部の作り込み1	クロックの設定 

第4回 細部の作り込み2	UARTドライバ 

[第5回 細部の作り込み3 タイマドライバ (ティックレス対応)](https://qiita.com/Debuggy_24/items/d80dfc71ce9398b1466c) 
:::
 
今回(第2回)はとりあえず動くものを移植して、実機でTOPPRS/ASP3を動作させます。 

 

## とりあえず動くもの 

TOPPERSプロジェクトで公開しているLPC55S69SEVK向けの[TOPPERS/ASP3簡易パッケージ](https://www.toppers.jp/asp3-e-download.html#lpc) を元に下記のように編集しました。


- 名前の付け替え：コード、ファイル名のLPCxxxxをMCXNx4xへ変更
- 名前の付け替え：コード、ファイル名のボード名をLPC55S69EVKをfrdm_mcxn947へ変更
- CPUクロックを仮設定
- UARTドライバを仮実装
- タイマーとしてCortex-M内蔵のSysticを使う
- アプリケーションLED 1秒点滅を実装
- メモリ配置 (リンカスクリプト)
- マクロの定義
- Makefileの編集

名前の付け替えについては、後述するソースコードを参照してください。
それ以外の項目について説明していきます。

:::note 
本記事向けのソースコード一式は[こちら](https://github.com/jun1-hirata/toppers_asp3_frdm_mcxn947_step_by_step_porting/releases/tag/part2-package)からダウンロードできます。

![source_release_github_part2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/f3d5b33b-01f6-1c79-80c9-2173ebbf58c1.png)

:::




## CPUクロックを仮設定

LPC55S69EVK向けのコードではtarget_kernel_impl.cに実装されているhardware_init_hook関数にCPUクロックの設定を行っています。
LPC55S69向けのクロック設定なので、一度この関数のコードを全て削除することで、MCU起動時点でのクロック(48MHz)で動作させます。
MCXN947は150MHzで動作可能ですが、これは次回(第3回)で設定していきます。

```target_kernel_impl.c
/*
 * 起動時のハードウェア初期化処理
 */
void hardware_init_hook(void)
{
}

```

<b>※補足</b>
hardware_init_hookについての説明は、
asp3/doc/porting.txtの「6.8 カーネルの起動・終了」の項にあります。
```
(b) hardware_init_hookを呼び出す

システムのリセット後すぐに行う必要のあるターゲットシステム依存の初期化
処理を行うために，hardware_init_hookを呼び出す．

hardware_init_hookは，ターゲット依存部で用意するのが標準であるが，シス
テムのリセット後すぐに行う必要のある初期化処理を追加するために，アプリ
ケーションで用意したものを用いる場合もある．
```


frdm_mcxn947.hで定義されているマクロCPU_CLOCK_HZで周波数を設定します。
```
/*
 *  コアのクロック周波数
 */
#define CPU_CLOCK_HZ    48000000

```


## UARTドライバを仮実装

MCXN947に内蔵されているUART向けのドライバを実装するのですが、
今回は仮実装ということで、下記の機能をもつスタブとして実装します。

- 出力 - 何もしない (UARTへ出力しない)
- 入力 - 文字aを入力したことにする

次々回(第4回)でMCX947向けのコードを実装します。

asp3/arch/arm_m_gcc/MCXNx4x/tUsart.cファイルのコードを編集します。

![uart_file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/a07fb56f-bcea-be22-0caf-5292fc22dbce.png)


(1) このファイルの全ての関数の中身を全て削除 (空の関数にする)

```
/*
 * シリアルI/Oポートのオープン
 */
void eSIOPort_open(CELLIDX idx)
{
}
```

(2) 関数eSIOPort_putCharの戻り値をtrueとすることで出力したことにする

```
/*
 * シリアルI/Oポートへの文字送信
 */
bool_t eSIOPort_putChar(CELLIDX idx, char c)
{
    return true; // 送ったことにしてtrueを返す
}
```

(3) 関数eSIOPort_getCharの戻り値を'a'とすることで'a'を受信したことにする

```
/*
 * シリアルI/Oポートからの文字受信
 */
int_t eSIOPort_getChar(CELLIDX idx)
{
    return 'a'; // 仮実装 'a'が取得できたことにしておく
}
```


また、UART出力を少なくするためにシステムログの出力を抑制します。

asp3/target/frdm_mcxn947_gcc/Makefile.targetのコンパイラオプションで
マクロTOPPERS_OMIT_SYSLOGを追加するため、CDEFSの定義に
-DTOPPERS_OMIT_SYSLOGを追加します。

![compiler_option.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/c2092beb-061f-c373-b2ee-451700930786.png)



## タイマーの仮実装

TOPPERS/ASP3ではCortex-M内蔵のSystickタイマーを使用するコードが実装されています。お手軽に使えるので、今回はこれを有効にしてタイマーとします。

asp3/target/frdm_mcxn947_gcc/Makefile.targetのKERNEL_TIMERの定義を編集してください。
![KERNEL_TIMER.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/fa3f8833-5131-740a-736d-10a92332f83a.png)



:::note warn
[注意]　
Systickを使用した実装ではTOPPERS/APS3の「ティックレスの高分解能時間管理」は使用できません。Systickを使用しない実装(ティックレスへの対応)については第5回で説明します。
:::



## メモリ配置 (リンカスクリプト)


GNUのツールチェインではプログラムのメモリ配置を設定するためにリンカスクリプトを使用しています。リンカスクリプトを用意します。

[第1回](https://qiita.com/Debuggy_24/items/e6a8c788979df97b4b67)で説明したNXP社のサンプルプログラム(led_blinky)のリンカスクリプトを参考にします。サンプルプログラムビルド後に生成されるDebug/frdmmcxn947_led_blinky_Debug.ldがリンカスクリプトです。

![ld1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/3514677d-e3c3-e11c-cf84-51df6fca222c.png)




サンプルプログラムから下記に挙げる点を変更します。編集後のリンカスクリプトはasp3/target/frdm_mcxn947.ldへ置いています。

- 割り込みベクタの配置するセクション名を「.isr_vector」から「.vector」へ変更
![ld2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/1dc96229-196d-55a7-a2a3-a7ec5c191100.png)


## マクロの定義


### TBITW_IPRI (割込み優先度のビット幅)


Cortex-Mが実装されたSoCによって割り込み優先度のビット数は異なるので、
データシートやリファレンスマニュアルなどを参照して実装されているビット数を調べます。
MCUXN947の場合は下記のように8段階(3ビット)となっています。

MCX Nx4x Reference Manual Rev5, 06/2024より引用
![NVIC_MCXN947.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/2a8e2263-1d08-e40d-08e1-ce4b4988c72e.png)



asp3/arch/arm_m_gcc/MCXNx4x/chip_sil.hに定義されている値を編集します。
```
/*
 * 割込み優先度のビット幅(core_sil.hで参照するためここで定義)
 */
#define TBITW_IPRI 3
```

### TMIN_INTPRI (割込み優先度の最小値) 


asp3/arch/arm_m_gcc/MCXNx4x/chip_kernel.hに定義されている値を編集します。
LPC55S69と同じく割り込み優先度のビット数が3ビットなので変更していません。
```
/*
 * カーネル管理の割込み優先度の範囲
 *
 * TMIN_INTPRIの定義を変更することで，このレベルよりも高い割込み優先度
 * を持つものをカーネル管理外の割込みとするかを変更できる．
 */
#define TMIN_INTPRI (-7) /* 割込み優先度の最小値（最高値）*/
```

### TMAX_INTNO

MCUに実装されている割り込みの個数を入力します。

asp3/target/frdm_mcxn947_gcc/frdm_mcxn947.hで定義しています。

```
/*
 *  割込み数
 */
#define TMAX_INTNO (155 + 16)
```




## Makefileの編集

MCU起動直後はTrustZoneのセキュアワールドで動作しているので、TrustZoneありでコンパイルします。asp3/target/Makefile.targetのを編集します。
```
#
# TrustZoneを使用するか
#
ENABLE_TRUSTZONE = 1

```



## アプリケーションLED一秒点滅を実装

特にオプションを指定しないデフォルトのビルドではアプリケーションはsample1(asp3/sample/sample1.c)となります。sample1はUART入出力を前提としたつくりとなっているので、UARTを使用できない今回の動作確認には使用できません。そのため、sample1の最初期にLED点滅を行うコードを実装することで、動作確認を行います。

sample1.cの関数main_taskの冒頭でLEDを初期化(led_init())、その後、1秒毎(dly_tsk())にLEDのON/OFFを切り替える(led_toggle())ようにしています。while(1)で無限ループするので、sample1に実装されているUART入力のコードは実行されません。

```
/*
 *  メインタスク
 */
void
main_task(EXINF exinf)
{
	char	c;
	ID		tskid = TASK1;
	int_t	tskno = 1;
	ER_UINT	ercd;
	PRI		tskpri;
#ifndef TASK_LOOP
	SYSTIM	stime1, stime2;
#endif /* TASK_LOOP */
	HRTCNT	hrtcnt1, hrtcnt2;

	{

	    void led_init(void);
	    void led_toggle(void);
	    
	    led_init();
	    while(1) {
	        dly_tsk(1000*1000);
	        led_toggle();
	    }
	}

```


LED(GREEN)はP0_27のポートに接続しているので、初期化関数led_initで初期化、led_toggleでON/OFFを切り替えます。

```
#include "MCXNx4x.h"
#include "sil.h"

#define MCXNx4x_GPIO0_BASE (0x40096000)
#define MCXNx4x_GPIO1_BASE (0x40098000)
#define PSOR               (0x44)       /* Port Set Output (PSOR)     */
#define PCOR               (0x48)       /* Port Clear Output (PCOR)   */
#define PDDR               (0x54)       /* Port Data Direction (PDDR) */

/* LED
 *   Red    P0_10
 *   Green  P0_27
 *   Blue   P1_2
 */

void led_init(void)
{
    uint32_t val32;
    /* CLOCK enable GPIO0, GPIO1, PORT0, PORT1 */
    /*  GPIO0 : bit 19 */
    /*  GPIO1 : bit 20 */
    /*  PORT0 : bit 13 */
    /*  PORT1 : bit 14 */
    val32 = (1 << 19)
            | (1 << 20)
            | (1 << 13)
            | (1 << 14)
            ;
    sil_wrw_mem(MCXNx4x_SYSCON_AHBCLKCTRLSET0, val32);
    
    /* Set port direction output */
    /*  Red, Green */
    //sil_orw((uint32_t *)(MCXNx4x_GPIO0_BASE + PDDR), (1 << 10));
    sil_orw((uint32_t *)(MCXNx4x_GPIO0_BASE + PDDR), (1 << 27));

    /*  Blue */
    //sil_orw((uint32_t *)(MCXNx4x_GPIO1_BASE + PDDR), (1 << 2));
}

int led_stat = 0;

void led_toggle(void)
{
    if (led_stat == 0) {
        led_stat = 1;
        sil_wrw_mem((uint32_t *)(MCXNx4x_GPIO0_BASE + PSOR), (1 << 27));
    } else {
        led_stat = 0;
        sil_wrw_mem((uint32_t *)(MCXNx4x_GPIO0_BASE + PCOR), (1 << 27));
    }
}

```



## ビルド

 
(1)下記のフォルダ構成でビルドします。コマンドプロンプトを起動してbuildフォルダへ移動(cd)してください。
![build1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/98e9c4fc-04b7-ff87-6872-c7028c554f68.png)


(2)ビルドに使用するmakeやツールチェーンへパスを通します。MCUXpresso IDEをインストールしたフォルダにある「MCUXpressoPath.cmd」をコマンドプロンプトで実行します。
![build2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/e92badc1-86bc-4363-822a-f05f58c15a59.png)
![build3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/7a5321cb-a00c-efb8-b9d6-6459aff868a7.png)


(3)「ruby ..\asp3\configure.rb -T frdm_mcxn947_gcc」を実行
(4)「make」を実行

ビルドが正常に終了すると、ファイル「asp」が生成されます。
このファイルは実行時に使用します。
![build4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/4269a27f-098f-f1ed-616a-87c83258cb9d.png)




## 実行

次の手順で実行します。

(1)MCUXpresso IDEを開き、前回(第1回)で実行したNXP社のサンプルプログラムをビルド、実行したワークスペースを開きます。

(2)「Run」->「Debug Configurations」

![run1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/2183afe8-9192-a0d3-6efc-6e687c53868f.png)

(3)frdmmcxn947_led_blink...を右クリック後、Duplicateしてコンフィグレーションをコピー
![run2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/273b6d56-e04c-e1e1-e372-bce5055211cc.png)



(4)C/C++アプリケーションにビルドで生成したファイル(asp3)をフルパスで入力後、Debugを押すとデバッグを開始します。
![run3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817840/5f530a8a-14fa-cde9-da7f-8c0d6a2dbee5.png)


正常に動作していれば、1秒間隔でLED(ボードのD2)が点滅します。


以上で「第2回 とりあえず動くものを移植」を説明し、実機上でTOPPERS/ASP3を動作させました。次回以降に細部の作り込みを行います。

「第3回 細部の作り込み1	クロックの設定 」へ続きます。