R8C/Tiny(M12) の命令実行時間について

こんにちは,toitecです。

除算(32ビット÷32ビット)のサンプルを
改造しながら、実行時間を測定していたところ,
腑に落ちない状況がありまして,御教授下さい。

<症状>
 除算サブルーチンのメモリ配置位置をひとつ
 ズラすと,処理時間が大きく変化する.
 偶数アドレス -> 61.28μs
 奇数アドレス -> 77.28μs

<評価プログラム抜粋>

.ORG 0400h
ZHBIN:  .BLKB 16   ; 入力
ZHBOUTA:  .BLKB 16   ; H_DIVL_x 出力
; RAMはメモリダンプで見やすくするために16byte
; 単位で確保しています

X_MAIN:

X_MAIN10:
   JSR _R_MA  ; メモリファインダー
   BTSTC trjif_trjir ; 1ms経過?
   JNC X_MAIN10 ; No \,ループ

   ; テスト開始
   MOV.W ZHBIN+0\,R0 ; 入力データロード
   MOV.W ZHBIN+2\,R2
   MOV.W ZHBIN+4\,R1
   MOV.W ZHBIN+6\,R3
   BSET 3\,p1 ; 処理時間計測スタート
   JSR H_DIVL_R ; 32bit÷32bit
   BCLR 3\,p1 ; 処理時間計測エンド
   MOV.W R0\,ZHBOUTA+0
   MOV.W R2\,ZHBOUTA+2
   MOV.W A0\,ZHBOUTA+4
   MOV.W A1\,ZHBOUTA+6
   JMP.W X_MAIN

;   NOP ; 無関係なNOP
   ; 偶数/奇数設定用

H_DIVL_R:
   ENTER #1
   MOV.B #0\,A0
   MOV.B #0\,A1
   CMP.W #0\,R1
   JNE H_DIVL_R_10
   CMP.W #0\,R3
   JEQ H_DIVL_R_90
H_DIVL_R_10:
   MOV.B #32\,-1[FB]
H_DIVL_R_20:
   SHL.W #1\,R0
   ROLC.W R2
   ROLC.W A0
   ROLC.W A1
   SUB.W R1\,A0
   SBB.W R3\,A1
   BMC 0\,R0
   JC H_DIVL_R_30
   ADD.W R1\,A0
   ADC.W R3\,A1
H_DIVL_R_30:
   ADJNZ.B #-1\,-1[FB]\,H_DIVL_R_20
   FCLR Z
H_DIVL_R_90:
  EXITD

 32回のループが存在するため,1.6μsの
 倍数(20MHz:50ns×32=1.6us)で差がでるのは
 予想していたのですが,トータルで16μsの
 差は大き過ぎると感じました。
 プログラム中の,「NOP」を有効/無効にするこ
 とで,サブルーチンの配置位置を動かします。
 計測は,サブルーチン実行の前後でポート出力
 して測定しています。
 被除数と除数はRAM上にセットし,メモリファ
 インダーで自由に変更できるようにしています。
 今回は,FFFFFFFF÷AAAA で確認しました。
 0400~0403:被序数
 0404~0407:除数
 0410~0413:商
 0414~0417:余り
 
 よろしく,お願い致します。
  • toitecさん

    不思議ですね。
    分岐命令を伴う場合(?)は、先頭命令の配置アドレスが奇数偶数で命令キューの溜まり方が違うんですかねー?

    >    NOP ; 無関係なNOP

    試しにこのNOPを2個とか3個とか4個とか5個に増やしてみると何か分かるかもしれませんよ。


    他には、
    > EXITD
    の後ろにもダミーでNOPを4個いれたりするとか?

    むむむむっ。
  • RX CPUについて、次のような記述が有りました。


    「分岐先の命令実行向け整合」
    RX CPUの命令キューを効率良く動作させ、プログラムの実行速度を向上
    RX600シリーズでは分岐先の命令を8バイトに整合
    RX200シリーズでは分岐先の命令を4バイトに整合

    分岐先の命令実行向け整合を行った場合、NOP命令で分岐先の境界を調整します。
    このため、使用メモリ量は増加しますが、処理速度を向上させることが可能となります。
  • すと@konです。

    > 32回のループが存在するため,1.6μsの
    > 倍数(20MHz:50ns×32=1.6us)で差がでるのは
    > 予想していたのですが,トータルで16μsの
    > 差は大き過ぎると感じました。

    まずこの表現が?です。32回のループと偶数奇数アドレス配置と何か関係ありますか?

    また、R8Cはバスが8ビットのため、偶数奇数アドレスを問わず8ビットアクセスとなります。つまり、配置が異なることによるフェッチの変化はない、ということです。

    最初はソフトウェアウェイトでも入っているのかと考えましたが、シングルチップであるためこれも除外されます(ウェイトの設定ができないため)。となると、外部要因で時間差が生じていることが想定されます。

    正確に判断するには偶数配置、奇数配置でそれぞれ10回くらい測定し、各々の処理時間の平均とバラツキを評価してください。

    有意な差を明確にしてから、偶数・奇数による違いを検討された方がいいと思います。


    命令ダンプを見れるなら遡ってチェックするのも手ですが…うんざりしちゃうのでこれは最後の手段ですね。
  • KIRIN様、リカルド様、情報をありがとうございます。

    いろいろ試した結果、下記のコーディングがベストの
    ようです。

    ZHCCNTB: .BLKB 1 ; ループカウンタ

                   ; .SB領域に配置
                   ; 今回はSB=0400h

        .ALIGN
        NOP
    H_DIVL_B:
        MOV.B  #0\,A0
        MOV.B  #0\,A1
        CMP.W  #0\,R1
        JNE    H_DIVL_B_10
        CMP.W  #0\,R3
        JEQ    H_DIVL_B_90
    H_DIVL_B_10:
        MOV.B  #32\,ZHCCNTB-400h[SB]
    H_DIVL_B_20:        ←偶数アドレス
        SHL.W  #1\,R0
        ROLC.W  R2
        ROLC.W  A0
        ROLC.W  A1
        SUB.W  R1\,A0
        SBB.W  R3\,A1
        OR.B   #01h\,R0L    ←BSET 0\,R0
        JC    H_DIVL_B_30
        AND.B  #0FEh\,R0L    ←BCLR 0\,R0
        ADD.W  R1\,A0
        ADC.W  R3\,A1
    H_DIVL_B_30:
        DEC.B  ZHCCNTB-400h[SB]←ADJNZより速い
        JNZ   H_DIVL_B_20   ←Offset=偶数
        FCLR   Z
    H_DIVL_B_90:
        RTS

    <補足>
    ・ループ部分先頭(H_DIVL_B_20)と、ループ部分の
     最後の分岐命令のOffsetの偶数/奇数が影響して
     いるようです。
    ・ADJNZは1行で便利な命令ですが、DEC + JNZ に
     分解した方が1クロックお得のようです。トータル
     ではジャスト32クロック速いわけではありませ
     んが。

    <ルネサス殿へ要望>
     是非、マニュアルに追記お願いします。

     それにしても、32回ループの1ループあたり
     10クロック前後の処理時間の増加は納得でき
     ないところです。ループ内の各命令フェッチで
     1クロックずつ余分に実行時間が増加している
     感じです。

    *今回は、約51.6μsまで短縮できたので、
     スッキリしました。
  • toitecさん

    早くなりましたね。

    ここからは趣味の世界ですけども、
    すとさんがおっしゃっているように、命令コードからクロック数の合計を数えてみてください。

    理論上の動作クロックよりも伸びているとすると
    どこかでストールが発生しているかもしれません。

    私の想像ですけども、
    条件分岐のフラグを読み出すためにCPU内部でデータバスを使用するのと、フェッチ(リード)するためのバスアクセスが競合して、1クロックお休み、みたいな?感じですかね。
    例えば、フラグを読み出すバスが下位側だとすると奇数アドレスの命令をフェッチする場合は早いけど、逆は遅いとか。

    分岐の成立不成立を含めて、細かく命令キューにたまるクロック数を積み上げてみないと確証はもてませんけどねー。

    qIaCrmST9QoEdXGI-0_A0289.gif

  • スタッフのチョコです。

    toitec様のスレッドにコメントさせていただきます。
    (以下に担当者に確認した内容を示します。)

    toitec様へのコメント

    >  それにしても、32回ループの1ループあたり
    >  10クロック前後の処理時間の増加は納得でき
    >  ないところです。ループ内の各命令フェッチで
    >  1クロックずつ余分に実行時間が増加している
    >  感じです。

    R8Cファミリはソフトウェア マニュアルの「第6章 サイクル数の計算」に記載されているように,4バイトの命令キューをもっています。
    R8Cファミリの場合、データバスは8bit幅なので,命令キューへの取り込みは,すと様のコメントのように1Byteずつアクセスします。しかし,命令キューからCPUに取り込む際は偶数、奇数の順に16bit(2Byte)単位で取り込みます。
    また、命令の読み込み処理速度はバス空き待ちや命令キューバッファの状態によって変化します。
    命令キューバッファより長い命令は、1クロック毎に1Byteのデータを読み込む時間が必要になります。
    したがって、命令コードの配置が奇数/偶数番地の違いでサイクル数に差が出ることがあります。


    すと様へのコメント

    > また、R8Cはバスが8ビットのため、偶数奇数アドレスを問わず8ビットアクセスとなります。
    > つまり、配置が異なることによるフェッチの変化はない、ということです。

    上記のように,間に命令キューが入り,CPUは命令キューからは16bit単位で取り出すために,命令コードの配置が奇数/偶数の違いで実行サイクル数に差が出ます。


  • すと@konです。


    なるほど…実行サイクルは結構深いんですねぇ。

    コンパイラの最適化処理も突き詰めればこの領域も解決してくれるのかな?



    勝手に検証:
    toitecさんのケースでは1サイクル(20MHz:50ns)だから逆算していくと…32ループで16μs、1ループで500ns、ループ内コードが11コードだから約50ns。十分あり得る遅れなんですね。

    μsの遅れは許容できるものでもありませんが(汗
  • スタッフのチョコです。

    すと様
    > なるほど…実行サイクルは結構深いんですねぇ。

    ここらを反省して,RL78では3段のパイプライン構成にも関わらず,比較的簡単にプログラムの実行クロック数が計算できるようになっています。(当然,命令が偶数番地か奇数番地かも影響しません。)

    実際にRL78で同じプログラムを思ったのですが,積和乗除演算回路が搭載されているので,意味がありません。

    ここであきらめたら面白くないので,積和乗除演算回路が搭載されていない8ビットのRL78/G10で試してみました。
    シミュレータでの測定結果ですが,どちらも1340クロックと同じ結果になっています。




    8taAlNkBUWSevLB1-0_A0290.jpg

    A8XiL85q6fRkk6vd-1_A0291.jpg

  • チョコさん

    RL78だとRAM実行した時のクロックが読めないんですよねー。

    > クロック数は内部ROM(フラッシュ・メモリ)領域にプログラムがある場合です。
    >内部RAM領域から命令フェッチする場合,最大2倍+3クロックになります。

    G13だと、だいたいROMの2倍程度みたいですけども、奇数偶数でズレがあるみたいですね。
    8bitCPUのG10だとRAM実行でも奇数偶数の違いはないのかな?


    PS
    G10は、
    >クロック数は内部ROM(フラッシュ・メモリ)領域にプログラムがある場合です。
    >内部RAM領域から命令フェッチする場合,最大4倍+6クロックになります。
    と書いてあったので、G13の倍のクロックになるだけなんですかねー。
  • スタッフのチョコです。

    Kirin様の「RL78でRAM実行すると」にコメントさせていただきます。

    RL78でRAM上のプログラムを実行する場面は,フラッシュのセルフプログラミングや高速CRCの計算処理など限られているかと存じます。そんな状況ですので,RAMでの実行速度までは。。。。。

    基本的に,RL78ではコード・フラッシュ・メモリからのプリフェッチは4バイト単位で実行されます。RL78/G13などのデータバスは16ビット(2バイト)ですし,RL78/G10は8ビットのCPUで,データバスは8ビットしかありません。そこらが実行クロック数に影響します。

    ちなみに,先ほどのG10で1340クロックかかったプログラムをそのままG12で動作させたところ,882クロックでした。ここらがバス幅による実行クロック数の差になりますか。