チョコさんが以前に指摘されていたRL78コード生成機能によるCSI連続送信の終了の割り込みコードの問題を調べています

チョコさん、こんにちは。NoMaYです。

すみません、掲題の件、下記のスレッドでの話題ですが、コード生成されたソースの以下のコードのことでしょうか?

r_cg_serial_user.c

static void __near r_csi00_interrupt(void)
{
    if (g_csi00_tx_count > 0U)
    {
        SIO00 = *gp_csi00_tx_address;
        gp_csi00_tx_address++;
        g_csi00_tx_count--;
    }
    else
    {
        if ((SSR00 & _0040_SAU_UNDER_EXECUTE) == 0U)
        {
            r_csi00_callback_sendend();    /* complete send */
        }
        else
        {
            SMR00 &= ~_0001_SAU_BUFFER_EMPTY;
        }
    }
}

RL78/G14のEEPROMのI2C通信について
japan.renesasrulz.com/cafe_rene/f/002-2095199602/5962/rl78-g14-eeprom-i2c/33068#33068

チョコです。
鈴木さん,MAX7219をCSIで制御してLED表示を行っていて,問題を見つけました。
CSIの連続送信で最後のデータを書き込んだ後で,割り込みタイミングを書き換えてますが,転送速度とプログラムの実行速度の関係で,最後の割り込みが発生しない可能性があります。


RL78/G13 CSIのマスタ連続送受信でオーバーランエラーが発生する
japan.renesasrulz.com/cafe_rene/f/002-2095199602/5972/rl78-g13-csi/33137#33137

チョコです。
この現象については,「RL78/G14のEEPROMのI2C通信について」で少し触れていますが,次のような動作になっていると考えられます。
①最後のデータを書き込むための割り込みが発生します。
②CSIは最後のデータを送信します。
③CPUは割り込みを受け付けると,最後のデータの送信中かチェックして,最後のデータなら割り込みタイミングを転送完了に書き換えます。
ところが,③で割り込みタイミングを書き換える前に送信が完了してしまっていると,書き換えた時点では,送信完了割り込みの発生タイミングを終了しているので,期待した最後の割り込みが発生しません。
そのため,通信処理を終えることができなくなってしまいます。


なお、以下のCS+ V8.03の画面コピーは先程のソースを生成させた時のコード生成機能の設定内容です。



 

Parents
  • こんにちは。NoMaYです。

    対処方法を考えてみたのですが、まず、今回の割り込みコードの問題は、見え方として、以下の2つのタイプに見えるのかな、と思います。

    (1) CPU動作周波数(MHz)とCSIボーレート(Mbps)の比が4:1ぐらいの場合(例えば24MHz:6Mbpsとか32MHz:8Mbpsとか)には、コンパイラの種類とかバージョンとか最適化レベルとか(或いはコード生成されたコードを改造した内容とか)のバランス次第では、(常に)送信終了のコールバック関数呼び出しがスッポ抜ける(コールバック関数が呼び出されない)

    (2) 上記の(1)の場合よりCSIボーレートが低い場合でも、割り込み禁止区間(他の割り込みや明示的なDI/EIによるもの等)が絶妙なタイミングでくだんの割り込み要求の発生とオーバーラップしてしまった時には、送信終了のコールバック関数呼び出しがスッポ抜ける(コールバック関数が呼び出されない)

    RL78/G14 ユーザーズマニュアル ハードウェア編の記載は、以下の画面コピーの通りなのですが、文面からイメージする対処方法はこんな感じなのかな、と思います。ですが、せっかくコード生成されたコードにはユーザーズマニュアルに記載されていないアイデアが盛り込まれていますので、それを継承した対処方法を試してみました。(もっとも、製品に適用する場合には、ルネサスさんにNOPを挿入して時間稼ぎする必要がある箇所がないかどうか確認した方が良いと思いますが、、、)

    ユーザーズマニュアルの文面からイメージする対処方法:
    (1) ⇒ 諦める(連続送信モードを諦めてシングル送信モードを使う)
    (2) ⇒ 多重割り込みを使って何とかする or 諦める(連続送信モードを諦めてシングル送信モードを使う)

    RL78/G14 ユーザーズマニュアル ハードウェア編 画面コピー
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01uh0186
    r01uh0186jj0330-rl78g14.pdf



    コード生成されたコードのアイデアを継承した対処方法: (桃色文字の行はデバッグ用なのでデバッグ時以外は削除)

    r_cg_serial_user.c

    static void __near r_csi00_interrupt(void)
    {
        if (g_csi00_tx_count > 0U)
        {
            SIO00 = *gp_csi00_tx_address;
            gp_csi00_tx_address++;
            g_csi00_tx_count--;
        }
        else
        {

            SMR00 &= ~_0001_SAU_BUFFER_EMPTY;
            if ((SSR00 & _0040_SAU_UNDER_EXECUTE) == 0U)
            {
                CSIMK00 = 1U;    /* disable INTCSI00 interrupt */
                CSIIF00 = 0U;    /* clear INTCSI00 interrupt flag */
                r_csi00_callback_sendend();    /* complete send */
                LED1 = LED_ON;
            }
            else
            {
                LED0 = LED_ON;
            }
        }
    }

    なお、上記の割り込み処理ルーチンのリストファイルは以下の通りです。(デフォルト最適化でコンパイルしています。) SMR00への書き込み箇所とSSR00からの読み出し箇所を赤文字にしています。(なお、以下の場合、SMR00への書き込みとSSR00からの読み出しが連続していますが、コンパイラの種類とかバージョンとか最適化レベルとか次第では連続せずに間が開く可能性もあると思っていて、その場合、その区間で送信終了した(シフトレジスタから送出終了した)時には、そこで割り込み要求が発生してしまい、コールバック関数呼出し後にもう1回割り込みが受け付けられて、そこでまたコールバック関数呼出しが行われてしまいますので、それを防止する為、緑文字のコードを追加しています。)

        。。。途中省略。。。

        ;***       85 :         SMR00 &= ~_0001_SAU_BUFFER_EMPTY;
        movw ax, [de+0x10]
        xch a, x
        and a, #0xFE
        xch a, x
        movw [de+0x10], ax
        ;***       86 :         if ((SSR00 & _0040_SAU_UNDER_EXECUTE) == 0U)
        movw ax, [de]
        mov a, x
        bt a.6, $.BB@LABEL@1_3
    .BB@LABEL@1_2:  ; if_then_bb16
        ;***       87 :         {
        ;***       88 :             CSIMK00 = 1U;    /* disable INTCSI00 interrupt */
        set1 0xFFFE5.5
        ;***       89 :             CSIIF00 = 0U;    /* clear INTCSI00 interrupt flag */
        clr1 0xFFFE1.5
        ;***       90 :             r_csi00_callback_sendend();    /* complete send */
        call !!_r_csi00_callback_sendend@1
        ;***       91 :             LED1 = LED_ON;
        clr1 0xFFF04.4
        br $.BB@LABEL@1_4
    .BB@LABEL@1_3:  ; if_else_bb26
        ;***       92 :         }
        ;***       93 :         else
        ;***       94 :         {
        ;***       95 :             LED0 = LED_ON;
        clr1 0xFFF04.3

        。。。以後省略。。。

    以下にプロジェクトのファイル一式を添付します。(当方特有の事情で、コード生成はCS+ V8.03、シミュレータはCS+ V8.01、ビルドはCC-RL V1.02、を使用しています。)

    プロジェクトのファイル一式:
    issue_20200228_0308.zip

    今回、通常処理ルーチン側を以下の通りとしました。

    r_main.c

    void main(void)
    {
        R_MAIN_UserInit();
        /* Start user code. Do not edit comment generated here */
        {
            uint8_t tx[5];
            EI();
            R_CSI00_Start();
            tx[0] = 0;
            tx[1] = 1;
            tx[2] = 2;
            tx[3] = 3;
            tx[4] = 4;
            R_CSI00_Send(tx, 5);

    #if 0 /* Renesas RL78 Simulator */
    #define SIM if(1)
    #define _x_
    #define FPB else if(0)
    #elif 1 /* RL78/G14 Fast Prototyping Board */
    #define SIM if(0)
    #define _x_
    #define FPB else if(1)
    #endif

            /* Note that total clocks of below line are clocks of NOPs + clocks in ISR. */
            SIM{ NOP300();NOP00();NOP5(); } _x_ FPB{ NOP300();NOP10();NOP5(); }
    #if 0
            /* LED0 == ON,  LED1 == ON     --> OK */
            /* nothing */
    #elif 0
            /* LED0 == ON, LED1 == ON     --> OK */
            DI();
            P1 = 1;
            SIM{ NOP80();NOP9(); } _x_ FPB{ NOP80();NOP6(); }
            P1 = 0;
            EI();
    #elif 1
            /* LED0 == OFF, LED1 == ON     --> OK */
            DI();
            P1 = 1;
            SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
            P1 = 0;
            EI();
    #elif 0
            /* LED0 == ON, LED1 == ON     --> OK */
            NOP1();
            DI();
            P1 = 1;
            SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
            P1 = 0;
            EI();
    #endif
            NOP();

            while (false == g_csi00_tx_ready_flag)
            {
                NOP();
            }
        }
        END();

        while (1U)
        {
            NOP();
        }
        /* End user code. Do not edit comment generated here */
    }

    以下、Renesas RL78 Simulatorでの実行結果です。

    /* LED0 == ON,  LED1 == ON     --> OK */
    /* nothing */
    /* LED0 == ON, LED1 == ON     --> OK */
    DI();
    P1 = 1;
    SIM{ NOP80();NOP9(); } _x_ FPB{ NOP80();NOP6(); }
    P1 = 0;
    EI();
    /* LED0 == OFF, LED1 == ON     --> OK */
    DI();
    P1 = 1;
    SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
    P1 = 0;
    EI();
    /* LED0 == ON, LED1 == ON     --> OK */
    NOP1();
    DI();
    P1 = 1;
    SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
    P1 = 0;
    EI();

     

Reply
  • こんにちは。NoMaYです。

    対処方法を考えてみたのですが、まず、今回の割り込みコードの問題は、見え方として、以下の2つのタイプに見えるのかな、と思います。

    (1) CPU動作周波数(MHz)とCSIボーレート(Mbps)の比が4:1ぐらいの場合(例えば24MHz:6Mbpsとか32MHz:8Mbpsとか)には、コンパイラの種類とかバージョンとか最適化レベルとか(或いはコード生成されたコードを改造した内容とか)のバランス次第では、(常に)送信終了のコールバック関数呼び出しがスッポ抜ける(コールバック関数が呼び出されない)

    (2) 上記の(1)の場合よりCSIボーレートが低い場合でも、割り込み禁止区間(他の割り込みや明示的なDI/EIによるもの等)が絶妙なタイミングでくだんの割り込み要求の発生とオーバーラップしてしまった時には、送信終了のコールバック関数呼び出しがスッポ抜ける(コールバック関数が呼び出されない)

    RL78/G14 ユーザーズマニュアル ハードウェア編の記載は、以下の画面コピーの通りなのですが、文面からイメージする対処方法はこんな感じなのかな、と思います。ですが、せっかくコード生成されたコードにはユーザーズマニュアルに記載されていないアイデアが盛り込まれていますので、それを継承した対処方法を試してみました。(もっとも、製品に適用する場合には、ルネサスさんにNOPを挿入して時間稼ぎする必要がある箇所がないかどうか確認した方が良いと思いますが、、、)

    ユーザーズマニュアルの文面からイメージする対処方法:
    (1) ⇒ 諦める(連続送信モードを諦めてシングル送信モードを使う)
    (2) ⇒ 多重割り込みを使って何とかする or 諦める(連続送信モードを諦めてシングル送信モードを使う)

    RL78/G14 ユーザーズマニュアル ハードウェア編 画面コピー
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01uh0186
    r01uh0186jj0330-rl78g14.pdf



    コード生成されたコードのアイデアを継承した対処方法: (桃色文字の行はデバッグ用なのでデバッグ時以外は削除)

    r_cg_serial_user.c

    static void __near r_csi00_interrupt(void)
    {
        if (g_csi00_tx_count > 0U)
        {
            SIO00 = *gp_csi00_tx_address;
            gp_csi00_tx_address++;
            g_csi00_tx_count--;
        }
        else
        {

            SMR00 &= ~_0001_SAU_BUFFER_EMPTY;
            if ((SSR00 & _0040_SAU_UNDER_EXECUTE) == 0U)
            {
                CSIMK00 = 1U;    /* disable INTCSI00 interrupt */
                CSIIF00 = 0U;    /* clear INTCSI00 interrupt flag */
                r_csi00_callback_sendend();    /* complete send */
                LED1 = LED_ON;
            }
            else
            {
                LED0 = LED_ON;
            }
        }
    }

    なお、上記の割り込み処理ルーチンのリストファイルは以下の通りです。(デフォルト最適化でコンパイルしています。) SMR00への書き込み箇所とSSR00からの読み出し箇所を赤文字にしています。(なお、以下の場合、SMR00への書き込みとSSR00からの読み出しが連続していますが、コンパイラの種類とかバージョンとか最適化レベルとか次第では連続せずに間が開く可能性もあると思っていて、その場合、その区間で送信終了した(シフトレジスタから送出終了した)時には、そこで割り込み要求が発生してしまい、コールバック関数呼出し後にもう1回割り込みが受け付けられて、そこでまたコールバック関数呼出しが行われてしまいますので、それを防止する為、緑文字のコードを追加しています。)

        。。。途中省略。。。

        ;***       85 :         SMR00 &= ~_0001_SAU_BUFFER_EMPTY;
        movw ax, [de+0x10]
        xch a, x
        and a, #0xFE
        xch a, x
        movw [de+0x10], ax
        ;***       86 :         if ((SSR00 & _0040_SAU_UNDER_EXECUTE) == 0U)
        movw ax, [de]
        mov a, x
        bt a.6, $.BB@LABEL@1_3
    .BB@LABEL@1_2:  ; if_then_bb16
        ;***       87 :         {
        ;***       88 :             CSIMK00 = 1U;    /* disable INTCSI00 interrupt */
        set1 0xFFFE5.5
        ;***       89 :             CSIIF00 = 0U;    /* clear INTCSI00 interrupt flag */
        clr1 0xFFFE1.5
        ;***       90 :             r_csi00_callback_sendend();    /* complete send */
        call !!_r_csi00_callback_sendend@1
        ;***       91 :             LED1 = LED_ON;
        clr1 0xFFF04.4
        br $.BB@LABEL@1_4
    .BB@LABEL@1_3:  ; if_else_bb26
        ;***       92 :         }
        ;***       93 :         else
        ;***       94 :         {
        ;***       95 :             LED0 = LED_ON;
        clr1 0xFFF04.3

        。。。以後省略。。。

    以下にプロジェクトのファイル一式を添付します。(当方特有の事情で、コード生成はCS+ V8.03、シミュレータはCS+ V8.01、ビルドはCC-RL V1.02、を使用しています。)

    プロジェクトのファイル一式:
    issue_20200228_0308.zip

    今回、通常処理ルーチン側を以下の通りとしました。

    r_main.c

    void main(void)
    {
        R_MAIN_UserInit();
        /* Start user code. Do not edit comment generated here */
        {
            uint8_t tx[5];
            EI();
            R_CSI00_Start();
            tx[0] = 0;
            tx[1] = 1;
            tx[2] = 2;
            tx[3] = 3;
            tx[4] = 4;
            R_CSI00_Send(tx, 5);

    #if 0 /* Renesas RL78 Simulator */
    #define SIM if(1)
    #define _x_
    #define FPB else if(0)
    #elif 1 /* RL78/G14 Fast Prototyping Board */
    #define SIM if(0)
    #define _x_
    #define FPB else if(1)
    #endif

            /* Note that total clocks of below line are clocks of NOPs + clocks in ISR. */
            SIM{ NOP300();NOP00();NOP5(); } _x_ FPB{ NOP300();NOP10();NOP5(); }
    #if 0
            /* LED0 == ON,  LED1 == ON     --> OK */
            /* nothing */
    #elif 0
            /* LED0 == ON, LED1 == ON     --> OK */
            DI();
            P1 = 1;
            SIM{ NOP80();NOP9(); } _x_ FPB{ NOP80();NOP6(); }
            P1 = 0;
            EI();
    #elif 1
            /* LED0 == OFF, LED1 == ON     --> OK */
            DI();
            P1 = 1;
            SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
            P1 = 0;
            EI();
    #elif 0
            /* LED0 == ON, LED1 == ON     --> OK */
            NOP1();
            DI();
            P1 = 1;
            SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
            P1 = 0;
            EI();
    #endif
            NOP();

            while (false == g_csi00_tx_ready_flag)
            {
                NOP();
            }
        }
        END();

        while (1U)
        {
            NOP();
        }
        /* End user code. Do not edit comment generated here */
    }

    以下、Renesas RL78 Simulatorでの実行結果です。

    /* LED0 == ON,  LED1 == ON     --> OK */
    /* nothing */
    /* LED0 == ON, LED1 == ON     --> OK */
    DI();
    P1 = 1;
    SIM{ NOP80();NOP9(); } _x_ FPB{ NOP80();NOP6(); }
    P1 = 0;
    EI();
    /* LED0 == OFF, LED1 == ON     --> OK */
    DI();
    P1 = 1;
    SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
    P1 = 0;
    EI();
    /* LED0 == ON, LED1 == ON     --> OK */
    NOP1();
    DI();
    P1 = 1;
    SIM{ NOP90();NOP0(); } _x_ FPB{ NOP80();NOP7(); }
    P1 = 0;
    EI();

     

Children
No Data