ゆとりーなの日記

日記的な事を書いて行くと思はれる

なんだかんだでC言語が最強疑惑

久々の日記はハードウェアを叩くとかそんな話です。
あるハードを叩くコードをC言語で用意しようとすると大抵かうなります。

#define HOGE_BASE 0xffff0000

#define HOGE_HUGA_OFFSET 0x00
#define HOGE_HAGE_OFFSET 0x04

#define HOGE_HUGA (HOGE_BASE + HOGE_HUGA_OFFSET)
#define HOGE_HAGE (HOGE_BASE + HOGE_HAGE_OFFSET)

define塗れで眩暈がしますね。そして、これを實際に使ふ時はキャスト塗れになるのが嫌なのでこれまたマクロに頼る訣です。

#define IO_READ_WORD(addr) (*(unsigned long *)((void *)(addr)))
#define IO_WRITE_WORD(addr, data) (*(unsigned long *)((void *)(addr)) = (data))

で、實際に叩く段になると、

void hoge_init(void) {
  IO_WRITE_WORD(HOGE_HAGE, 0x313);
}

みたいなことになる訣ですね。ガン萎えですはい。
といふ訣で構造體に甘えてみようかと云ふ流れになります。構造體を使ふと大分落ち着きます(まあきつと4バイト境界なら大丈夫でせうと云ふ一點讀みで、この邊の嚴密な規格はよく知らないので惡しからず)。

volatile struct {
  unsigned long huga;
  unsigned long hage;
} * hoge = (void *)0xffff0000;

で、これを使ふには、

void hoge_init(void) {
  hoge->hage = 0x313;
}

で濟みます。defineを書かないだけでかうも心が晴れやかになるのです。
レジスタのワードアクセスは割とこれで解決だつたりするのですが、この手のレジスタのアクセスは大體ビット單位だつたりするんですよね。といふ訣でまたここでもdefineの魔の手が潜んでゐるのです。

#define HOGE_FOO_SHIFT 5
#define HOGE_FOO_DISABLE 0x0
#define HOGE_FOO_ENABLE 0x1
.
.
.

正直この手のdefineはどの程度用意した方がいいのかよく分らんのです。別にビット演算が嫌ひな訣ではなくて、あるビットを立てたら有效になる機能があつた時に、有效にする時は|=、無效にするときは&=で~みたいなこと書くのが氣持ち惡いと言ひますかね、あと1ビットならず、3ビット位取つて指定するものになるとめんどくさくてしやうがないのです。
そこで救世主となるかと思はれたのがビットフィールドです。

volatile struct {
  union {
    unsigned int word;
    struct {
      unsigned int reserve1:26;
      unsigned int foo:1;
      unsigned int reserve2:5;
    } bits;
  } huga;
  union {
    ...
  } hage;
} * hoge = (void *)0xffff0000;

こんな風に構造體を定義しておけば

void set_foo(void) {
  hoge->huga.bits.foo = 1; /* 有效 */
  hoge->huga.bits.foo = 0; /* 無效 */
}

非常に綺麗に書けるのです。所がこの美しさには刺がありまして、かういふコードを書くとあつさり「sb」命令が吐かれてしまふのです。この手のハードウェアを叩く樣なコードではワードアクセス、即ち「sw」が必須なことが案外あつて、シミュレータにすら彈かれるみたいな悲しみを背負ふことも稀に良くあつたりします。しかもvolatileを外すとちやんと「sw」を吐く樣になつたりと結構謎な所もあるのですが、volatileを外すなんて事は恐くて迚も出來ませんし、ビットフィールド樣は諦めざるをえない展開は避けられないのです。
そこで滿を持しての我らがC++の登場です。テンプレートとかで輕く呪文を唱へればポータブルなビットフィールドが作れたりするのです。以前kikairoya先生が作つてゐた樣なものを用意してみれば、

template <int High, int Low = High>
struct bit_access  {  
  volatile bit_access &operator =(T t) volatile {
    data = (data & mask) | (t << Low);
    return *this;
  }

  operator T() const volatile {
    return (data & mask) >> Low;
  }
 
 private:
  static const uint32_t mask
    = (((~static_cast<T>(0) >> (High + 1)) << (High + 1)) |
       ~((~static_cast<T>(0) >> Low) << Low));
       
  volatile uint32_t data;
};

volatile struct {
  union {
    unsigned int word;
    struct {
      bit_access<5> foo;
    } bits;
  } huga;
  union {
    ...
  } hage;
} * hoge = (void *)0xffff0000;

void set_foo(void) {
  hoge->huga.bits.foo = 1; // ちやんとsw!
}

さくつと解決するのです。流石C++樣です。ところが話はここで終りではなくて、ある悲しみが殘されてゐたりします。あるハードウェアレジスタに一度に複數の設定を書き込む時、詰りは飛び飛びのビットに書き込む時に效率の惡いコードが吐かれて終ふと云ふ悲しみがあるのです。

volatile struct {
  union {
    unsigned int word;
    struct {
      bit_access<5> foo;
      bit_access<2> hoo;
    } bits;
  } huga;
  union {
    ...
  } hage;
} * hoge = (void *)0xffff0000;

void set_foo(void) {
  hoge->huga.bits.foo = 1; // ここでsw
  hoge->huga.bits.hoo = 1; // ここでもsw
}

まあvolatileが附いてゐるので當たり前と言へば當たり前の話で、寧ろここで纏められたら他で何されるか分らなくて怖いみたいな話にもなつて終ふのでこれはこれで仕方ない感はあるのですが、古へのC言語では

#define HOGE_FOO_ENABLE (1 << 5)
#define HOGE_HOO_ENABLE (1 << 2)

void set_foo(void) {
  IO_WRITE_WORD(HOGE_HUGA, HOGE_FOO_ENABLE | HOGE_HOO_ENABLE); /* sw一發... */
}

とsw一發で濟んでゐたことを考へると少々負けた氣がしてして終ふのですよね。どうにかならないものですかね...。