なんとかなるさね

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


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に対するリード/ライトは区別不可能です。

ニーモニック       命令コード
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スケッチ環境のソースコードも見てみようかと思っています。

関連記事

2015/01/29   blog-entry-560   category: RL78 /* 16bit,8bit CISC */

go page top