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:
使用されませんでした、と書いたのですが、逆に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:
- 関連記事
-
- RL78マイコン|プログラムが変なところでブレークした時にどこまでデバッグ出来るか? (2)
- RL78マイコン|プログラムが変なところでブレークした時にどこまでデバッグ出来るか? (1)
- RL78マイコン|プログラムが変なところでブレークする様子をシミュレータで見てみた
- RL78マイコン|volatile変数アクセスの衝突防止(結局はDI/EIですが) (2)
- RL78マイコン|KURUMIスケッチ環境でのvolatile変数アクセスの衝突防止の実装 → 無かった
- RL78マイコン|本家Arduinoライブラリでのvolatile変数アクセスの衝突防止の実装
- RL78マイコン|volatile変数アクセスの衝突防止(結局はDI/EIですが) (1)
- RL78マイコン|32bit変数へのアクセスはアトミックじゃない
- RL78マイコン(G12,G13,G14)|CS+(CubeSuite+)でインターバルタイマシミュレーションを試す
- RL78マイコン基板(RL78/G10)|CS+(CubeSuite+)で内蔵周辺機能シミュレーションを試す (3)
- RL78マイコン基板(RL78/G10)|CS+(CubeSuite+)で内蔵周辺機能シミュレーションを試す (2)
- RL78マイコン基板(RL78/G10)|CS+(CubeSuite+)で内蔵周辺機能シミュレーションを試す (1)
- RL78マイコン基板(RL78/G10)|e2studio同梱のPython 2.7.2+pySerial 2.7でシリアルポート操作
- RL78マイコン基板(RL78/G10)|CS+(CubeSuite+)のPythonコンソール上でシリアルポート操作 (3)
- RL78マイコン基板(RL78/G10)|CS+(CubeSuite+)のPythonコンソール上でシリアルポート操作 (2)
2015/01/27 blog-entry-558 category: RL78 /* 16bit,8bit CISC */
| h o m e |