RL78/G13 CSIのマスタ連続送受信でオーバーランエラーが発生する

こんにちは、ユキと申します。

RL78/G13のCSIで、バッファ空き割込みを使った連続送受信をマスタとして行うときのことについて
ご教授いただけないでしょうか。
コンパイラはCC-RL、IDEはe2studioを使用しております。

現在、コード生成を使い、CSI21を以下のような設定で動作させようとしています。

転送モード:連続転送モード
データ長:8ビット
データ転送方向:MSB
データ送受信タイミング:タイプ4
転送レート:
 クロック・モード:内部クロック(マスタ)
 ボー・レート:2000000bps
通信完了割り込み優先順位:低
コールバック機能設定:すべて有効

コード生成を実行した上で、自分のコードでR_CSI21_Send_Receive()を呼び出しているのですが、
以下のような現象が起きています。

・1バイトのデータを送信した時は通信が成功する
・2バイト以上のデータを送信するとオーバーランエラーが発生する
・シングル転送モードを使った場合は、2バイト以上のデータを送信してもオーバーランエラーは発生しない

そして、r_cg_serial_user.cのr_csi21_interrupt()の、

if (g_csi21_tx_count > 0U)
{
    if (g_csi21_tx_count != (g_csi21_send_length - 1U))
    {
        *gp_csi21_rx_address = SIO21;
        gp_csi21_rx_address++;
    }

    SIO21 = *gp_csi21_tx_address;
    gp_csi21_tx_address++;
    g_csi21_tx_count--;
}

上記のコードを以下のように書き換えてみると、オーバーランエラーが発生しなくなりました。

if (g_csi21_tx_count > 0U) 
{

    *gp_csi21_rx_address = SIO21;
    gp_csi21_rx_address++;

    SIO21 = *gp_csi21_tx_address;
    gp_csi21_tx_address++;
    g_csi21_tx_count--;
}

期待通りの動作をするようになったものの、何故こうなるのか理解できておりません。
上記はコード生成でできるコードなので、私の使い方が間違っている気がするのですが……

バッファオーバーランが起きる時、送信データの1バイト目を書き込んだ後の最初のバッファ空き割込みで、
実際にはSDRにデータが存在するような動作をしているように見えます。

しかし、ハードウェアマニュアルを読むと、
送信データの1バイト目を書き込んだ後の最初のバッファ空き割込みでは、
データを受信していないし、バッファ空き割込みなのだからSDRは空いている。
何もせずに2バイト目のデータをSDRに書き込んで良い
というように読み取れました。

私の理解は正しいのでしょうか。そして、何故このような動作をするのでしょうか。
ご助言をいただけると嬉しいです。

足りない情報がございましたら、おっしゃってください。

よろしくお願いいたします。

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

    送信モード時のこの対策としては,現在投稿の準備中ですが,割り込み処理の中で残り転送データ数が0になっていたら,SSRレジスタのTSFビットが0になったら通信は完了しているとして最後のデータを読み出して通信を完了すればいいと思います。
    MAX7219に対して,RL78/G12(24MHzの動作クロック)のCSI00をタイプ4に設定して,6Mbpsの転送速度で送信して正常に送信できています。
  • チョコさん

    お返事ありがとうございます。

    最後に送信完了割り込みが来ることを期待して割り込みタイミングを書き換えるコードになっているのですが、
    送信完了割り込みが発生しない場合があるということですね。

    ご助言を基に、残り転送データ数が0の時にSSRレジスタをポーリングで見て、
    TSFビットが0になったらデータを読み出して抜けるようにしてみましたが、
    まだオーバーランエラーが発生しております。
    以下が変更後の割り込み処理のコードです。間違いがあると思うのですが、ご指摘いただけないでしょうか。

    よろしくお願いいたします。

    static void __near r_csi21_interrupt(void)
    {
    volatile uint8_t err_type;
    err_type = (uint8_t)(SSR11 & _0001_SAU_OVERRUN_ERROR);

    SIR11 = (uint16_t)err_type;

    if (1U == err_type)
    {
    r_csi21_callback_error(err_type); /* overrun error occurs */
    }
    else
    {
    if (g_csi21_tx_count > 0U)
    {
    if (g_csi21_tx_count != (g_csi21_send_length - 1U))
    {
    *gp_csi21_rx_address = SIO21;
    gp_csi21_rx_address++;
    }
    SIO21 = *gp_csi21_tx_address;
    gp_csi21_tx_address++;
    g_csi21_tx_count--;
    }
    else
    {
    r_csi21_callback_sendend(); /* complete send */
    *gp_csi21_rx_address = SIO21;
    gp_csi21_rx_address++;

    while ((SSR11 & _0040_SAU_UNDER_EXECUTE) != 0U)
    {
    NOP();
    }
    *gp_csi21_rx_address = SIO21;
    r_csi21_callback_receiveend(); /* complete receive */
    }
    }
    }
  • チョコです。
    基本的に,SIO21の読出しタイミングの問題だけのようです。そこで,SIO21からの読出しそのものは処理の頭でやるようにして,最初のバッファ空き割り込みでは読み出したデータを格納するポインタを更新しないようにしてみました。
    割り込み処理での時間をできるだけ早くするには,レジスタ・バンク機能を使うことが考えられます。CSI00の例を下に示します。この例では,レジスタ・バンク1を使うように設定しています。
    #pragma interrupt r_csi00_interrupt(vect=INTCSI00,bank=RB1)

    今は,手元に動作環境がないので,論理的に考えただけですが,こんな風に直してみました。この例では,CSI21の割り込みタイミングも書き換えています。

    static void __near r_csi21_interrupt(void)
    {
    volatile uint8_t err_type;

    *gp_csi21_rx_address = SIO21; /* 受信データの読出し */

    err_type = (uint8_t)(SSR11 & _0001_SAU_OVERRUN_ERROR);

    SIR11L = err_type; /* エラーフラグのクリア */

    if (1U == err_type)
    {
    r_csi21_callback_error(err_type); /* overrun error occurs */
    }
    else
    {
    if (g_csi21_tx_count > 0U)
    {
    if (g_csi21_tx_count != (g_csi21_send_length - 1U))
    {
    gp_csi21_rx_address++;
    }
    SIO21 = *gp_csi21_tx_address;
    gp_csi21_tx_address++;
    g_csi21_tx_count--;

    if ( g_csi21_tx_count == 0 ) /* 最後の送信データ書き込み */
    {
    SMR11 &= ~0x0001; /* 転送完了割り込みに設定 */
    if ((SSR11 & _0040_SAU_UNDER_EXECUTE) == 0U)
    { /* 最終データ送信完了 */
    r_csi21_callback_sendend(); /* complete send */
    CSIIF21 = 1; /* 念のために割り込みを要求 */
    }
    }

    }
    else
    {
    *gp_csi21_rx_address = SIO21; /* 最終受信データの読出し */
    SMR11 |= 0x0001; /* バッファ空き割り込みに */
    r_csi21_callback_receiveend(); /* complete receive */

    }
    }
    }
Reply
  • チョコです。
    基本的に,SIO21の読出しタイミングの問題だけのようです。そこで,SIO21からの読出しそのものは処理の頭でやるようにして,最初のバッファ空き割り込みでは読み出したデータを格納するポインタを更新しないようにしてみました。
    割り込み処理での時間をできるだけ早くするには,レジスタ・バンク機能を使うことが考えられます。CSI00の例を下に示します。この例では,レジスタ・バンク1を使うように設定しています。
    #pragma interrupt r_csi00_interrupt(vect=INTCSI00,bank=RB1)

    今は,手元に動作環境がないので,論理的に考えただけですが,こんな風に直してみました。この例では,CSI21の割り込みタイミングも書き換えています。

    static void __near r_csi21_interrupt(void)
    {
    volatile uint8_t err_type;

    *gp_csi21_rx_address = SIO21; /* 受信データの読出し */

    err_type = (uint8_t)(SSR11 & _0001_SAU_OVERRUN_ERROR);

    SIR11L = err_type; /* エラーフラグのクリア */

    if (1U == err_type)
    {
    r_csi21_callback_error(err_type); /* overrun error occurs */
    }
    else
    {
    if (g_csi21_tx_count > 0U)
    {
    if (g_csi21_tx_count != (g_csi21_send_length - 1U))
    {
    gp_csi21_rx_address++;
    }
    SIO21 = *gp_csi21_tx_address;
    gp_csi21_tx_address++;
    g_csi21_tx_count--;

    if ( g_csi21_tx_count == 0 ) /* 最後の送信データ書き込み */
    {
    SMR11 &= ~0x0001; /* 転送完了割り込みに設定 */
    if ((SSR11 & _0040_SAU_UNDER_EXECUTE) == 0U)
    { /* 最終データ送信完了 */
    r_csi21_callback_sendend(); /* complete send */
    CSIIF21 = 1; /* 念のために割り込みを要求 */
    }
    }

    }
    else
    {
    *gp_csi21_rx_address = SIO21; /* 最終受信データの読出し */
    SMR11 |= 0x0001; /* バッファ空き割り込みに */
    r_csi21_callback_receiveend(); /* complete receive */

    }
    }
    }
Children
  • チョコです。
    追加のコメントです。
    >通信完了割り込み優先順位:低
    そもそも,オーバーラン・エラーが発生して問題になるのなら,優先順位は最優先にしておくべきです。
    その上で,多重割り込みを使うなりの対策を行ってください。それでもだめなら,DMAの使用を考えてください。
  • チョコさん
    >基本的に,SIO21の読出しタイミングの問題だけのようです。
    これは、割り込み処理でSIO21からデータを読み出すのが間に合っていなくて、
    新しく受信したデータがSIO21に上書きされてしまっているのではないか、という意味でしょうか?
  • チョコです。
    >割り込み処理でSIO21からデータを読み出すのが間に合っていなくて、
    はい,そうです。今は,その可能性が高いと考えています。
  • チョコさん

    多くのご助言ありがとうございます。

    ■ソースコードについて
    データの読み出しを間に合わせるために、読み出しを処理の頭でやるようにしたということですね。
    しかし、これでもオーバーランエラーは解消しませんでした。
    データの読み出し(*gp_csi21_rx_address = SIO21;)の場所を変えて、
    if (1U == err_type)
    {
        r_csi21_callback_error(err_type); /* overrun error occurs */
    }
    else
    {
        if (g_csi21_tx_count > 0U)
        {
            *gp_csi21_rx_address = SIO21; ← ここでデータを読み出す
            if (g_csi21_tx_count != (g_csi21_send_length - 1U))
            {
                gp_csi21_rx_address++;
            }
            SIO21 = *gp_csi21_tx_address;
            gp_csi21_tx_address++;
            g_csi21_tx_count--;
        }
    ...(以下省略)
    こちらのようにするとエラーが発生しなくなります。
    しかし、これは単にSDRへのデータの取り込みとデータの読み出しのタイミングが
    絶妙に噛み合っただけということですよね。

    ちなみに、レジスタバンクは既に使用しております。

    ■追加のコメントについて
    ・割り込みの優先度を高にする
    ・CSI21以外の割り込み処理の先頭でEI()を実行する
    を試してみましたが、それでも駄目でした。DMAを使うしかないでしょうか……

  • チョコです。
    実際のプロジェクトでコードを確認しないとこれ以上のコメントはできません。
    また,CC-RL78を使っていますか,それともCA-78K0Rを使っていますでしょうか。
    CA-78K0Rでは気になるコードに展開される可能性があります。
  • チョコさん

    そうですよね…実際のプロジェクトはちょっと公開できないので、
    いただいたヒントを基にもう少しこちらで調べてみます。
    色々とご助言くださり、ありがとうございました。
    何か分かりましたら、こちらに投稿させていただきます。

    使っているのはCC-RL78です。
    気になるコードに展開される可能性というのには当てはまらなさそうですね。

  • チョコです。
    最後に,*gp_csi21_rx_address = SIO21;の場所を割り込み処理の一番最初にもっていってください。
    単に読み出してデータ領域に書き込んでも,ポインタを更新しない限りは問題ないはずです。
  • > 実際のプロジェクトはちょっと公開できないので、

    実際のプロジェクトとは別に現象を確認できるプロジェクトを作成して相談されている方もいらっしゃいますよ。

    japan.renesasrulz.com/.../-lib_rename
    > 現象を確認可能なプロジェクトを作成したのですが、
  • チョコさん

    お世話になっております。
    少し分かったことがあるので報告いたします。

    *gp_csi21_rx_address = SIO21;を割り込み処理関数の先頭に持って行ったのでは、
    オーバーランエラーを解消できませんでした。

    これが何故か不思議で調べてみたところ、どうやら以下のような状況であるようです。

    1. 割り込み処理関数に入った直後はデータは受信されておらず、SDRは空である
      1. SDRからデータを読み出しても受信データは得られない
    2. 割り込み処理関数に入って少し後(SDRからデータを読み出して、SSRを読み出した後)辺りでデータの送受信が終わり、SDRに受信したデータが入る
    3. SDRにデータを書き込む
    4. オーバーランエラーが発生する

    つまり、以下のうち3.と4.の間に送信するデータをSDRに書き込まなければなりませんが、
    これが間に合っておらずタイミングが4.の後になってしまっていると考えています。

    1. SDRにデータを書き込み、バッファ空き割り込みを待つ
    2. SDRからシフトレジスタにデータが転送される
    3. バッファ空き割り込みが発生する → 割り込み処理関数内でSDRにデータを書き込む
    4. シフトレジスタ内のデータが送信され、受信したデータがSDRに格納される

    転送速度が2Mbpsですから、1バイト転送するのに掛かる時間は4usですよね。
    IOポートとオシロを使って、SDRに最初の送信データが入ってから割り込み処理関数に入るまでの時間を測ったところ4us掛かっており、
    次の送信データをSDRに書くまでにはさらに1us程掛かっていることが分かりました。
    これでは確かに厳しい気がします。

    CPUのクロックは高速オンチップオシレータクロックで低速メインモード、周波数は8MHzですが、
    この辺りは関係するでしょうか?
    マニュアルの「最大転送レート」から、SCIの転送速度はfCLK/4まで設定可能だと理解したのですが……。

  • fujitaさん

    ご助言ありがとうございます。
    仕事用のPCで作成したプロジェクトのアップロードにはどうしても慎重になってしまいまして……
    考えてみます。