「ソフトウェアー・エンジニアリング」カテゴリーアーカイブ

ソフトウェアー関係の話題など・・

最近は、github にプッシュしてます~

RL78/G13のスタートアップルーチン修正

const 領域の問題が解決したと思って、コンパイラに含む問題は解決されたと
思っていたのだが、何だか、動作が怪しい・・・

少し調べると、クラスの初期化リストが正しく動いていないように感じた。

xxxx.lst を見ながら、xxxx.mot ファイルのバイナリーを色々調査すると、明
らかに呼ばれていない関数がある事が判った。

00004c96 <__GLOBAL__sub_I__Z5wait_v>:

このアドレス「0x00004c96」は、どうやら、「.ctor」に積まれているようだ。

今まで、スタティックに定義したクラスのコンストラクターは、

extern void rl78_run_preinit_array(void);
extern void rl78_run_init_array(void);
extern void rl78_run_fini_array(void);

の3つを走らせれば良いのだと思っていたが、どうやら、「ctor」に積まれた
アドレスもコールしておく必要があるようだ・・

そこで、リンカースクリプトに「ctor」、「dtor」リストのシンボルを追加して、
「start.s」に関数を呼ぶエントリーを追加、「init.c」から呼ぶようにしてみ
た。
※どの順番で呼ぶのか不明なので、ctor を最初に呼ぶようにした。
※ctor、dtor のエントリーアドレスを得る方法が判らないので、シンボル追加。
この「無理やり」な解決方法が合っているか不明ではあるけど・・・

とりあえず、初期化が正しく行われ、正常に動作しているようだ。

まだまだ、スタートアップの方法に問題を含んでいるのかもしれない・・・

ここは、時間が出来たら、もう少し厳密な調査をしたい。

RL78/G13でインターバルタイマーを使ってみる。

RL78も、UARTが動作し始めた事で、佳境に入った感が出て来た。

とりあえず、現在使ってみた感想を述べてみる~
・バイナリーは、R8Cより少なくなる感じではあるけど、基本内部は8ビット構成な
ので、16ビットや32ビットを扱うと、肥大化は免れない。
・ミラー領域のおかげで、64K領域を超えた場合も、ある程度普通に扱う事が出来る。
・32MHzで動作するので、速度面でもかなり有利に感じる。
・RL78/G13は、コアは「S-2」なので、掛け算や割り算命令は無いものの、
外部に「乗除積和算器」があり、コンパイラオプション「-mmul=g13」で、コンパイラ
はこのリソースを使う為、それなりの速度で動作する。
※但し、割り込みルーチン内で使う場合には注意を要すると思われる。
・トータルメモリーのサイズから考えると、コストパフォーマンスに優れている。
・消費電力が非常に小さい。

いつもは、LED点滅の後くらいに、インターバルタイマーを実装するけど、RL78
のインターバルタイマーは、1ユニットで、12ビットレンジ、低速で、シンプルすぎ
るので、イマイチ意欲が沸かなかったが、必要な機能ではあるので、粛々と実装した、
動いた。

ただ、残念なのは、カウンターの値を読み出す事が出来ないので、正確なタイマーを実
装したい場合などに使えない。

単に設定したインターバルを待つだけのものでしか無い。

-----

ついでなので、ソフトディレイも実装した。
RL78/G13では、32MHz動作なので、より細かい単位も可能だけど、とりあえ
ず良く使うだろう、1uS(1マイクロ秒)単位の関数にした、これは、R8Cと同じ仕
様。
最初、アセンブラは、覚えなくても良いとか言ってたけど、結局、なんだかんだで、覚え
る必要が出てきて、ソフトウェアーマニュアルを読む事になった・・・

void micro_second(uint16_t us)
{    
    while(us > 0) {
        --us;
    }
}

上のようなコードは、最適化(-O2)して、以下のように展開される

(2) decw    0xffef0
(1) movw    ax, 0xffef0
(1) cmpw    ax, #0
(1) skz
(3) br      !!4922 <.L629>

最適化されても、ワークメモリーが使われているが、マシンサイクルから考えると、なる
ほど、ペナルティーは意外と少ない。
RL78のような、アーキュムレーターが基本のCPUでは、レジスターだけに割り振る
コードを出すのは難しいのかもしれない・・
※()内がマシンサイクル

この結果を考慮して、全体で32クロックになるように「nop」命令を置く。

・次のコードで実験してみた。

    while(1) {
        utils::delay::micro_second(10);
        P4.B3 = !P4.B3();
    }

10uS毎にポートを変化させてみた。
IMG_0802s
大体合ってる~

インターバルタイマーソースコード

RL78/G13でUARTを使ってみる

暇をみて、I/O関係の定義を粛々と実装しているけど、まずはシリアル通信だろう~
※I/Oポートの定義は実装した。

早速、SAU(シリアル・アレイ・ユニット)の定義を作った。
RXマイコンやR8Cのハードウェアーマニュアルでは、レジスター名の命名は、規則的
で、一貫性があり、作りやすいのだけど、RL78は、一貫性が無く、ハードウェアー
マニュアルと乖離しないように、考えながら実装しないと駄目な感じで疲れる。
※R8Cは、多少古いので、部分的に微妙な部分もあるけど・・

例えば、I/Oポート:
「P1」はP1グループ(P10~P17)を指す。
「P10」は、「P1」ポートの「0」ビットなんだけど・・・
P10グループ(P100~P107)のポートも同時に存在する為、「当たる」・・・
従って、ビット指定の令名はプログラム上から行えない。
なので、「P1.B0」とするようにした、これは、RXやR8Cもそうしているので、
問題ないけど・・、何でそうなるの?、作った人って何考えてるか・・・

I/Oポートでそんな感じだから、他も、かなり酷い、でもまぁ仕方無い、なるべく、
ハードウェアーマニュアルの記述を取り込むように、考えながら実装してみた。

それで、早速UART0を使った、送信、受信のクラスを書いてみた。

RXともR8Cとも違う構成のレジスター郡で、RL78はNEC系なんだと思うが、
理解と動作するまでに、それなりに時間がかかった・・・

文字列を出力する簡単なプログラムが、思ったように動作しない事が主な原因だった。
※この問題は、「RL78のミラー領域」で詳細を解説してある。

とりあえず、ポーリングによる実装のみしてあり、これから、割り込みを使った物や
DMAを使った実装を行いたい。

追記:
割り込みを使った実装を行った、以前に書いた、R8C用のコードをほぼそのまま
使ったが、問題無く動作する。
この辺り、C++は、再利用性が高い、これは、実装段階で、再利用を考えながら
ハードの依存を極力減らして書いている事が大きいのだけど・・・

DMAは、転送量があまり多く無いシリアル通信の為に使うと、もったいない気が
する、SDカードの読み書き、LCDへの転送など、転送量が多いデバイス用に確
保する方が良いと思った、また、DMAでは、結局、同期を取ったり、その他の部
分のマネージメントが色々ありそうなので、とりあえず保留とする事にした。

-----
RL78内蔵の「シリアル・アレイ・ユニット」は、このデバイスでは、2ユニット
、6チャネルの通信回路が使えて、UART回線なら3回線を同時に使える。

「G13/sau.hpp、common/uart_io.hpp」テンプレートでは、これを扱えるように工夫
してある。
RL78/G13 では、シリアル通信は、偶数チャネルで送信動作、奇数チャネルで受信動
作を行う為、送信チャネル、受信チャネルを別々に指定する。
※制限もあるので、複数チャネルを使う場合には注意が必要。

UART0を宣言する場合は以下のようにする。
※現在は使われていないが、送信バッファと受信バッファの大きさを指定する。

    device::uart_io<device::SAU00, device::SAU01, 128, 128> uart0_io_;

他のチャネルは動作テストはしていないけど、同じように扱う事が出来ると思う。

    device::uart_io<device::SAU02, device::SAU03, 128, 128> uart1_io_;
    device::uart_io<device::SAU10, device::SAU11, 128, 128> uart2_io_;

UART の簡単なテスト

IMG_0800s

-----
フラッシュへのプログラムと、UARTとの通信では、共に、RxD,TxDなどを
使う、これらの切り替えを出来るように、2回路のスイッチを設けて、書き込み回路
もテスト基板に追加した。
※書き込み器は、デバイスの「/RESET」信号を制御するので、「RTS」信号を必要とす
る。

RL78_Flash_UART

RL78/G13のミラー領域

UARTの実装を行っている過程で、文字列を出力する簡単なプログラムが、思ったよう
に動作しないで、随分悩んだ・・・

調べると、ミラー領域に関する認識が甘かったようで、データフラッシュ領域と当たって
いた事が判った。

8/16ビットマイコンでは、ポインターは16ビットサイズで、基本64キロバイトの
領域しかダイナミックにアクセス出来ない。

RL78では、物理的なメモリー空間は1Mバイト(64Kが16ページ)ある。
プログラム領域は、0番地から始まり、128Kバイト品(R5F100LGAFB)の場合、
0x1FFFFまで使える。

RAM領域は、128キロバイト品の場合、12キロバイトあり、0xFCF00から
始まる。

データフラッシュは128キロバイト品の場合、8キロバイトあり、0xF1000
から0xF2FFFまである。
※64キロバイト品では、半分の4キロバイト

RL78のgccでは、R/Wアクセスする領域は、常に0xF0000~
0xFFFFFをベースとしていて、RAM領域をベースにアクセスするようになって
いる。
ここで問題になるのが、プログラム領域に置かれた読み出し専用データのアクセスとなる。
RL78では、「ミラー領域」を使ってこれを解決していて、RAM領域、
データフラッシュ領域以外は、ROM領域のアクセスと同等となる
仕組みがある。

ここで、以前に修正したリンカースクリプトが問題となった・・・

 .rodata (MAX(__romdatastart + __romdatacopysize, 0x2000)) : {

この「0x2000」のオフセットが何なのか、判ってなかった為、これを無効
(0x0000)としていた・・・
※0x2000の領域が「空く」のは勿体無い・・

当然、「0x0000」にすると、文字列データは、先頭から配置される為、テストして
いたプログラムでは、0x2000以下の領域に配置される、ミラー領域では、このエリ
アは、データフラッシュ領域の為、0xF1000~0xF1FFFがアクセスされてい
た。
それには、途中で気がついて、オフセットを0x2000に戻したが、それでも、直らな
い、良く調べると、128KB品は、データフラッシュが倍の8Kバイトあり、
0xF1000~0xF2FFFまでとなっていた。
そこで、オフセットを「0x3000」とする事で、解決した。

 .rodata (MAX(__romdatastart + __romdatacopysize, 0x3000)) : {

※リンカースクリプトは修正済みでプッシュしてある。

つまり文字列などの固定データは、0x3000以降、0xCF00までに置く必要があ
る。
まぁ、このくらいの領域があれば、当面困る事は無いと思われる。

先頭の空いた領域には、「.lowtext」セクションが割り当ててあるので、ここには、割り込
みルーチンなどを優先的に配置するのが良さそうだと思う。
※割り込みルーチンは、ベクターが16ビット固定なので、必ず64K以内に配置する必要
がある。

また、64K以降のエリアへは、「__far」を使う事で、ポインターを32ビット扱いとして
アクセスする事も出来るが、C++ ではこのキーワードを上手く扱えないようで、悩んでる。
※とりあえず、Cの関数だけで操作するしか無い。

Cの関数宣言では、ポインターが32ビット扱いになり、逆アセンブルリストを観た感じでは
ESレジスター付き20ビットでアクセスするコードになっている。

「コード」の呼び出しは、20ビット対応の「call」命令が使われているので、全域に対して
アクセスでき、64Kの壁を意識する必要は無いようだ。

 12c:   fc be 32 00        call    !!32be <_main>

※コードが64Kを超えて伸びていっても問題は無いと思われる。

-----
RL78は、Z80のようだと思ったら、実際Z80(78K0)がベースのようだwww

※昔、ゲームボーイのプログラムで、Z80のアセンブラを随分扱ったので、馴染みがある。
でも、32MHzで動くZ80となると、かなり話が違う。

プログラムでプログラムを生成する

最近、RL78用のデバイス定義ファイル生成用のプログラムを実装している。

sed、grep、awk、perl、などなど、スクリプトを駆使しても良いが、C++ では、
文字列の処理もかなり、柔軟に書けるので、制限が多いスクリプトはあまり使わ
なくなった。

基本的な流れとしては、必要最低限の表現で記述された定義ファイルを読み込ん
で、それから、C++ のコードを生成するもの。
もう一つは、テキスト化されたファイル(元は PDF )から、フィルターを駆使し
て、必要な部分を抜き出して、定義ファイルを作成する事。

しかし、PDF をテキスト化する過程の制限や、キーワードを自動で「抜く」のは難
しい事が判った。
この部分は、しばらくは手作業で行う事になる・・・
また、RL78、M32C、RX のハードウェアーマニュアルの違いで、一律では出来ない。

-----

それでも、以前に比べれば、冗長な記述を長い時間をかけて、だらだら書くのに比べ
れば、非常に軽減され、ミスも少なくなった。
そして、構成を変えるのも苦にならない、単純に、プログラムを修正して作りなおす
だけなので。

定義ファイルは、JSON とか使えば良いだろうと思うかもしれないが、以前から、もっ
と軽量な物を模索していたので、一度、自分で書いてみる事にした。
もちろん、XML は、史上最悪なフォーマットである事は言うまでもない、これを有難が
る人がいる事に、驚くばかりである、疫病のように世間に広まってしまっているけど・・

実装してみると、以外と奥が深く、色々な書き方に柔軟に対応するのは難しい事が判っ
た。
以前に、自分のアプリ用にプリファレンスを読み書きするクラスを書いたけど、これは、
人間が編集する事を前提としていないのと、構造が今回とは違う物なので、採用しなか
った。

定義ファイルはこんな感じで書く:

base {
    .title     "RL78/G13 グループ・ポート・レジスター定義",
               "Copyright 2016 Kunihito Hiramatsu"
    .author    "平松邦仁 (hira@rvf-rc45.net)"
    .file      "common/io_utils.hpp"
    .space     "device"
}

reg {
    .title     "クロック動作モード制御レジスタ(CMC)",
               "リセット時:00H"
    .base      "rw8_t"
    .address   "0xFFFA0"
    .name      "CMC"

    bit {
        .title  "高速システム・クロック端子の動作モード"
        .def    7, 1
        .name   EXCLK
    }
}

変換すると、以下のように C++ コードを作る~

#pragma once
//=====================================================================//
/*! @file
    @brief  RL78/G13 グループ・ポート・レジスター定義 @n
            Copyright 2016 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "common/io_utils.hpp"

namespace device {

    /// @brief CMC レジスタ定義
    typedef rw8_t<0xFFFA0> cmc_rw;
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  クロック動作モード制御レジスタ(CMC) @n
                リセット時:00H
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    struct cmc_rw_t : public cmc_rw {
        using cmc_rw::operator =;
        using cmc_rw::operator ();
        using cmc_rw::operator |=;
        using cmc_rw::operator &=;

        bit_rw_t<cmc_rw, 7> EXCLK;    ///< 高速システム・クロック端子の動作モード
    };
    static cmc_rw_t CMC;
}

これらの処理を簡潔に行う為に、色々なユーティリティ-を実装したが、その一つが、
「text_edit」クラスで、この実装で、エディターで行うような操作をプログラムから
行う事が出来るようになった。
まだ、通常のエディターが備えている機能には程遠いので、これから、ボチボチ機能を
追加したい。
この実装、設計は、なかなか楽しい!

これらソースコード一式は、GitHub にある。
iod_make

RL78/G13スタートアップルーチンと起動テスト

フラッシュへの書き込みプログラムを先に作ろうと思ったけど、まず、動かしてみない
事には始まらない~

gcc はビルドしてあるので、スタートアップルーチンを作って起動するまでの道を作る。

(1)まず、リンカースクリプトを精査する。

/usr/local/rl78-elf/rl78-elf/lib/rl78.ld

を雛形にして改造すれば良さそうだ~
※標準のリンカースクリプトは、RL78/G13 (Flash:64K / RAM:4K) 仕様のようだ。
このリンカースクリプトを参考に、4つのデバイス用を作成
※オリジナルでは、0x0000 ~ 0x1FFF までは、開発用のブート領域のようで、使わない
ので、それを無効にした。

    .rodata (MAX(__romdatastart + __romdatacopysize, 0x2000)) : {

    .rodata (MAX(__romdatastart + __romdatacopysize, 0x0000)) : {

追記:
この0x2000は、ミラー領域内で、データフラッシュ領域をバイパスするオフセットで、
重要な事が判明、又、128KB、256KB品では、データフラッシュ領域は、倍なので、
オフセットを0x3000にする必要がある・・

※「ミラー領域」の事が良く判ってなかった・・・

また、ハードウェアーベクターセクションに多少の問題がある。

  .vec :
  {
    *(.vec)
  } > VEC

通常、ハードウェアーベクターはソフトウェアーからは参照されないので、何もしないと、
最適化で、省かれてしまう・・・

  .vec :
  {
    KEEP(*(.vec))
  } > VEC

そこで、「KEEP」キーワードで囲む必要がある。
※「ivec」(割り込みベクターテーブル)も同じ。

また、各種デバイス用に、ROM、RAM の開始アドレス、長さなど設定して、4つのデバイス用
リンカースクリプトを作成した。

R5F100LCAFB:   32K (0x00000 - 0x07FFF) /  2K (0xFF700 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LEAFB:   64K (0x00000 - 0x0FFFF) /  4K (0xFEF00 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LGAFB:  128K (0x00000 - 0x1FFFF) / 12K (0xFCF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)
R5F100LJAFB:  256K (0x00000 - 0x3FFFF) / 20K (0xFAF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)

RL78/G13リンカースクリプト
※データフラッシュ領域は記述が無いので、何らかの対応を行う必要があると思う。

(2)次にスタートアップルーチン
今までは、独自に、スタックをセットするとか、アセンブラで書いたのだけど、ライブラリー
に含まれる標準のスタートアップオブジェクト「crt0.o」を、objdump でアセンブルソースを
出力して、参考にする方法が確実で簡単な事が判った。

rl78-elf-objdump -h -S crt0.o > crt0.lst

※これなら、まねるだけなので、アセンブラを詳細に理解する必要がほぼ無い。
※最低限の知識は必要だが、手本があれば、非常に簡単だ。
「crt0.o」は、以下の構成のようだ。

・ハードウェアースタックの設定
※スタックの開始アドレスは、リンカースクリプトで指定されたラベルを使う、また、
スタックの深さは、リンカースクリプトで指示する。(通常は、RAM 領域の最後から取る)
・ROM 領域から RAM 領域への転送
※定数などを読み書き可能な変数として設定している場合は、変数はRAM上に配置されるの
で、初期値をコピーしておく必要がある。
・.bss セクションのクリア
・C++ コンストラクター呼び出し
※rl78_run_preinit_array()、rl78_run_init_array()、rl78_run_fini_array()
※C++ では、main が始まる前にコンストラクターを走らせて、初期化しておく必要性がある。
・init 関数呼び出し
・main 関数呼び出し
・exit 関数呼び出し

とりあえず、適等に切り貼りして、「start.s」を作成。
呼び出し部は、「init.c」で実行。
最後に、リセットベクターに、start.sの開始アドレスを指示する必要がある。「vect.c」

const void* vec_[] __attribute__ ((section (".vec"))) = {
    start,
};

これで、全て準備が整った、後は、自分のプログラムをコンパイルして、先に作ったプログラムを
リンクするだけで起動するはず。

-nostartfiles    ----->    標準のスタートファイルを使わない
-T xxxxx    ----->    xxxxx のリンカースクリプトを使う

リンカーオプションでは、以上の二つが重要となる。

(3)LED点滅を書いて、実行してみる・・
以上で、main 関数が実行される準備が整った。
今回、LEDを接続するポートとして、P43を使った。
最初、LEDは点灯したままだったが、無効ループが短すぎたようだ、そこで、以下のように無効
ループの回数を増やした。
※「int」型は16ビットなので、「uint32_t」を使った。

int main(int argc, char* argv[])
{
    device::PM4.B3 = 0;  // output

    bool f = false;
    while(1) {
        for(uint32_t i = 0; i < 100000; ++i) {
	    asm("nop");
        }
        device::P4.B3 = f;
        f = !f;
    }
}

※無効ループ内では、「asm("nop")」を実行する、そうしないと最適化で、ループ自体が無くな
ってしまう。

これで、無事にLEDの点滅まで出来た。

プロジェクト全体のソースコードは、GitHub に全てある、

  cd FIRST_test
  make

で、実行バイナリー、「first_sample.mot」が出来るので、これを、「Flash Programmer」でデバイス
に書き込む。
※書き込み後、リセットが有効になっているので、書き込み機のリセットラインをオフラインにする
必要がある。

IMG_0798s

RL78を始める~ gcc の構築から~

R8C/M120 は確かにコスパが高く、パッケージも手頃で、良いのだけど、RAM容量が少なく、もう少しだけリッチなマイコンが欲しいと思っていたのだった・・・
※メモリーはせめて2Kバイトくらいは欲しい、128×64のビットマップLCDとかを扱う場合、フレームメモリーで1Kバイト消費するし、SDカードでファイルを扱う場合も、バッファ
がある程度必要だし・・

現在の自分の環境では、R8C の上位は、いきなり RX になってしまう、まぁそれはそれで良いのかもしれないけど、RX では高機能過ぎるし、デバイスが少しコスト高なので、AVR 328 くらいのデバイスも扱えれば良いなぁーって思っていた。

R8C/M120 の上位を使えば、もう少し話は早いけど、R8C 系は、既に古いデバイスで、上位のデバイスは割安感が無い、そこで、やはりと言うか、RL78 に落ち着く訳だけど、値段、デバイスの種類、機能など、バランスが良い事に改めて気づく。
ただ、自作で使っている人は少ないのか、製作記時は少ないようだ・・
※パッケージが基本、フラットパッケージのみなので敬遠されているのかもしれない。

何故、ARM を選択しないのかと思うかもしれないが、日本人だからルネサスを使うだけの事で、少し意地になっているかもしれないけど、ルネサスのラインナップは豊富だし、入手性も良く、値段もこなれていて安定している、マニュアルも日本語なので、開発のハードルは低いと感じている。
逆に、「日本人」なのに、何故海外製のマイコンに走るのか、聞いてみたい。
※ AVR は随分使ったけど、マイクロチップに買収されてしまったしなぁ・・

RL78 のラインナップは凄まじく多くて、どれを使うか非常に迷うけど、入手性と値段で、「G13」グループをとりあえず選択してみた。
「秋月電子」で安く購入出来る。

プログラムフラッシュ/RAM/データフラッシュ
・R5F100LCAFB:  32K/ 2K/4K @250円
・R5F100LEAFB:  64K/ 4K/4K @290円
・R5F100LGAFB: 128K/12K/8K @340円
・R5F100LJAFB: 256K/20K/8K @400円
・R5F100LGAFB搭載変換モジュール     @420円

早速、290円、340円を数個購入してみた、後で気がついたけど、340円のデバイスが、変換基板にハンダ付けされたタイプが420円で売られていた、これは安い!
※0.5mmピッチのハンダ付けはコツがいるので、自信の無い人は、モジュールを選べば良いだろう~
IMG_0795s
※変換基板にピンを立てて、ソケット化すると、交換が出来て便利ではあるけど、ソケットのコストが痛いので、このような安いデバイスは、直で、ユニバーサル基板に乗せている。

まず gcc を構築してみた、以前に R8C、RX で行った方法がそのまま使える。
※ gcc-4.9.3 を使った。
詳しい方法は、以下のリンクを参照して欲しい。
My GitHub RL78

次に、フラッシュプログラミング環境を整える。
最初、シリアルポートで簡単に接続出来ると思ったのだが、ドキュメントを読むと、多少の付加回路が必要な事が判った。
RL78 には、プログラミング時の専用端子「TOOL0」があり、「/RESET」のタイミングで、通信を行う事で、プログラミングモードに移行する。
※通常動作では、抵抗を介してプルダウンしておく。

・「ルネサスの参考回路」
リンクの参考回路(タイプB)では、トライステートバッファ(オープンドレインゲートとして利用)と、インバーターを使っている、少し考えて、トライステートをダイオードで置き換え、
インバーターをNチャネルのFETで置き換え、簡易回路を作成してみた。
RL78_FlashProgrammer
※自分が使った部品は、3.3V~5Vの環境に対応している、電圧降下が少ないショットキーダイオードを使ったが、リークの少ない物を使用している。
※/RESET のプルアップ抵抗は、デバイスに直接取り付けてあるので、省く。

※同じような変換回路を既に製作していた~ 「簡易UART書き込み器」

RL78/G13のデバイスは電源が少なくて、配線が楽だ!
・Vss、Vdd、EVss、EVddに0.1uFのパスコンを付け電源に接続する。
・REGCは0.47~1uFのコンデンサでVssに接続する。
※1uFを選択した。

IMG_0796s

とりあえず、「ルネサスの Flash Programmer 」と接続して、認識できる事を確認出来た。
※3.3Vの電源を接続

今回はここまで・・
次は、書き込みプログラムを実装してみようと思う。(MacBook で使いたい!)

RX マイコン用リンカースクリプトとスタートアップ

RX マイコン用 gcc を更新したので、リンカースクリプトも更新してみた。

gcc のリンカースクリプトの記述仕様は「不明」の部分が多く、それでも、C 言語
なら、そう問題にならないが、C++ の場合、事前準備が必要なので、そう簡単では
無いと思える。
また、gcc の新しい機能として、新規のセクションが必要な場合もあるかもしれず、
gcc のバージョンを変更したら、その gcc がインストールされたディレクトリーに
ある基準となるリンカースクリプトをベースにしておく必要がある。

組み込みマイコン用に、自前で gcc をビルドして使っている人の中には、かなり
昔に作られた、乖離したリンカースクリプトを使っている場合もあるようだけど、
これは、少し問題がある、何か新しい機能が新設された場合、ライブラリーが正しく
動作する保障は無いと言える。
※ C 言語のみの場合は、あまり問題にならない場合が多いかもしれないけど・・

自分の環境では:(/usr/local/rx-elf)
gcc 付属のリンカースクリプトは、「/usr/local/rx-elf/rx-elf/lib/rx.ld」にあり、
それを、基準にしている。

・「rx.ld」を観ていこう~
(1) これは、RAM、ROM領域、及びスタックを定義している、自分が使うマイコンの
メモリーマップに従って、領域を設定する。
※スタックは、下位アドレスに向かって消費するので、「LENGTH」に大きな意味は無いものと
思う。

/* This memory layout corresponds to the smallest predicted RX600 chip.  */
MEMORY {
	RAM (w)   : ORIGIN = 0x00000000, LENGTH = 0x00020000 /* 128k */
	STACK (w) : ORIGIN = 0x00020000, LENGTH = 16 /* top of RAM */

	ROM (w)   : ORIGIN = 0xfff40000, LENGTH = 0x000bffd0 /* 768k */
/* This is the largest RX6000: */
/*	ROM (w)   : ORIGIN = 0xffe00000, LENGTH = 0x001fffd0 */ /* 2Mb */
}

(2) このセクションは、プログラムコードが収められている。

  .text :
  {
    PROVIDE (_start = .);
    *(.text P .stub .text.* .gnu.linkonce.t.*)
    KEEP (*(.text.*personality*))
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
    *(.interp .hash .dynsym .dynstr .gnu.version*)
    PROVIDE (__etext = .);
    PROVIDE (_etext = .);
    PROVIDE (etext = .);
    . = ALIGN(4);
    KEEP (*(.init))
    KEEP (*(.fini))
  } > ROM

(3) このセクションは、読み出し専用データが収められている。

  .rodata : {
    . = ALIGN(4);
    *(.plt)
    *(.rodata C C_2 C_1 W W_2 W_1 .rodata.* .gnu.linkonce.r.*)
    *(.rodata1)
    *(.eh_frame_hdr)
    KEEP (*(.eh_frame))
    KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
    . = ALIGN(4);
    PROVIDE(__romdatastart = .);
  } > ROM

(4) このセクションは、起動時、RAM にコピーしておく必要のあるデータなどで、ROM 領域
に配置する。
※「__romdatacopysize」が定義されている。
この中で、「.ctors」、「.dtors」と二つの重要な部分がある、これは、アプリケーション
を起動する前に準備しておく必要のあるコンストラクター、及び、アプリケーション終了時
に呼び出す、デストラクター関係である。
※通常組み込みでは、アプリケーションは終了しないので、デストラクターは呼び出す必要
が無い、もちろん、複数のアプリケーションを動的に呼び出すような機構を備えたシステム
では、デストラクターを呼び出す事は、必須であるけど。

  .data : {
    . = ALIGN(4);
    PROVIDE (__datastart = .); /* IF_ROROM */
    PROVIDE (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE (__preinit_array_end = .);
    PROVIDE (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    PROVIDE (__init_array_end = .);
    PROVIDE (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE (__fini_array_end = .);
    LONG(0); /* Sentinel.  */

    /* gcc uses crtbegin.o to find the start of the constructors, so
       we make sure it is first.  Because this is a wildcard, it
       doesn't matter if the user does not actually link against
       crtbegin.o; the linker won't look for a file to match a
       wildcard.  The wildcard also means that it doesn't matter which
       directory crtbegin.o is in.  */
    KEEP (*crtbegin*.o(.ctors))

    /* We don't want to include the .ctor section from from the
       crtend.o file until after the sorted ctors.  The .ctor section
       from the crtend file contains the end of ctors marker and it
       must be last */
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))

    KEEP (*crtbegin*.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    KEEP (*(.jcr))
    *(.data.rel.ro.local) *(.data.rel.ro*)
    *(.dynamic)

    *(.data D .data.* .gnu.linkonce.d.*)
    KEEP (*(.gnu.linkonce.d.*personality*))
    SORT(CONSTRUCTORS)
    *(.data1)
    *(.got.plt) *(.got)

    /* We want the small data sections together, so single-instruction offsets
       can access them all, and initialized data all before uninitialized, so
       we can shorten the on-disk segment size.  */
    . = ALIGN(4);
    *(.sdata .sdata.* .gnu.linkonce.s.* D_2 D_1)

    . = ALIGN(4);
    _edata = .;
    PROVIDE (edata = .);
    PROVIDE (__dataend = .);
  } > RAM AT> ROM

  /* Note that __romdatacopysize may be ZERO for the simulator, which
     knows how to intialize RAM directly.  It should ONLY be used for
     copying data from ROM to RAM; if you need to know the size of the
     data section, subtract the end symbol from the start symbol.  */
  /* Note that crt0 assumes this is a multiple of four; all the
     start/stop symbols are also assumed long-aligned.  */
  PROVIDE (__romdatacopysize = SIZEOF(.data));

(5) このセクションは、変数などの領域、通常、起動時に「0」クリアしておく。

  .bss : {
    . = ALIGN(4);
    PROVIDE (__bssstart = .);
    *(.dynbss)
    *(.sbss .sbss.*)
    *(.bss B B_2 B_1 .bss.* .gnu.linkonce.b.*)
    . = ALIGN(4);
    *(COMMON)
    . = ALIGN(4);
    PROVIDE (__bssend = .);
    _end = .;
    PROVIDE (end = .);
  } > RAM
  PROVIDE (__bsssize = SIZEOF(.bss) / 4);

(6) このセクションは、スタック領域
RX マイコンの場合、スタックは2つあり、ハードウェアー依存のスタック(主に、割り込み
発生の場合などで、ステータスやレジスターを退避する)と、ユーザー領域依存のスタックが
ある。

  .stack (ORIGIN (STACK)) :
  {
    PROVIDE (__stack = .);
    *(.stack)
  }

(7) このセクションは、ハードウェアー依存のベクターテーブル専用領域。
※RX マイコンでは、「リセット」信号がアサートされると、「_start」から起動する。
※他に、ハードウェアー依存の例外が発生した場合に起動するベクターを記述する。

  /* Providing one of these symbols in your code is sufficient to have
     it linked in to the fixed vector table.  */

  PROVIDE (__rx_priviledged_exception_handler = 0x00000000);
  PROVIDE (__rx_access_exception_handler = 0x00000000);
  PROVIDE (__rx_undefined_exception_handler = 0x00000000);
  PROVIDE (__rx_floating_exception_handler = 0x00000000);
  PROVIDE (__rx_nonmaskable_exception_handler = 0x00000000);

  .vectors (0xFFFFFFD0) :
  {
    PROVIDE (__vectors = .);
    LONG (__rx_priviledged_exception_handler);
    LONG (__rx_access_exception_handler);
    LONG (0);
    LONG (__rx_undefined_exception_handler);
    LONG (0);
    LONG (__rx_floating_exception_handler);
    LONG (0);
    LONG (0);
    LONG (0);
    LONG (0);
    LONG (__rx_nonmaskable_exception_handler);
    LONG (_start);
  }

・それでは、「_start」関数から、「main」までにどのような手順を踏んだら良いのか?
同じディレクトリーに、「crt0.o」がありこのコードが基準となる。
このオブジェクトは、既にバイナリーになっているので、objdump などで、逆にアセン
ブリコードに戻す事で、内容を確認出来る。

  rx-elf-objdump -S crt0.o

crt0.o:     file format elf32-rx-le

Disassembly of section P:

00000000 <_start>:
	.text

	.global _start
_start:
.LFB2:
	mvtc	#0, psw
   0:	fd 77 00 00                   	mvtc	#0, psw
	/* Enable the DN bit - this should have been done for us by
           the CPU reset, but it is best to make sure for ourselves.  */	
	mvtc    #0x100, fpsw
   4:	fd 7b 03 00 01                	mvtc	#256, fpsw
   9:	fb 02 00 00 00 00             	mov.l	#0, r0
	mov	#__stack, r0
   f:	fd 73 0c 00 00 00 00          	mvtc	#0, intb
	mvtc	#__vectors, intb
  16:	fb 12 00 00 00 00             	mov.l	#0, r1

	mov	#__datastart, r1
  1c:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__romdatastart, r2
  22:	fb 32 00 00 00 00             	mov.l	#0, r3
	mov	#__romdatacopysize, r3
	smovf
  28:	7f 8f                         	smovf
  2a:	fb 12 00 00 00 00             	mov.l	#0, r1

	mov	#__bssstart, r1
	mov	#0, r2
  30:	66 02                         	mov.l	#0, r2
  32:	fb 32 00 00 00 00             	mov.l	#0, r3
	mov	#__bsssize, r3
	sstr.l
  38:	7f 8a                         	sstr.l
  3a:	fb d2 00 00 00 00             	mov.l	#0, r13
	/* Initialise the constant data pointer and small data pointers.  */
	mov	#__pid_base, r13
	mov	#__gp, r12
#else
	/* Initialise the small data area pointer.  */
	mov	#__gp, r13
  40:	05 00 00 00                   	bsr.a	40 <_start+0x40>
	mov	# _start, r1
	mov	# _etext, r2
	bsr.a	__monstartup
#endif

	mov	#0, r1 /* argc */
  44:	66 01                         	mov.l	#0, r1
	mov	#0, r2 /* argv */
  46:	66 02                         	mov.l	#0, r2
	mov	#0, r3 /* envv */
  48:	66 03                         	mov.l	#0, r3
  4a:	05 00 00 00                   	bsr.a	4a <_start+0x4a>
	bsr.a	_main
  4e:	05 00 00 00                   	bsr.a	4e <_start+0x4e>

00000052 <_rx_run_preinit_array>:
        mov      r1, r13       ; Save return code.
	bsr.a	__mcleanup
        mov     r13, r1
#endif

	bsr.a	_exit
  52:	fb 12 00 00 00 00             	mov.l	#0, r1

	.global	_rx_run_preinit_array
	.type	_rx_run_preinit_array,@function
_rx_run_preinit_array:
	mov	#__preinit_array_start,r1
  58:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__preinit_array_end,r2
  5e:	04 25 00 00                   	bra.a	83 <_rx_run_inilist>

00000062 <_rx_run_init_array>:
	bra.a	_rx_run_inilist
  62:	fb 12 00 00 00 00             	mov.l	#0, r1

	.global	_rx_run_init_array
	.type	_rx_run_init_array,@function
_rx_run_init_array:
	mov	#__init_array_start,r1
  68:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__init_array_end,r2
	mov	#4, r3
  6e:	66 43                         	mov.l	#4, r3
  70:	04 13 00 00                   	bra.a	83 <_rx_run_inilist>

00000074 <_rx_run_fini_array>:
	bra.a	_rx_run_inilist
  74:	fb 22 00 00 00 00             	mov.l	#0, r2

	.global	_rx_run_fini_array
	.type	_rx_run_fini_array,@function
_rx_run_fini_array:
	mov	#__fini_array_start,r2
  7a:	fb 12 00 00 00 00             	mov.l	#0, r1
	mov	#__fini_array_end,r1
	mov	#-4, r3
  80:	fb 36 fc                      	mov.l	#-4, r3

00000083 <_rx_run_inilist>:
	/* fall through */

_rx_run_inilist:
next_inilist:
	cmp	r1,r2
  83:	47 12                         	cmp	r1, r2
  85:	20 17                         	beq.b	9c 
	beq.b	done_inilist
	mov.l	[r1],r4
  87:	ec 14                         	mov.l	[r1], r4
	cmp	#-1, r4
  89:	75 04 ff                      	cmp	#-1, r4
  8c:	20 0c                         	beq.b	98 
	beq.b	skip_inilist
	cmp	#0, r4
  8e:	61 04                         	cmp	#0, r4
  90:	20 08                         	beq.b	98 
	beq.b	skip_inilist
	pushm	r1-r3
  92:	6e 13                         	pushm	r1-r3
	jsr	r4
  94:	7f 14                         	jsr	r4
	popm	r1-r3
  96:	6f 13                         	popm	r1-r3

00000098 :
skip_inilist:
	add	r3,r1
  98:	4b 31                         	add	r3, r1
  9a:	2e e9                         	bra.b	83 <_rx_run_inilist>

0000009c :
	bra.b	next_inilist
done_inilist:
	rts
  9c:	02                            	rts

Disassembly of section .fini:

00000000 <__rx_fini>:
	.text

	.global _start
_start:
.LFB2:
	mvtc	#0, psw
   0:	05 00 00 00                   	bsr.a	0 <__rx_fini>

00000003 :
	...

ここから、必要な部分をコピーして、また、自分のシステムに必要な部分を追加して、スタートアップ
ルーチンを構築するのが良いと思う。

で、RX63T(RAM: 8KB, ROM: 64KB) 用のリンカースクリプトと、スタートアップルーチンを作成してみた。

RX63T 用リンカースクリプト
スタートアップルーチン「start.s」
初期化関数「init.c」
※自分の実装では、「スタートアップ」は、「start.s」と「init.c」に分けている。

RXマイコン用、フラッシュ書き込みプログラム(RX63T)

先日、gcc-4.9.3 を使って、R8C の開発環境を整えた。

RXマイコンにおいても、gcc-4.9.3 で、構築でき、そのコンパイラでビルド
したプロジェクトも問題無く動作する事を確認した。

以前に R8C 用のフラッシュ書き込みプログラムを作成して、もはや、ルネサス
のツールを一切利用する事なく、オープンソースだけで開発出来るようになった。
※Mac-Book でも、R8C マイコンの開発が可能となった。

RXマイコン用のフラッシュ書き込みツールも、ブート時のプロトコルが公開さ
れている為、実装は可能だと思っていた。
そこで、RX63T のハードウェアーマニュアルに記載されたプロトコルを理解しな
がら、少しづつ、機能を実装して、書き込みまで確認出来たので、とりあえず公開
する。
同じルネサスでも、R8C とは、構成が違うので、基本的な部分以外は書き直しとな
った。
※RX63T の仕様を元にしているが、他のRXシリーズにも、そのまま、又は少しの
修正で使えると思う。

いつものように GitHub に公開している。
※R8C のソースと重複するソースコードが含まれているが、多少更新している。
そのうち、重複する部分は、別にまとめておきたいと思う。

-----
まず参考にした資料は、「RX63Tグループ ユーザーズマニュアル ハードウェア編」
で、ルネサスからダウンロードできる。

細かい仕様は、マニュアル「40.8 ブートモード」に詳細に書かれているので、それを
理解すれば、誰でも実装する事が出来る。
16ビットや32ビットの値を送る場合や、読み込む場合は、ビッグ・エンディアン
になっているようだ。
※これは、マニュアルには書かれていなかった。

現在使っている、デバイスは、RX63Tの64ピン版で、USBを持たないので、
シリアル通信に限定している。

また、コードプロテクト機能を使わない場合でも、フラッシュ書き込みモード(P/E)
に移行すると、内部フラッシュは自動で消去されてしまう為、内部フラッシュの状態を
読み出す必要がある場合は、必ず、プロテクトIDを設定する必要があるようだ。

ソースコードをマルチプラットホームにしたいので、RS232C の入出力は、「termios.h」
を使って実現しているので、MSYS2 環境でコンパイルする必要があり、また、boost を
使っている為、「/usr/local/」以下に boost のソースを配置しておく必要がある。
※ライブラリーを使っていないのでヘッダーだけあれば良い。
MacOS や Linux でもコンパイルする事が出来ると思う。
※現状では「termios.h」は、MSYS2 環境にのみ存在する。
※ MSYS2 環境では、boost はインストーラーで管理していないみたい。
※ボーレートの設定範囲は、環境に依存する様なので、どの環境でもサポートされている
ボーレートのみを使っている為、上限は、115200 となっている。

IMG_0782

フラッシュへの書き込みは、必ず256バイトのブロック毎に行う仕様で、ベリファイの
コマンドは無いので、比較を行う場合は、P/E モード中に読み出して、比較する必要がある。

コードプロテクト行った場合は、フラッシュの消去は自動では無いので、書き込む前に消去
を行う必要がある。

まだ、未完成ではあるが、とりあえず、RX63T に、書き込めるようになったので、とりあえず
公開しておく。

R8C 用コンパイラの構築 gcc-4.9.3

Windows の開発環境で、MSYS2 がメインになってから、
長い間、最新の gcc で m32c(R8C)のコンパイラの構築に失敗していた。
コンパイラの構築には時間がかかる為、オプションの影響などもあまり正確に認識していな
かった。
休みに入ったので、コンパイラと格闘してみた結果、
gcc-5.2.0 で、m32c 用 gcc の構築に成功したので、「要点」を書いておこうと思う。
gcc-5.2.0 の構築は出来たものの、バイナリーを作成してデバイスに書き込んでも、正常に動作しなかったので、
とりあえず、動作が確認されているバージョン(gcc-4.8.3、gcc-4.9.3)を使う。

※「gcc-4.7.4」は現状では、MSYS2 のカレントのコンパイラが 4.9 系では、正常にコンパイル
できない~
いつもながら、クロス gcc の構築には、苦労する・・・

Windows、OS-X で試したので、多分再現性は高いと思う。

Windows では、MSYS2 環境で行い、OS-X では、macport で行う。
基本的に、MSYS2 環境の方が、多少ハードルが高いけど、MSYS2 は、ツールのインストール
が pacman で出来るので、以前より簡単となっている。

まず、MSYS2をインストールする。
※詳しいインストールの方法は、ネットで探すと沢山出てくるので、参考にされたい。
※MSYS2には、3つの実行環境があり、今回は、「msys2_shell.bat」環境で行う。
・まず、システムをアップデート

% update-core

完了したら、ターミナルをクローズして、再度ターミナルを開き、残りを更新。

% pacman -Su

次に、ビルドに必要なツール類をインストールする。(MSYS2)

% pacman -S gcc
% pacman -S texinfo
% pacman -S mpc-devel
% pacman -S diffutils
% pacman -S automake

※他にもあるかもしれないので、適宜インストールする。
※もし、何かツールが無くてコンパイルに失敗したら、ビルドディレクトリーを消して、
「configure」からやり直すのが「確実」だと思う。

・ソースコードのアーカイブを取っておく、今回は以下の組み合わせで行った。
※「gcc-4.8.3」を使う場合は、「4.9.3」を「4.8.3」に読み替えて行う。

    binutils-2.25.1.tar.gz
    gcc-4.9.3.tar.gz
    newlib-2.2.0.tar.gz

・binutils-2.25.1 の構築

% cd
% tar xfvz binutils-2.25.1.tar.gz
% cd binutils-2.25.1
% mkdir m32c_build
% cd m32c_build
% ../configure --target=m32c-elf --prefix=/usr/local/m32c-elf --disable-nls
% make
% make install

・「/usr/local/m32c-elf/bin」へPATHを通して、シェルを起動しなおす。

・C コンパイラの構築
※少し古い gcc のソースコードでは、MSYS2 環境を認識する定義が無い為、「automake-1.15」の
設定をコピーする。(その時の最新をコピーすれば良いと思われる、今回は 1.15 を使った)

% cd
% tar xfvz gcc-4.9.3.tar.gz
% cd gcc-4.9.3
% cp /usr/share/automake-1.15/config.guess .
% mkdir m32c_build
% cd m32c_build
% ../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c --disable-libssp --with-newlib --disable-nls --disable-threads --disable-libgomp --disable-libmudflap --disable-libstdcxx-pch --disable-multilib --disable-bootstrap
% make
% make install

・newlib-2.2.0 の構築

% cd
% tar xfvz newlib-2.2.0.tar.gz
% cd newlib-2.2.0
% mkdir m32c_build
% cd m32c_build
% ../configure --target=m32c-elf --prefix=/usr/local/m32c-elf
% make
% make install

・C++ コンパイラの構築

% cd
% cd gcc-4.9.3
% cd m32c_build
% ../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c,c++ --disable-libssp --with-newlib --disable-nls --disable-threads --disable-libgomp --disable-libmudflap --disable-libstdcxx-pch --disable-multilib --disable-bootstrap
% make
% make install

エラー無く終了すれば、完了!

r8cprog の構築も MSYS2 で出来るので、これでやっと R8C の開発環境が全て MSYS2 で完結する~

一応、I2C_RTC_test をビルドして、M120 に書き込んでテストしてみた。
※問題なく動作しているようである。

※「configure」で、「m32c」を「rx」に変更すれば、RX マイコン用 gcc が構築出来る。
※ rx マイコン用 gcc の構築では、4.9.3 を使う。(4.8.3 は C コンパイラの構築中エラーで止まる)

参考リンク:
最低限のクロスコンパイラの作り方
MSYS2における正しいパッケージの更新方法