PSoC 6 のデュアルコアでＬチカ (2)

これは、[PSoC Advent Calendar 2017]の2日目に突っ込まれた記事です。

##前回のあらすじ
[前回の記事][(1)]では、ふたつのCPUでかわりばんこにLow/Highを出力させることで、Ｌチカを実現しました。しかし、これらのCPUは互いに同期しているわけではないので、タイミングがずれるときれいなＬチカになりません。
今回は、セマフォという仕組みを使ってふたつのCPUを同期させます。

##セマフォって、何じゃろうか
セマフォについて、ここでは詳しく解説しません。セマフォという「もの」が一つだけ存在し、一方のCPUが獲得したら他方は獲得できなくなるという仕組みで働きます。

##ここで実現したいこと
このプロジェクトでは、「500m秒のあいだセマフォを獲得し、その間だけLEDを点灯する。」というプログラムをふたつのCPUに実行させます。すると、

1. CPU1がセマフォを獲得しLED1を点灯させる。
2. CPU1が500m秒の間、セマフォを保持し続ける。
3. CPU1がLED1を消灯しセマフォを解放する。
4. CPU2が解放されたセマフォを獲得しLED2を点灯させる。
5. CPU2が500m秒の間、セマフォを保持し続ける。
6. CPU2がLED2を消灯しセマフォを解放する。
7. 1に戻る

以上のように、ふたつのCPUがかわりばんこにセマフォを獲得し、LED1/LED2が点滅をするという計画です。

##**Cortex-M0+**のプログラム

```c:main_cm0p.c
#include "project.h"

#define SEMAPHORE_LED  (8)  /* セマフォの識別番号 */

int main(void)
{
    __enable_irq(); /* 全体の割り込みを許可する */

    /* Cortex-M4 を叩き起こして CY_CORTEX_M4_APPL_ADDR から実行させる。 */
    Cy_SysEnableCM4(CY_CORTEX_M4_APPL_ADDR); 

    for(;;)
    {
        /* セマフォを獲得する */
        while (
            Cy_IPC_Sema_Set(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
        /* GPIO 出力をクリア (Low) する。 */
        Cy_GPIO_Clr(Pin_LEDR_PORT, Pin_LEDR_NUM);
        /* 500m秒待つ */
        CyDelay(500);
        /* GPIO 出力をセット (High) する。 */
        Cy_GPIO_Set(Pin_LEDR_PORT, Pin_LEDR_NUM);
        /* セマフォを開放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
    }
}
```

セマフォは、`main()` 関数に制御が渡る前に `SystemInit()`関数によって初期化されて、全部で128個のセマフォが作成されます。このうち、0番から7番まではシステムで予約されています。このプログラムでは、8番だけを使用します。

無限ループでは、先のステップ通りにセマフォの獲得(Cy_IPC_Sema_Set)および解放(Cy_IPC_Sema_Clear)とLEDのON/OFF制御を行っています。

```c:main_m4.c
#include "project.h"

#define SEMAPHORE_LED  (8)  /* セマフォの識別番号 */

int main(void)
{
    __enable_irq(); /* 全体の割り込みを許可する */

    for(;;)
    {
        /* セマフォを獲得する */
        while (
            Cy_IPC_Sema_Set(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
        /* GPIO 出力をクリア (Low) する。 */
        Cy_GPIO_Clr(Pin_LEDG_PORT, Pin_LEDG_NUM);
        /* 500m秒待つ */
        CyDelay(500);
        /* GPIO 出力をセット (High) する。 */
        Cy_GPIO_Set(Pin_LEDG_PORT, Pin_LEDG_NUM);
        /* セマフォを開放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
    }
}
```

**Cortex-M4**側でも `main()` に制御が渡る前に初期化されます。ふたつのCPUが同じセマフォにアクセスするために識別番号 `SEMAPHORE_LED` が定義されています。

無限ループでの振る舞いは、操作するLEDが異なるのを除いて、**Cortex-M0+**と同じです。

##うまくいかなかった
プロジェクトを作成してＬチカさせたところ、ふたつのLEDが交互に光るのを期待したのに、点灯時間が異なっているようです。オシロスコープでみた所、このようになっていました。

![NewFile86.png](https://qiita-image-store.s3.amazonaws.com/0/224737/7c2888ac-32a2-44be-df61-0e30ff192391.png)

上(CH1)が**Cortex-M0+**側の波形で、下(CH2)が**Cortex-M4**側の波形です。どうやら**Cortex-M4**側がセマフォを獲ったまま解放していないように見えます。そこで、セマフォが解放された瞬間を詳しく見てみました。

![NewFile90.png](https://qiita-image-store.s3.amazonaws.com/0/224737/827796c9-2141-b114-b006-c4189b6260e2.png)

先ほどの波形を見る限りでは、セマフォが解放されていないように見えました。しかし、解放の様子を詳しく見ると、**Cortex-M4**により解放されたセマフォが、ふたたび**Cortex-M4**によって獲得されているのがわかります。再獲得するまでの時間は、約3&mu;秒です。

![NewFile89.png](https://qiita-image-store.s3.amazonaws.com/0/224737/96faecbb-2d1c-24a6-c6f2-f104388f4bb2.png)

一方、**Cortex-M0+**がセマフォを獲得できた場合を見ると、約4&mu;秒かかっています。これだけの時間がかかっていたら、**Cortex-M4**に先を越されてしまいます。
つまり、**Cortex-M0+**がセマフォを獲得するスキが無かったのが原因のようです。

##**Cortex-M0+**にゆずってあげる
色々な解決方法が考えられますが、手っ取り早いのが「**Cortex-M0+**がセマフォを獲得するスキを作る」ことです。`main_cm4.c`の無限ループの終わりの方にこのようにスキを作りました。

```c:main_cm4.c
        /* セマフォを解放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
        /* Cortex-M0+ にチャンスを与える */
        CyDelayUs(3);
    }
}
```

これで、毎回**Cortex-M0+**がセマフォを獲得することができるようになります。変更後は、50%デューティーの波形が観測されました。

![NewFile92.png](https://qiita-image-store.s3.amazonaws.com/0/224737/28cc498e-55c1-78eb-4c59-4d81e6a0d0c5.png)

**Cortex-M4**がセマフォを開放した後の様子を見ると、**Cortex-M0+**がセマフォを獲得するまでに約5.6&mu;秒を要しています。この時間の長さが**Cortex-M4**にセマフォを横取りされてしまった原因と考えられます。

![NewFile93.png](https://qiita-image-store.s3.amazonaws.com/0/224737/36f54c6f-f310-6488-ccb4-fd98c4485df3.png)

##こんな目分量でいいの？

とはいえ、この記事で「スキ」を作るために待ち合わせた3&mu;秒が適切かどうかはわかりません。もっと確実に動作させる方法は、無いのでしょうか？


##関連文献
[AN215656 – PSoC 6 MCU Dual-Core CPU System Design][AN215656]
[CE216795 - PSoC(R) 6 MCU Dual-Core Basics][CE216795]

##関連記事
[PSoC 6 のデュアルコアでＬチカ (1)][(1)]

[PSoC Advent Calendar 2017]:https://qiita.com/advent-calendar/2017/psoc
[PSoC 6]:http://www.cypress.com/psoc6
[CY8CKIT-062-BLE]:http://www.cypress.com/cy8ckit-062-ble
[AN215656]:https://www.cypress.com/an215656
[CE216795]:https://www.cypress.com/ce216795
[(1)]:https://qiita.com/noritan_org/items/d08f4578a91d63b93c06