RL78マイコン|本家Arduinoライブラリでのvolatile変数アクセスの衝突防止の実装
前のエントリでvolatile変数アクセスの衝突防止の為のアセンブラコードを書いてみたのですが、本家Arduino
1.0.6のライブラリでの実装を見てみました。
ファイル: arduino-1.0.6\hardware\arduino\cores\arduino\wiring.c
内容(抜粋):
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
SREGは以下のように定義されていました。
ファイル: arduino-1.0.6\hardware\tools\avr\avr\include\avr\common.h
内容(抜粋):
/* Status Register */
#ifndef SREG
# if __AVR_ARCH__ >= 100
# define SREG _SFR_MEM8(0x3F)
# else
# define SREG _SFR_IO8(0x3F)
# endif
#endif
ファイル: arduino-1.0.6\hardware\tools\avr\avr\include\avr\sfr_defs.h
内容(抜粋):
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))
#define _MMIO_DWORD(mem_addr) (*(volatile uint32_t *)(mem_addr))
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr)
#define _SFR_MEM32(mem_addr) _MMIO_DWORD(mem_addr)
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + __SFR_OFFSET)
これをRL78に焼き直すと以下のようになると思います。
unsigned long millis()
{
unsigned long m;
uint8_t oldPSW = PSW;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
DI();
m = timer0_millis;
PSW = oldPSW;
return m;
}
ここでPSWなのですが、トレースデータを見ていると予想がついてしまうように、PSWは0xffffa番地から
リード/ライト出来るようです。RL78のCPUコアのマニュアルの命令フォーマットのページを見ても、命令
コード的に0xffffa番地に対するリード/ライトとPSWに対するリード/ライトは区別不可能です。
ですので、PSWは以下のように定義してしまえば良いように思います。(コード生成機能を使わない場合には
uint8_tがtypedefされていないかも。)
#define PSW (*(volatile uint8_t *)0xffffa)
または
#define PSW (*(volatile unsigned char *)0xffffa)
なお、本家Arduinoライブラリの実装ではSoftwareSerial.cppやServo.cppやGSM3SoftSerial.cppにも同様な
記述がありました。更に、USBCore.cppではC++のコンストラクタ/デストラクタを利用した小技も使われて
いました。
ファイル: arduino-1.0.6\hardware\arduino\cores\arduinoUSBCore.cpp
内容(抜粋):
class LockEP
{
u8 _sreg;
public:
LockEP(u8 ep) : _sreg(SREG)
{
cli();
SetEP(ep & 7);
}
~LockEP()
{
SREG = _sreg;
}
};
u8 USB_Available(u8 ep)
{
LockEP lock(ep);
return FifoByteCount();
}
変数lockがスタック上に確保されるタイミングでコンストラクタにより_sregメンバ変数へのSREGの値待避と
cli()が自動的に行われ、他方、変数lockが解放されるタイミングでデストラクタにより_sregメンバ変数からの
SREGの値復帰が自動的に行われるようになっていました。小技を使わないで記述すると以下のような記述に
なるだろうと思います。
u8 USB_Available(u8 ep)
{
u8 c;
u8 oldSREG = SREG;
cli();
SetEP(ep & 7);
c = FifoByteCount();
SREG = oldSREG;
return c;
}
後でKURUMIスケッチ環境のソースコードも見てみようかと思っています。
1.0.6のライブラリでの実装を見てみました。
ファイル: arduino-1.0.6\hardware\arduino\cores\arduino\wiring.c
内容(抜粋):
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
SREGは以下のように定義されていました。
ファイル: arduino-1.0.6\hardware\tools\avr\avr\include\avr\common.h
内容(抜粋):
/* Status Register */
#ifndef SREG
# if __AVR_ARCH__ >= 100
# define SREG _SFR_MEM8(0x3F)
# else
# define SREG _SFR_IO8(0x3F)
# endif
#endif
ファイル: arduino-1.0.6\hardware\tools\avr\avr\include\avr\sfr_defs.h
内容(抜粋):
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))
#define _MMIO_DWORD(mem_addr) (*(volatile uint32_t *)(mem_addr))
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr)
#define _SFR_MEM32(mem_addr) _MMIO_DWORD(mem_addr)
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + __SFR_OFFSET)
これをRL78に焼き直すと以下のようになると思います。
unsigned long millis()
{
unsigned long m;
uint8_t oldPSW = PSW;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
DI();
m = timer0_millis;
PSW = oldPSW;
return m;
}
ここでPSWなのですが、トレースデータを見ていると予想がついてしまうように、PSWは0xffffa番地から
リード/ライト出来るようです。RL78のCPUコアのマニュアルの命令フォーマットのページを見ても、命令
コード的に0xffffa番地に対するリード/ライトとPSWに対するリード/ライトは区別不可能です。
ニーモニック | 命令コード |
MOV A, PSW | 8E, FA |
MOV A, sfr | 8E, FA(sfr = 0xffffa番地の時) |
ニーモニック | 命令コード |
MOV PSW, A | 9E, FA |
MOV sfr, A | 9E, FA(sfr = 0xffffa番地の時) |
ですので、PSWは以下のように定義してしまえば良いように思います。(コード生成機能を使わない場合には
uint8_tがtypedefされていないかも。)
#define PSW (*(volatile uint8_t *)0xffffa)
または
#define PSW (*(volatile unsigned char *)0xffffa)
なお、本家Arduinoライブラリの実装ではSoftwareSerial.cppやServo.cppやGSM3SoftSerial.cppにも同様な
記述がありました。更に、USBCore.cppではC++のコンストラクタ/デストラクタを利用した小技も使われて
いました。
ファイル: arduino-1.0.6\hardware\arduino\cores\arduinoUSBCore.cpp
内容(抜粋):
class LockEP
{
u8 _sreg;
public:
LockEP(u8 ep) : _sreg(SREG)
{
cli();
SetEP(ep & 7);
}
~LockEP()
{
SREG = _sreg;
}
};
u8 USB_Available(u8 ep)
{
LockEP lock(ep);
return FifoByteCount();
}
変数lockがスタック上に確保されるタイミングでコンストラクタにより_sregメンバ変数へのSREGの値待避と
cli()が自動的に行われ、他方、変数lockが解放されるタイミングでデストラクタにより_sregメンバ変数からの
SREGの値復帰が自動的に行われるようになっていました。小技を使わないで記述すると以下のような記述に
なるだろうと思います。
u8 USB_Available(u8 ep)
{
u8 c;
u8 oldSREG = SREG;
cli();
SetEP(ep & 7);
c = FifoByteCount();
SREG = oldSREG;
return c;
}
後でKURUMIスケッチ環境のソースコードも見てみようかと思っています。
- 関連記事
-
- RL78マイコン|プログラムが変なところでブレークした時にどこまでデバッグ出来るか? (4)
- RL78マイコン|プログラムが変なところでブレークした時にどこまでデバッグ出来るか? (3)
- 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でシリアルポート操作
2015/01/29 blog-entry-560 category: RL78 /* 16bit,8bit CISC */
| h o m e |