GR-SAKURA で遊んでみる(3) GNU toolchain による開発環境の構築

前回 LED の点滅プログラムを書いたものの、Renesas 製のツールチェインにてビルドすることを前提としたもので、Windows PC でないとビルドできない。

普段は NetBSDLinux を使用しているので、ちょっとしたことを GR-SAKURA で試すのにもわざわざ Windows PC を起動してビルドするのは面倒である。

そこで GNU toolchain による RX 向けのクロス開発環境を整備した。ソースからのビルド手順は ARM や SH 等の他アーキテクチャの場合と同様であり、特に難しいものではない。しかし一からやり直すとなると、地道な試行錯誤を繰り返す羽目になるので、備忘録を兼ねて以下に手順をまとめておく。なお、この手順は NetBSD 6.1.2、Linux(Slackware-14.0)、MinGW(Windows XP) にて試行した*1

binutils のインストール

RX 向けの as(1)、ld(1)、objdump(1)は、binutils をインストールすることで使用可能になる。

後述する gcc(1) と異なり、binutils は多倍長/浮動小数点に関するライブラリ群には依存しない。binutils のソースの入手、ビルドおよびインストールの手順を以下に示す。

$ wget ftp://ftp.jaist.ac.jp/pub/GNU/binutils/binutils-2.24.tar.gz
$ tar zxvf binutils-2.24.tar.gz
$ cd binutils-2.24
$ ./configure --prefix=/usr/local/rx-elf --target=rx-elf
$ make
# make install

※ # で始まる行は root 権限で行う必要があることを示す(MinGW 環境下では、特に root 権限を意識する必要はない)

binutils の動作確認結果:

$ cat > loop.s 
        .text
        .globl  _start

_start:
        nop
        nop
        nop
        bra     .

        .end
(Ctrl-D を入力)
$ rx-elf-as -o loop.o loop.s
$ rx-elf-objdump -Dx loop.o 

loop.o:     file format elf32-rx-le
loop.o
architecture: rx, flags 0x00000010:
HAS_SYMS
start address 0x00000000
private flags = 0x8:32-bit doubles, no dsp, no pid, RX ABI
Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 P             00000005  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 D_1           00000000  00000000  00000000  00000039  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 B_1           00000000  00000000  00000000  00000039  2**0
                  ALLOC
SYMBOL TABLE:
00000000 l    d  P      00000000 P
00000000 l    d  D_1    00000000 D_1
00000000 l    d  B_1    00000000 B_1
00000000 g       P      00000000 _start



Disassembly of section P:

00000000 <_start>:
   0:   03                              nop
   1:   03                              nop
   2:   03                              nop
   3:   2e 00                           bra.b   3 <_start+0x3>

objdump(1) の出力結果にて、コードに割り当てられたセクション名が(.text ではなく) P になっている点に注目されたい。

RX 向けの as(1) では、単に ".text" と記述すると、割り当てるセクションの名前は .text ではなく P になる*2。同様に擬似命令 ".data" および ".bss" は、それぞれ D_1 および B_1 セクションを割り当てる。もちろん後述の gcc(1) で C のソースをコンパイルした場合、.text/.data/.rodata/.bss の代わに P/D_1/C/B_1 を生成する。

この点は他アーキテクチャ向けの binutils と異なるので、リンカスクリプトを自前で作成する際は注意が必要である。

GCC のインストール

GCCbinutils に加えて、GMP(GNU Multiple Precision Library)、MPFR(GNU Multiple Precision Floating-Point Reliably) および MPC の多倍長/浮動小数点数を扱うライブラリ群に依存している。従って、これらを先にインストールする必要がある。

各ライブラリのソースの入手からインストールまでの手順を以下に示す。

GMP:

$ wget ftp://ftp.jaist.ac.jp/pub/GNU/gmp/gmp-4.3.2.tar.gz
$ tar zxvf gmp-4.3.2.tar.gz
$ cd gmp-4.3.2
$ ./configure && make
# make install
$ make check

MPFR:

$ wget ftp://ftp.jaist.ac.jp/pub/GNU/mpfr/mpfr-2.4.2.tar.gz
$ tar zxvf mpfr-2.4.2.tar.gz
$ cd mpfr-2.4.2
$ ./configure --with-gmp=/usr/local && make
# make install

MPC:

$ wget http://www.multiprecision.org/mpc/download/mpc-0.8.2.tar.gz
$ tar zxvf mpc-0.8.2.tar.gz
$ cd mpc-0.8.2
$ ./configure --with-gmp=/usr/local &&  make
# make install

次に gcc(1) のソースを入手し、ビルドおよびインストールを行う。
ここでは、4.7.x 系のバージョンの内、2014 年 9 月 24 日時点での最新版である 4.7.4 をビルドした。
4.8.x や 4.9.x の場合、ビルドの途中でエラーになる*3

$ wget ftp://ftp.jaist.ac.jp/pub/GNU/gcc/gcc-4.7.4/gcc-4.7.4.tar.gz
$ tar zxvf gcc-4.7.4.tar.gz
$ mkdir gcc-4.7.4.build && cd gcc-4.7.4.build
$ ../gcc-4.7.4/configure --prefix=/usr/local/rx-elf --target=rx-elf --with-gmp=/usr/local --disable-libssp --enable-languages=c
$ LD_LIBRARY_PATH=/usr/local/lib gmake
# gmake install

※ 上記で(make ではなく)gmake を使用しているのは、GCC のソースに含まれる configure が GNU 拡張文法を使用した Makefile を生成することに因る(*BSDユーザランドに含まれる make では解析に失敗する)。LinuxMinGW 環境下では通常 GNU make が標準なので、gmake とせずに make としても問題ない。

GCC 版 LED 点滅プログラム

前回の LED 点滅プログラムを gcc 向けに書き直した。ソース一式は以下に置いている。

http://he4.seikyou.ne.jp/home/tsukimizake/gr-sakura-dev/blink_gcc.tar.gz

各々のソースの内容は以下のとおり:

blink.S:

    1 #define SCKCR                   0x00080020
    2 #define SCKCR2                  0x00080024
    3 #define SCKCR3                  0x00080026
    4 
    5 #define PLLCR_ADDR              0x00080028
    6 #define PLLCR2_ADDR             0x0008002a
    7 #define MOSCCR_ADDR             0x00080032
    8 #define MOSCWTCR_ADDR           0x000800a2
    9 #define PLLWTCR_ADDR            0x000800a6
   10 
   11 #define IR12_ADDR               0x0008701c
   12 #define IPR004_ADDR             0x00087304
   13 #define IER03_ADDR              0x00087203
   14 
   15 #define PRCR_ADDR               0x000803fe
   16 #define MSTPCRA_ADDR            0x00080010
   17 #define MSTPCRB_ADDR            0x00080014
   18 #define MSTPCRC_ADDR            0x00080018
   19 
   20 #define PORT0_PDR_ADDR          0x0008c000
   21 #define PORT1_PDR_ADDR          0x0008c001
   22 #define PORT2_PDR_ADDR          0x0008c002
   23 #define PORT3_PDR_ADDR          0x0008c003
   24 #define PORT4_PDR_ADDR          0x0008c004
   25 #define PORT5_PDR_ADDR          0x0008c005
   26 #define PORT6_PDR_ADDR          0x0008c006
   27 #define PORT7_PDR_ADDR          0x0008c007
   28 #define PORT8_PDR_ADDR          0x0008c008
   29 #define PORT9_PDR_ADDR          0x0008c009
   30 #define PORTA_PDR_ADDR          0x0008c00a
   31 #define PORTB_PDR_ADDR          0x0008c00b
   32 #define PORTC_PDR_ADDR          0x0008c00c
   33 #define PORTD_PDR_ADDR          0x0008c00d
   34 #define PORTE_PDR_ADDR          0x0008c00e
   35 #define PORTF_PDR_ADDR          0x0008c00f
   36 #define PORTG_PDR_ADDR          0x0008c010
   37 #define PORTJ_PDR_ADDR          0x0008c012
   38 #define PORTA_PODR_ADDR         0x0008c02a
   39 
   40 #define CMSTR0_ADDR             0x00088000
   41 #define CMT0_CMCR_ADDR          0x00088002
   42 #define CMT0_CMCNT_ADDR         0x00088004
   43 #define CMT0_CMCOR_ADDR         0x00088006
   44 
   45         .text
   46 
   47         .globl  _start
   48         .globl  intvect
   49         .globl  unhandled_interrupt
   50         .globl  unhandled_exception
   51         .globl  cmt0_interrupt
   52 
   53 _start:
   54         ; disable interrupts
   55         clrpsw  i
   56 
   57         mvtc    #0x00020000, usp
   58         mvtc    #0x0001fc00, isp
   59         mvtc    #intvect, intb
   60 
   61         ; stop unused peripherals
   62         bsr     stop_modules
   63 
   64         ; initialize non-existent I/O port
   65         ; see following:
   66         ;
   67         ;   RX63N Group, RX631 Group
   68         ;   User's Manual:Hardware
   69         ;   21.5.1 Products fewer than 176 Pins
   70         bsr     init_nonexistent_port
   71 
   72         ; enable CMT0
   73         mov.l   #PRCR_ADDR, r1
   74         mov.w   #0xa503, [r1]
   75         mov.l   #MSTPCRA_ADDR, r1       
   76         mov.l   [r1], r2
   77         bclr    #15, r2
   78         mov.l   r2, [r1]
   79 
   80         ; if the main clock stabilization waiting time(tMAINOSC) is 20 ms, 
   81         ; we should set MOSCWTCR.MSTS[4:0] to 14
   82         ; (262,144 cycle > 20.00 ms = 240,000 cycles),
   83         ; so we need to wait
   84         ;   tMAINOSC + (262,144 + 16384)/ fMAIN
   85         ;   = 20 + (262,144 + 16,384) / 12,000 = approx. 43.23 ms
   86         mov.l   #MOSCWTCR_ADDR, r1
   87         mov.b   #0xe, [r1]              ; MOSCWTCR.MSTS[4:0]=14
   88 
   89         ; run the main clock oscillator
   90         mov.l   #MOSCCR_ADDR, r1
   91         bclr    #0, [r1].b              ; MOSCCR.MOSTP=0
   92         ; confirm that MOSCCR.MOSTP has actually been updated
   93 1:      movu.b  [r1], r2
   94         tst     r2, r2
   95         bnz     1b
   96 
   97         mov.l   #44, r1
   98         bsr     mdelay
   99 
  100         ; configure the PLL clock:
  101         ; to generate the PLL clock from main clock by dividing by 1 and
  102         ; multiplying by 8(fPLL = 12.0 * 8 = 96.0 MHz),
  103         ; we should set PLLWTCR.PSTS to 9
  104         ; (n = 65536 cycles = 682.7 us),
  105         ; so we need to wait
  106         ;   tPLL1 + (n + 131,072) / fPLL
  107         ;   = 0.5 + (65,536 + 131,072)/ 96,000 = 2.548 ms
  108         mov.l   #PLLCR_ADDR, r1
  109         mov.w   #0x0700, [r1]
  110         mov.l   #PLLWTCR_ADDR, r1
  111         mov.b   #9, [r1]
  112         mov.l   #PLLCR2_ADDR, r1
  113         bclr    #0, r1                  ; PLLCR2.PLLEN=0
  114 
  115         mov.l   #3, r1
  116         bsr     mdelay
  117 
  118         mov.l   #SCKCR, r1
  119         mov.l   #0x22c23311, [r1]       ; ICLK -> 1/4, PCLK -> 1/8
  120         mov.l   #SCKCR2, r1
  121         mov.w   #0x0002, [r1]
  122         mov.l   #SCKCR3, r1
  123         mov.w   #0x0400, [r1]           ; use PLL clock
  124 
  125         mov.l   #PRCR_ADDR, r1
  126         mov.w   #0xa500,[r1]            ; write protection for clock control registers
  127 
  128         ; turn off user LEDs
  129         mov.l   #PORTA_PODR_ADDR, r1
  130         bclr    #0, [r1].b              ; Set PA0 output level to L
  131         bclr    #1, [r1].b              ; Set PA1 output level to L
  132         bclr    #2, [r1].b              ; Set PA2 output level to L
  133         bclr    #6, [r1].b              ; Set PA6 output level to L
  134         mov.l   #PORTA_PDR_ADDR, r1
  135         bset    #0, [r1].b              ; Set PA0 direction to output
  136         bset    #1, [r1].b              ; Set PA1 direction to output
  137         bset    #2, [r1].b              ; Set PA2 direction to output
  138         bset    #6, [r1].b              ; Set PA6 direction to output
  139 
  140         ; initialize .bss section
  141         mov.l   #__bss_start, r1
  142         mov.l   #0, r2
  143         mov.l   #__bss_end, r3
  144         sub     r1, r3
  145         sstr.b
  146 
  147         mov.l   #CMT0_CMCOR_ADDR, r1
  148         mov.w   #0x05dc, [r1]           ; Set CMCOR
  149         mov.l   #CMT0_CMCR_ADDR, r1
  150         mov.w   #0x0040, [r1]           ; Set CMCR
  151         mov.l   #CMT0_CMCNT_ADDR, r1
  152         mov.w   #0x0000, [r1]           ; Clear CMCNT
  153 
  154         ; enable interrupts
  155         mov.l   #IPR004_ADDR, r1
  156         mov.b   #0x01, [r1]             ; Set IPR004
  157         mov.l   #IER03_ADDR, r1
  158         bset    #4, [r1].b              ; Set IER03.IEN4
  159         setpsw  i                       ; Set I bit
  160 
  161         mov.l   #CMSTR0_ADDR, r1
  162         mov.w   #0x0001, [r1]           ; Start CMT0
  163 
  164         bra     .
  165 
  166 mdelay:
  167         pushm   r1-r3
  168 
  169         mov.l   #CMT0_CMCOR_ADDR, r2
  170         mov.w   #0x0010, [r2]
  171         mov.l   #CMT0_CMCR_ADDR, r2
  172         mov.w   #0x0040, [r2]
  173         mov.l   #CMT0_CMCNT_ADDR, r2
  174         mov.w   #0x0000, 4[r2]
  175 
  176         mov.l   #IPR004_ADDR, r2
  177         bset    #0, [r2].b              ; Set IPR004
  178         mov.l   #IER03_ADDR, r2
  179         bset    #4, [r2].b              ; Set IER03.IEN4
  180 
  181         tst     r1, r1
  182 1:      bz      3f
  183 
  184         mov.l   #CMSTR0_ADDR, r2
  185         mov.w   #0x0001, [r2]           ; Start CMT0
  186 
  187 2:      mov.l   #IR12_ADDR, r2
  188         movu.b  [r2], r3
  189         tst     r3, r3
  190         bz      2b
  191         mov.b   #0x00, [r2]
  192 
  193         mov.l   #CMSTR0_ADDR, r2
  194         mov.w   #0x0000, [r2]           ; Stop CMT0
  195 
  196         sub     #1, r1
  197         bra     1b
  198 
  199 3:      mov.l   #IER03_ADDR, r2
  200         bclr    #4, [r2].b              ; Clear IER03.IEN4
  201         mov.l   #IPR004_ADDR, r2
  202         bclr    #0, [r2].b              ; Clear IPR004
  203 
  204         popm    r1-r3
  205         rts
  206 
  207 stop_modules:
  208         ; disable write protection for low power consumption module
  209         mov.l   #PRCR_ADDR, r1
  210         mov.w   #0xa502,[r1]
  211 
  212         ; stop unused peripherals(DMA and EXDMA)
  213         mov.l   #MSTPCRA_ADDR, r1       
  214         mov.l   [r1], r2
  215         bset    #28, r2                 ; DMA
  216         bset    #29, r2                 ; EXDMA
  217         mov.l   r2, [r1]
  218 
  219         mov.l   #PRCR_ADDR, r1
  220         mov.w   #0xa500, [r1]
  221         rts
  222 
  223 init_nonexistent_port:
  224         ; set non-existent I/O ports' direction to output
  225         push.l  r1
  226         mov.l   #PORT0_PDR_ADDR, r1
  227         mov.b   #0x0f, [r1]             ; P00-P03
  228         mov.l   #PORT1_PDR_ADDR, r1
  229         mov.b   #0x03, [r1]             ; P10-P11
  230         mov.l   #PORT2_PDR_ADDR, r1
  231         mov.b   #0x00, [r1]             ; none
  232         mov.l   #PORT3_PDR_ADDR, r1
  233         mov.b   #0x00, [r1]             ; none
  234         mov.l   #PORT4_PDR_ADDR, r1
  235         mov.b   #0x00, [r1]             ; none
  236         mov.l   #PORT5_PDR_ADDR, r1
  237         mov.b   #0xc0, [r1]             ; P56-P57
  238         mov.l   #PORT6_PDR_ADDR, r1
  239         mov.b   #0xff, [r1]             ; P60-67
  240         mov.l   #PORT7_PDR_ADDR, r1
  241         mov.b   #0xff, [r1]             ; P70-77
  242         mov.l   #PORT8_PDR_ADDR, r1
  243         mov.b   #0xff, [r1]             ; P80-87
  244         mov.l   #PORT9_PDR_ADDR, r1
  245         mov.b   #0xff, [r1]             ; P90-97
  246         mov.l   #PORTA_PDR_ADDR, r1
  247         mov.b   #0x00, [r1]             ; none
  248         mov.l   #PORTB_PDR_ADDR, r1
  249         mov.b   #0x00, [r1]             ; none
  250         mov.l   #PORTC_PDR_ADDR, r1
  251         mov.b   #0x00, [r1]             ; none
  252         mov.l   #PORTD_PDR_ADDR, r1
  253         mov.b   #0x00, [r1]             ; none
  254         mov.l   #PORTE_PDR_ADDR, r1
  255         mov.b   #0x00, [r1]             ; none
  256         mov.l   #PORTF_PDR_ADDR, r1
  257         mov.b   #0x3f, [r1]             ; PF0-PF5
  258         mov.l   #PORTG_PDR_ADDR, r1
  259         mov.b   #0xff, [r1]             ; PG0-OG7
  260         mov.l   #PORTJ_PDR_ADDR, r1
  261         mov.b   #0x20, [r1]             ; PJ5
  262         pop     r1
  263         rts
  264 
  265 unhandled_exception:
  266         ; turn on LED1
  267         mov.l   #PORTA_PODR_ADDR, r1
  268         bset    #0, [r1].b
  269         mov.l   #PORTA_PDR_ADDR, r1
  270         bset    #0, [r1].b
  271         bra     .
  272 
  273 unhandled_interrupt:
  274         ; turn on LED2
  275         mov.l   #PORTA_PODR_ADDR, r1
  276         bset    #1, [r1].b
  277         mov.l   #PORTA_PDR_ADDR, r1
  278         bset    #1, [r1].b
  279         bra     .
  280 
  281 cmt0_interrupt:
  282         pushm   r1-r2
  283         mov.l   #count, r1
  284         mov.l   [r1], r2
  285         tst     r2, r2
  286         bnz     1f
  287         mov.l   #0x01f4, r2
  288 
  289         ; toggle output level
  290         mov.l   #PORTA_PODR_ADDR, r1
  291         bnot    #6, [r1].b
  292 
  293         mov.l   #count, r1
  294 1:      sub     #1, r2
  295         mov.l   r2, [r1]
  296 
  297         popm    r1-r2
  298         rte
  299 
  300 
  301         .bss
  302 
  303 count:  .long   0x00000000
  304 
  305         .end

vect.S:

    1         .section        .intvect, "a"
    2 
    3         .globl  unhandled_interrupt
    4         .globl  cmt0_interrupt
    5         .globl  intvect
    6 
    7         .align  4
    8 intvect:
    9         .long   unhandled_interrupt
   10         .long   unhandled_interrupt
              (中略)
   36         .long   unhandled_interrupt
   37         .long   cmt0_interrupt
   38         .long   unhandled_interrupt
   39         .long   unhandled_interrupt
              (中略)
  264         .long   unhandled_interrupt
  265 
  266 
  267         .section        .fixedvect, "a"
  268         .globl  fixedvect
  269         .globl  unhandled_exception
  270         .globl  _start
  271 
  272         .align  4
  273 fixedvect:
  274         .long   unhandled_exception
  275         .long   unhandled_exception
  276         .long   unhandled_exception
  277         .long   unhandled_exception
  278         .long   unhandled_exception
  279         .long   unhandled_exception
  280         .long   unhandled_exception
  281         .long   unhandled_exception
  282         .long   unhandled_exception
  283         .long   unhandled_exception
  284         .long   unhandled_exception
  285         .long   _start
  286 
  287         .end

※セクション名に続く "a" は、allocatable なセクションであることを示す。これがないと、objcopy(1) による ELF -> S-recrd への変換時に、セクションの内容が破棄されてしまう。

blink.ld(リンカスクリプト):

    1 SECTIONS
    2 {
    3         . = 0x00000000;
    4         __bss_start = .;
    5         B_1 : { *(B_1) }
    6         __bss_end = .;
    7 
    8         . = 0xfff80000;
    9         P : { *(P) }
   10         .intvect : { *(.intvect) }
   11         . = 0xffffffd0;
   12         .fixedvect : { *(.fixedvect) }
   13 }

GNU-toolchain によるアセンブル、リンク、ELF -> S-recrd 変換の手順を以下に示す。

rx-elf-gcc -Wall -c -o blink.o blink.S
rx-elf-gcc -Wall -c -o vect.o vect.S
rx-elf-ld -o blink -T blink.ld blink.o vect.o
rx-elf-objcopy -O srec blink blink.mot

気になった点

double
foo(double a, double b)
{
        return a + b;
}

という C のソースを gcc(1) でコンパイルすると、その結果は

   0:   7e aa                           push.l  r10
   2:   7e a6                           push.l  r6
   4:   71 06 f8                        add     #-8, r0, r6
   7:   ef 60                           mov.l   r6, r0
   9:   e3 61                           mov.l   r1, [r6]
   b:   a0 6a                           mov.l   r2, 4[r6]
   d:   ec 6a                           mov.l   [r6], r10
   f:   fc 89 6a 01                     fadd    4[r6].l, r10
  13:   ef a1                           mov.l   r10, r1
  15:   62 80                           add     #8, r0
  17:   7e b6                           pop     r6
  19:   7e ba                           pop     r10
  1b:   02                              rts

となった。浮動小数点数の加算に対して fadd 命令を使ったコードを生成するということは、RX200 シリーズは対応しないということか。RX 向けの gcc(1) では特に RX200 シリーズ向けのオプションは無かったし...

ともあれ、これで GNU toolchain でビルドできる環境が整った。とはいえ、FDT で S-record のファイルを書き込む際には、結局 Windows PC が必要になるのだが。
# ブートローダがあれば...

*1:MinGWCygwin 上ではファイル I/O が遅く、ビルドにかなりの時間を要する。これらの環境下では、(就寝前など)数時間放置可能な状況下でビルドされることを奨める

*2:もちろん明示的に ".section .text" と記述すれば、セクション名を .text にできる。しかし、C コンパイラが .text/.data/.rodata/.bss ではなく P/D_1/C/B_1 セクションに割り付ける以上、これは避けた方が良さそうだ。

*3:libgcc の configure 中にエラーになっている模様