なんとかなるさね

マイコンをネタにブログを始めてみました


RL78マイコン|32bit変数へのアクセスはアトミックじゃない 

前のエントリで、16bit int変数のインクリメント程度の割り込み処理では割り込み処理コード内でPUSH/POPは
使用されませんでした、と書いたのですが、逆に32bit long変数のインクリメントではPUSH/POPも使用される
上にインクリメント自体に数命令掛かります。更に変数のリードでも上位16bitリードと下位16bitリードの2命令
掛かります。試しに、人為的に割り込み発生タイミングを調整し、上位16bitリードと下位16bitリードの隙間で
割り込みが発生して割り込み処理でリード途中の32bit全体のインクリメントが行われてしまうトレースデータを
作ってみました。(以下のコードはコンパイラにアセンブラソースを生成させて該当箇所を抜粋したものです。
ちなみに、[INF]の後の数字ですが、左側が命令コードのバイト数、右側が命令コードの実行クロック数です。)

前のエントリで試したV_timer_tickの変数宣言

volatile int V_timer_tick;

今回試したV_timer_tickの変数宣言

volatile long V_timer_tick;

32bit long変数をインクリメントする割り込み処理コード

; line    58 : __interrupt static void r_tau0_channel0_interrupt(void)
; line    59 : {

@@BASE  CSEG    BASE
_r_tau0_channel0_interrupt:
$DGL    1,20
        push    ax                                              ;[INF] 1, 1
        push    bc                                              ;[INF] 1, 1
??bf_r_tau0_channel0_interrupt:
; line    60 :     /* Start user code. Do not edit comment generated here */
; line    61 :     V_timer_tick++;
$DGL    0,3
        movw    bc,!_V_timer_tick+2                             ;[INF] 3, 1
        movw    ax,!_V_timer_tick                               ;[INF] 3, 1
        addw    ax,#01H ; 1                                     ;[INF] 3, 1
        sknc                                                    ;[INF] 2, 1
        incw    bc                                              ;[INF] 1, 1
?L0003:
        movw    !_V_timer_tick,ax                               ;[INF] 3, 1
        xchw    ax,bc                                           ;[INF] 1, 1
        movw    !_V_timer_tick+2,ax                             ;[INF] 3, 1
; line    62 :     /* End user code. Do not edit comment generated here */
; line    63 : }
$DGL    0,5
??ef_r_tau0_channel0_interrupt:
        pop     bc                                              ;[INF] 1, 1
        pop     ax                                              ;[INF] 1, 1
        reti                                                    ;[INF] 2, 6
??ee_r_tau0_channel0_interrupt:


32bit long変数をリードして正の値を判定するmain()関数側コード

; line    70 :          if ( V_timer_tick > 0 )
$DGL    0,10
        movw    ax,!_V_timer_tick+2                             ;[INF] 3, 1
        clrw    bc                                              ;[INF] 1, 1
        cmpw    ax,bc                                           ;[INF] 1, 1
        or1     CY,a.7                                          ;[INF] 2, 1
        bnz     $?L0007                                         ;[INF] 2, 4
        movw    ax,!_V_timer_tick                               ;[INF] 3, 1
        onew    bc                                              ;[INF] 1, 1
        cmpw    ax,bc                                           ;[INF] 1, 1
?L0007:
        bc      $?L0005                                         ;[INF] 2, 4
; line    71 :          {
??bb01_main:
; line    72 :                  NOP();
$DGL    0,12
        nop                                                     ;[INF] 1, 1
??eb01_main:
; line    73 :          }


上位16bitリードと下位16bitリードの隙間で割り込みが発生して32bit全体のインクリメントが行われることも。




なお、割り込み発生タイミングの調整はNOP();を複数挿入して行いました。



追記 : 補足

今回のmain()関数側コードにはV_timer_tick変数をリードしている箇所が2箇所あるのですが、よくよく考えて
みると、上位16bitのリードがどうとか下位16bitのリードがどうとか言う話以前に、素朴に2箇所あるリードの
隙間で割り込みが発生して変数の値が変わってしまう事もあり得ます。そこで、ちょっとだけ変更してみました。
(もちろん、上位16bitのリードがどうとか下位16bitのリードがどうとか言う話はそのまま何も変わりませんが。)
そして、変更していて気付いたのですが、上位16bitリードと下位16bitリードの隙間で割り込みが発生した時に
起きる妙な現象を示す例をトレースデータを使わずに作れそうでしたので作ってみました。

変更前

    V_timer_tick = 0;
    途中省略
    for (;;)
    {
        if ( V_timer_tick > 0 )
        {
            NOP();
        }
        else if ( V_timer_tick < 0 )
        {
            NOP();
        }
    }


変更後

    V_timer_tick = 0x1ffff;
    途中省略
    for (;;)
    {
        long tick;
        tick = V_timer_tick;
        if ( tick > 0x1ffff )
        {
            NOP();
        }
        else if ( tick < 0x1ffff )
        {
            NOP();
        }
    }


インクリメントすると下位16bitから上位16bitへ桁上がりが生じる値(例えば0x1ffff)の時に事が起きてしまうと、
本来は0x20000が読み出される筈なのに0x10000が読み出されるという妙な現象が発生します。



コードは以下の通りです。

; line    68 :  for (;;)
?L0003:
; line    69 :  {
??bb00_main:
; line    70 :          long tick;
; line    71 :          tick = V_timer_tick;
$DGL    0,11
        movw    bc,!_V_timer_tick+2                             ;[INF] 3, 1
        movw    ax,!_V_timer_tick                               ;[INF] 3, 1
        movw    [hl],ax ; tick                                  ;[INF] 1, 1
        xchw    ax,bc                                           ;[INF] 1, 1
        movw    [hl+2],ax       ; tick                          ;[INF] 2, 1
; line    72 :          if ( tick > 0x1ffff )
$DGL    0,12
        cmpw    ax,#02H ; 2                                     ;[INF] 3, 1
        or1     CY,a.7                                          ;[INF] 2, 1
        bnz     $?L0007                                         ;[INF] 2, 4
        movw    ax,[hl] ; tick                                  ;[INF] 1, 1
        clrw    bc                                              ;[INF] 1, 1
        cmpw    ax,bc                                           ;[INF] 1, 1
?L0007:
        bc      $?L0005                                         ;[INF] 2, 4
; line    73 :          {
??bb01_main:
; line    74 :                  NOP();
$DGL    0,14
        nop                                                     ;[INF] 1, 1
??eb01_main:
; line    75 :          }
; line    76 :          else if ( tick < 0x1ffff )
$DGL    0,16
        br      $?L0003                                         ;[INF] 2, 3
?L0005:
        movw    ax,[hl+2]       ; tick                          ;[INF] 2, 1
        onew    bc                                              ;[INF] 1, 1
        cmpw    ax,bc                                           ;[INF] 1, 1
        or1     CY,a.7                                          ;[INF] 2, 1
        bnz     $?L0010                                         ;[INF] 2, 4
        movw    ax,[hl] ; tick                                  ;[INF] 1, 1
        cmpw    ax,#0FFFFH      ; -1                            ;[INF] 3, 1
?L0010:
        bnc     $?L0003                                         ;[INF] 2, 4
; line    77 :          {
??bb02_main:
; line    78 :                  NOP();
$DGL    0,18
        nop                                                     ;[INF] 1, 1
??eb02_main:
; line    79 :          }
??eb00_main:
; line    80 :  }
$DGL    0,20
        br      $?L0003                                         ;[INF] 2, 3


追記 : メモ

ちなみに、割り込み処理コードから関数呼び出しを行ったら一気に壮絶なPUSH/POPの大量発生になりました。
(PUSH/POPでループが回っていますので見掛けよりずっとPUSH/POPが多いです。)

; line    59 : __interrupt static void r_tau0_channel0_interrupt(void)
; line    60 : {

@@BASE  CSEG    BASE
_r_tau0_channel0_interrupt:
$DGL    1,20
        push    ax                                              ;[INF] 1, 1
        push    bc                                              ;[INF] 1, 1
        push    de                                              ;[INF] 1, 1
        push    hl                                              ;[INF] 1, 1
        mov     c,#0CH                                          ;[INF] 2, 1
        dec     c                                               ;[INF] 1, 1
        dec     c                                               ;[INF] 1, 1
        movw    ax,_@SEGAX[c]                                   ;[INF] 3, 1
        push    ax                                              ;[INF] 1, 1
        bnz     $$-6                                            ;[INF] 2, 4
        mov     a,ES                                            ;[INF] 2, 1
        mov     x,a                                             ;[INF] 1, 1
        mov     a,CS                                            ;[INF] 2, 1
        push    ax                                              ;[INF] 1, 1
??bf_r_tau0_channel0_interrupt:
; line    61 :     /* Start user code. Do not edit comment generated here */
; line    62 :     V_timer_tick++;
$DGL    0,3
        movw    bc,!_V_timer_tick+2                             ;[INF] 3, 1
        movw    ax,!_V_timer_tick                               ;[INF] 3, 1
        addw    ax,#01H ; 1                                     ;[INF] 3, 1
        sknc                                                    ;[INF] 2, 1
        incw    bc                                              ;[INF] 1, 1
?L0003:
        movw    !_V_timer_tick,ax                               ;[INF] 3, 1
        xchw    ax,bc                                           ;[INF] 1, 1
        movw    !_V_timer_tick+2,ax                             ;[INF] 3, 1
; line    63 :  extfunc();
$DGL    0,4
        call    !!_extfunc                                       ;[INF] 4, 3
; line    64 :     /* End user code. Do not edit comment generated here */
; line    65 : }
$DGL    0,6
??ef_r_tau0_channel0_interrupt:
        pop     ax                                              ;[INF] 1, 1
        mov     CS,a                                            ;[INF] 2, 1
        mov     a,x                                             ;[INF] 1, 1
        mov     ES,a                                            ;[INF] 2, 1
        movw    de,#_@SEGAX                                     ;[INF] 3, 1
        mov     c,#06H                                          ;[INF] 2, 1
        pop     ax                                              ;[INF] 1, 1
        movw    [de],ax                                         ;[INF] 1, 1
        incw    de                                              ;[INF] 1, 1
        incw    de                                              ;[INF] 1, 1
        dec     c                                               ;[INF] 1, 1
        bnz     $$-5                                            ;[INF] 2, 4
        pop     hl                                              ;[INF] 1, 1
        pop     de                                              ;[INF] 1, 1
        pop     bc                                              ;[INF] 1, 1
        pop     ax                                              ;[INF] 1, 1
        reti                                                    ;[INF] 2, 6
??ee_r_tau0_channel0_interrupt:


関連記事

2015/01/27   blog-entry-558   category: RL78 /* 16bit,8bit CISC */

go page top