GR-SAKURA で遊んでみる(2)

先日購入したGR-SAKURA の JTAG I/F の場所にピンヘッダを半田付けした。

これで、E1 エミュレータを用いたデバッグが可能になる。
開発に必要なツール群は、以下の URL より入手した。

【無償評価版】RXファミリ用C/C++コンパイラパッケージ V2 (統合開発環境なし) V2.02.00:
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/evaluation_software_cc_rx.jsp

Renesas E-Series USBドライバ V.1.02 Release 00
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/upgrades_usb_driver_for_e-series.jsp

フラッシュ開発ツールキット V.4.09 Release 02 アップデート
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/evaluation_software_cc_rx.jsp

(OS を搭載したPC 上で)セルフ開発環境の整備後に最初に書くであろうプログラムは

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
        printf("Hello World!\n");
        exit(EXIT_SUCCESS);
}

というものであるが、OS も何も書き込まれていない白紙状態のボードで、新規にコードを書き起こして遊ぶ際には大抵 LED 点滅プログラムを出発点とする。
単に LED を点滅させるだけでも

  • I/Oポート
  • タイマ
  • 割り込み
  • PLL

の機能を試すことができ、後々作成するプログラムにも流用できる。

そこで、LED の点滅を行うプログラムを実際に書いてみた。

blink.src:

  1 SCKCR                   .equ    000080020h
  2 SCKCR2                  .equ    000080024h
  3 SCKCR3                  .equ    000080026h
  4
  5 PLLCR_ADDR              .equ    000080028h
  6 PLLCR2_ADDR             .equ    00008002ah
  7 MOSCCR_ADDR             .equ    000080032h
  8 MOSCWTCR_ADDR           .equ    0000800a2h
  9 PLLWTCR_ADDR            .equ    0000800a6h
 10
 11 IR12_ADDR               .equ    00008701ch
 12 IPR004_ADDR             .equ    000087304h
 13 IER03_ADDR              .equ    000087203h
 14
 15 PRCR_ADDR               .equ    0000803feh
 16 MSTPCRA_ADDR            .equ    000080010h
 17 MSTPCRB_ADDR            .equ    000080014h
 18 MSTPCRC_ADDR            .equ    000080018h
 19
 20 PORT0_PDR_ADDR          .equ    00008c000h
 21 PORT1_PDR_ADDR          .equ    00008c001h
 22 PORT2_PDR_ADDR          .equ    00008c002h
 23 PORT3_PDR_ADDR          .equ    00008c003h
 24 PORT4_PDR_ADDR          .equ    00008c004h
 25 PORT5_PDR_ADDR          .equ    00008c005h
 26 PORT6_PDR_ADDR          .equ    00008c006h
 27 PORT7_PDR_ADDR          .equ    00008c007h
 28 PORT8_PDR_ADDR          .equ    00008c008h
 29 PORT9_PDR_ADDR          .equ    00008c009h
 30 PORTA_PDR_ADDR          .equ    00008c00ah
 31 PORTB_PDR_ADDR          .equ    00008c00bh
 32 PORTC_PDR_ADDR          .equ    00008c00ch
 33 PORTD_PDR_ADDR          .equ    00008c00dh
 34 PORTE_PDR_ADDR          .equ    00008c00eh
 35 PORTF_PDR_ADDR          .equ    00008c00fh
 36 PORTG_PDR_ADDR          .equ    00008c010h
 37 PORTJ_PDR_ADDR          .equ    00008c012h
 38 PORTA_PODR_ADDR         .equ    00008c02ah
 39
 40 CMSTR0_ADDR             .equ    000088000h
 41 CMT0_CMCR_ADDR          .equ    000088002h
 42 CMT0_CMCNT_ADDR         .equ    000088004h
 43 CMT0_CMCOR_ADDR         .equ    000088006h
 44
 45         .section ResetPRG,code
 46         .glb    _start
 47         .glb    intvect
 48 _start:
 49         ; disable interrupts
 50         clrpsw  i
 51
 52         mvtc    #000020000h, usp
 53         mvtc    #00001fc00h, isp
 54         mvtc    #intvect, intb
 55
 56         ; stop unused peripherals
 57         bsr     stop_modules
 58
 59         ; initialize non-existent I/O port
 60         ; see following:
 61         ;
 62         ;   RX63N Group, RX631 Group
 63         ;   User's Manual:Hardware
 64         ;   21.5.1 Products fewer than 176 Pins
 65         bsr     init_nonexistent_port
 66
 67         ; enable CMT0
 68         mov.l   #PRCR_ADDR, r1
 69         mov.w   #0a503h,[r1]
 70         mov.l   #MSTPCRA_ADDR, r1
 71         mov.l   [r1], r2
 72         bclr    #15, r2
 73         mov.l   r2, [r1]
 74
 75         ; if the main clock stabilization waiting time(tMAINOSC) is 20 ms,
 76         ; we should set MOSCWTCR.MSTS[4:0] to 14
 77         ; (262,144 cycle > 20.00 ms = 240,000 cycles),
 78         ; so we need to wait
 79         ;   tMAINOSC + (262,144 + 16384)/ fMAIN
 80         ;   = 20 + (262,144 + 16,384) / 12,000 = approx. 43.23 ms
 81         mov.l   #MOSCWTCR_ADDR, r1
 82         mov.b   #0eh, [r1]              ; MOSCWTCR.MSTS[4:0]=14
 83
 84         ; run the main clock oscillator
 85         mov.l   #MOSCCR_ADDR, r1
 86         bclr    #0, [r1]                ; MOSCCR.MOSTP=0
 87         ; confirm that MOSCCR.MOSTP has actually been updated
 88 ?:      movu.b  [r1], r2
 89         tst     r2, r2
 90         bnz     ?-
 91
 92         mov.l   #44, r1
 93         bsr     mdelay
 94
 95         ; configure the PLL clock:
 96         ; to generate the PLL clock from main clock by dividing by 1 and
 97         ; multiplying by 8(fPLL = 12.0 * 8 = 96.0 MHz),
 98         ; we should set PLLWTCR.PSTS to 9
 99         ; (n = 65536 cycles = 682.7 us),
100         ; so we need to wait
101         ;   tPLL1 + (n + 131,072) / fPLL
102         ;   = 0.5 + (65,536 + 131,072)/ 96,000 = 2.548 ms
103         mov.l   #PLLCR_ADDR, r1
104         mov.w   #00700h, [r1]
105         mov.l   #PLLWTCR_ADDR, r1
106         mov.b   #9, [r1]
107         mov.l   #PLLCR2_ADDR, r1
108         bclr    #0, r1                  ; PLLCR2.PLLEN=0
109
110         mov.l   #3, r1
111         bsr     mdelay
112
113         mov.l   #SCKCR, r1
114         mov.l   #022c23311h, [r1]       ; ICLK -> 1/4, PCLK -> 1/8
115         mov.l   #SCKCR2, r1
116         mov.w   #00002h, [r1]
117         mov.l   #SCKCR3, r1
118         mov.w   #0400h, [r1]            ; use PLL clock
119         mov.l   #0000803feh, r1
120         mov.w   #0a500h,[r1]            ; write protection for clock control registers
121
122         ; turn off user LEDs
123         mov.l   #PORTA_PODR_ADDR, r1
124         bclr    #0, [r1]                ; Set PA0 output level to L
125         bclr    #1, [r1]                ; Set PA1 output level to L
126         bclr    #2, [r1]                ; Set PA2 output level to L
127         bclr    #6, [r1]                ; Set PA6 output level to L
128         mov.l   #PORTA_PDR_ADDR, r1
129         bset    #0, [r1]                ; Set PA0 direction to output
130         bset    #1, [r1]                ; Set PA1 direction to output
131         bset    #2, [r1]                ; Set PA2 direction to output
132         bset    #6, [r1]                ; Set PA6 direction to output
133
134         ; initialize section "B"
135         mov.l   #(TOPOF B), r1
136         mov.l   #0, r2
137         mov.l   #(SIZEOF B), r3
138         sstr.b
139
140         mov.l   #CMT0_CMCOR_ADDR, r1
141         mov.w   #005dch, [r1]           ; Set CMCOR
142         mov.l   #CMT0_CMCR_ADDR, r1
143         mov.w   #00040h, [r1]           ; Set CMCR
144         mov.l   #CMT0_CMCNT_ADDR, r1
145         mov.w   #00000h, [r1]           ; Clear CMCNT
146
147         ; enable interrupts
148         mov.l   #IPR004_ADDR, r1
149         mov.b   #001h, [r1]             ; Set IPR004
150         mov.l   #IER03_ADDR, r1
151         bset    #4, [r1]                ; Set IER03.IEN4
152         setpsw  i                       ; Set I bit
153
154         mov.l   #CMSTR0_ADDR, r1
155         mov.w   #00001h, [r1]           ; Start CMT0
156
157         bra     $
158
159 mdelay:
160         pushm   r1-r3
161
162         mov.l   #CMT0_CMCOR_ADDR, r2
163         mov.w   #00010h, [r2]
164         mov.l   #CMT0_CMCR_ADDR, r2
165         mov.w   #00040h, [r2]
166         mov.l   #CMT0_CMCNT_ADDR, r2
167         mov.w   #00000h, 4[r2]
168
169         mov.l   #IPR004_ADDR, r2
170         bset    #0, [r2]                ; Set IPR004
171         mov.l   #IER03_ADDR, r2
172         bset    #4, [r2]                ; Set IER03.IEN4
173
174         tst     r1, r1
175 L1:     bz      L2
176
177         mov.l   #CMSTR0_ADDR, r2
178         mov.w   #00001h, [r2]           ; Start CMT0
179
180 ?:      mov.l   #IR12_ADDR, r2
181         movu.b  [r2], r3
182         tst     r3, r3
183         bz      ?-
184         mov.b   #000h, [r2]
185
186         mov.l   #CMSTR0_ADDR, r2
187         mov.w   #00000h, [r2]           ; Stop CMT0
188
189         sub     #1, r1
190         bra     L1
191
192 L2:     mov.l   #IER03_ADDR, r2
193         bclr    #4, [r2]                ; Clear IER03.IEN4
194         mov.l   #IPR004_ADDR, r2
195         bclr    #0, [r2]                ; Clear IPR004
196
197         popm    r1-r3
198         rts
199
200 stop_modules:
201         ; disable write protection for low power consumption module
202         mov.l   #PRCR_ADDR, r1
203         mov.w   #0a502h,[r1]
204
205         ; stop unused peripherals(DMA and EXDMA)
206         mov.l   #MSTPCRA_ADDR, r1
207         mov.l   [r1], r2
208         bset    #28, r2                 ; DMA
209         bset    #29, r2                 ; EXDMA
210         mov.l   r2, [r1]
211
212         mov.l   #PRCR_ADDR, r1
213         mov.w   #0a500h, [r1]
214         rts
215
216 init_nonexistent_port:
217         ; set non-existent I/O ports' direction to output
218         push.l  r1
219         mov.l   #PORT0_PDR_ADDR, r1
220         mov.b   #00fh, [r1]             ; P00-P03
221         mov.l   #PORT1_PDR_ADDR, r1
222         mov.b   #003h, [r1]             ; P10-P11
223         mov.l   #PORT2_PDR_ADDR, r1
224         mov.b   #000h, [r1]             ; none
225         mov.l   #PORT3_PDR_ADDR, r1
226         mov.b   #000h, [r1]             ; none
227         mov.l   #PORT4_PDR_ADDR, r1
228         mov.b   #000h, [r1]             ; none
229         mov.l   #PORT5_PDR_ADDR, r1
230         mov.b   #0c0h, [r1]             ; P56-P57
231         mov.l   #PORT6_PDR_ADDR, r1
232         mov.b   #0ffh, [r1]             ; P60-67
233         mov.l   #PORT7_PDR_ADDR, r1
234         mov.b   #0ffh, [r1]             ; P70-77
235         mov.l   #PORT8_PDR_ADDR, r1
236         mov.b   #0ffh, [r1]             ; P80-87
237         mov.l   #PORT9_PDR_ADDR, r1
238         mov.b   #0ffh, [r1]             ; P90-97
239         mov.l   #PORTA_PDR_ADDR, r1
240         mov.b   #000h, [r1]             ; none
241         mov.l   #PORTB_PDR_ADDR, r1
242         mov.b   #000h, [r1]             ; none
243         mov.l   #PORTC_PDR_ADDR, r1
244         mov.b   #000h, [r1]             ; none
245         mov.l   #PORTD_PDR_ADDR, r1
246         mov.b   #000h, [r1]             ; none
247         mov.l   #PORTE_PDR_ADDR, r1
248         mov.b   #000h, [r1]             ; none
249         mov.l   #PORTF_PDR_ADDR, r1
250         mov.b   #03fh, [r1]             ; PF0-PF5
251         mov.l   #PORTG_PDR_ADDR, r1
252         mov.b   #0ffh, [r1]             ; PG0-OG7
253         mov.l   #PORTJ_PDR_ADDR, r1
254         mov.b   #020h, [r1]             ; PJ5
255         pop     r1
256         rts
257
258         .section IntPRG,code
259         .glb    unhandled_interrupt
260         .glb    unhandled_exception
261         .glb    cmt0_interrupt
262
263 unhandled_exception:
264         ; turn on LED1
265         mov.l   #PORTA_PODR_ADDR, r1
266         bset    #0, [r1]
267         mov.l   #PORTA_PDR_ADDR, r1
268         bset    #0, [r1]
269         bra     $
270
271 unhandled_interrupt:
272         ; turn on LED2
273         mov.l   #PORTA_PODR_ADDR, r1
274         bset    #1, [r1]
275         mov.l   #PORTA_PDR_ADDR, r1
276         bset    #1, [r1]
277         bra     $
278
279 cmt0_interrupt:
280         pushm   r1-r2
281         mov.l   #count, r1
282         mov.l   [r1], r2
283         tst     r2, r2
284         bnz     out
285         mov.l   #001f4h, r2
286
287         ; toggle output level
288         mov.l   #PORTA_PODR_ADDR, r1
289         bnot    #6, [r1]
290
291         mov.l   #count, r1
292 out:    sub     #1, r2
293         mov.l   r2, [r1]
294
295         popm    r1-r2
296         rte
297
298         .section B,DATA,align=4
299 count:  .blkl   1
300
301         .end

vect.src:

    1         .section C$VECT,ROMDATA,align=4
    2         .glb    unhandled_interrupt
    3         .glb    cmt0_interrupt
    4         .glb    intvect
    5 
    6 intvect:
    7         .lword  unhandled_interrupt
    8         .lword  unhandled_interrupt
              (中略)
   34         .lword  unhandled_interrupt
   35         .lword  cmt0_interrupt
   36         .lword  unhandled_interrupt
   37         .lword  unhandled_interrupt
              (中略)
  262         .lword  unhandled_interrupt
  263 
  264 
  265         .section FIXEDVECT,ROMDATA,align=4
  266         .glb    fixedvect
  267         .glb    unhandled_exception
  268         .glb    _start
  269 
  270 fixedvect:
  271         .lword  unhandled_exception
  272         .lword  unhandled_exception
  273         .lword  unhandled_exception
  274         .lword  unhandled_exception
  275         .lword  unhandled_exception
  276         .lword  unhandled_exception
  277         .lword  unhandled_exception
  278         .lword  unhandled_exception
  279         .lword  unhandled_exception
  280         .lword  unhandled_exception
  281         .lword  unhandled_exception
  282         .lword  _start
  283 
  284         .end

blink.src がボードの初期化処理および LED の点滅処理を、vect.src が可変ベクタテーブルおよび固定ベクタテーブルの内容を記述したものである。

これらのプログラムを GR-SAKURA 向けにアセンブルする場合、asrx コマンドを -cpu=rx600 オプションを付けて呼び出せばよい*1。以下にアセンブルおよびリンクの具体例を示す。

> asrx -cpu=rx600 blink.src
RX Family Assembler V2.01.00.04 [01 Aug 2013]
Copyright (C) 2008, 2013 Renesas Electronics Corporation
All rights reserved.

( blink.src )
macro processing now
---
assembler processing now
---
TOTAL ERROR(S)    00000
TOTAL WARNING(S)  00000
TOTAL LINE(S)     00301   LINES
CODE     0000000513(00000201H)  ResetPRG
DATA     0000000004(00000004H)  B
CODE     0000000073(00000049H)  IntPRG

> asrx -cpu=rx600 vect.src
RX Family Assembler V2.01.00.04 [01 Aug 2013]
Copyright (C) 2008, 2013 Renesas Electronics Corporation
All rights reserved.

( vect.src )
macro processing now
--
assembler processing now
--
TOTAL ERROR(S)    00000
TOTAL WARNING(S)  00000
TOTAL LINE(S)     00284   LINES
ROMDATA  0000001024(00000400H)  C$VECT
ROMDATA  0000000048(00000030H)  FIXEDVECT

> rlink -S9 -form=stype -start=B/00000000,ResetPRG,IntPRG,C$VECT/fff80000,FIXEDVECT/ffffffd0 blink.obj vect.obj

Renesas Optimizing Linker W1.01.00  [19 Jul 2013]
   Copyright (C) 2011, 2013 Renesas Electronics Corporation

The evaluation period has expired.
Renesas Optimizing Linker Completed

生成された blink.mot は、GR-SAKURA ボードを E1 エミュレータに接続後、フラッシュ開発ツールキット(FDT)を用いて書き込むことができる*2
プログラムの書き込み後、E1 を取り外し、USB または DC ジャックから電源を供給すると LED が 0.5 秒間隔で点燈/消燈を繰り返す。

ついでに、make(1) にてビルド可能なものを以下の URL に置いた。

http://he4.seikyou.ne.jp/home/tsukimizake/gr-sakura-dev/blink.zip

プログラムの処理内容

初期化処理の内容は

  1. 割り込みを無効化
  2. スタックポインタの設定
  3. 可変ベクタテーブルの設定
  4. 使用しない周辺機能の無効化
  5. 存在しないポートの初期化
  6. クロックの初期化
  7. セクションの初期化*3

というものである。いずれも初期化処理としてはごく一般的なものである。
ただし注意が必要な箇所があり、それらについては次節にて後述する。

初期化処理の終了後はタイマを動作させて無限ループに入る。

154         mov.l   #CMSTR0_ADDR, r1
155         mov.w   #00001h, [r1]           ; Start CMT0
156
157         bra     $

LED の点燈/消燈処理は、コンペアマッチタイマ 0(CMT0)の割り込みハンドラにさせている。

  280 cmt0_interrupt:
  281         pushm   r1-r2
  282         mov.l   #count, r1
  283         mov.l   [r1], r2
  284         tst     r2, r2
  285         bnz     out
  286         mov.l   #001f4h, r2
  287 
  288         ; toggle output level
  289         mov.l   #PORTA_PODR_ADDR, r1
  290         bnot    #6, [r1]
  291 
  292         mov.l   #count, r1
  293 out:    sub     #1, r2
  294         mov.l   r2, [r1]
  295 
  296         popm    r1-r2
  297         rte

消費電力低減機能

最近の MCU は、消費電力を下げる目的で、リセット直後はほとんどの機能を停止させている(クロックを供給していない)ものが多い。RX もそのような MCU の一つで、リセット直後ではコンペアマッチタイマすら使用できない。

このため、モジュールストップコントロールレジスタ A(MSTPCRA)を事前に書き換え、クロックを供給する必要があるのだが、この MSTPCRA はプログラム暴走時に意図せぬ書き換えが起きないよう保護の対象となっている。MSTPCRA を書き換えるためには、さらにプロテクトレジスタの書き換えも必要である(つまり二重に鍵が掛けられている状態である)。

blink.src の 67 行目から 73 行目にかけてのコードはこれらの処理を実行し、コンペアマッチタイマ 0(CMT0)へのクロック供給を開始するものである。

   67         ; enable CMT0
   68         mov.l   #PRCR_ADDR, r1
   69         mov.w   #0a503h,[r1]
   70         mov.l   #MSTPCRA_ADDR, r1
   71         mov.l   [r1], r2
   72         bclr    #15, r2
   73         mov.l   r2, [r1]

PLL クロックの使用

GR-SAKURA ボードでは、RX に 12MHz の水晶発振子が外付けされており、これをメインクロックとして用いることができる。今回のプログラムでは、12MHz のメインクロックを PLL により 1 分周・8 逓倍したものを生成し、それを 4 分周したものをシステムクロック(ICLK)として、8 分周したものを周辺クロック(PCLK)として使用した。したがって ICLK は 24 MHz、PCLK は 12 MHz となる。

(RX に限らず)使用するクロックを切り替える際には、切り替え先のクロックが安定するのをソフトウェアで待つ必要がある。RX の場合は、この時間をメイン発振器ウェイトコントロールレジスタ(MOSCWTCR)の [4:0] の bit で指定する。このビットに指定可能な値と、その値が何サイクルに相当するかの対応を以下に示す。

b4 b3 b2 b1 b0 機能
0 0 0 0 0 待機時間=2サイクル
0 0 0 0 1 待機時間=4サイクル
0 0 0 1 0 待機時間=8サイクル
0 0 0 1 1 待機時間=16サイクル
0 0 1 0 0 待機時間=32サイクル
0 0 1 0 1 待機時間=64サイクル
0 0 1 1 0 待機時間=512サイクル
0 0 1 1 1 待機時間=1,024サイクル
0 1 0 0 0 待機時間=2,048サイクル
0 1 0 0 1 待機時間=4096サイクル
0 1 0 1 0 待機時間=16,384サイクル
0 1 0 1 1 待機時間=32,768サイクル
0 1 1 0 0 待機時間=65,536サイクル
0 1 1 0 1 待機時間=131,072サイクル
0 1 1 1 0 待機時間=262,144サイクル
0 1 1 1 1 待機時間=524,288サイクル

ここで指定したサイクル数 + 16384 サイクル分だけメインクロックをカウントすると、MCU 内部へのメインクロック供給が開始される。

水晶発振子が安定するまでの時間({t_{MAINOSC})が 20.0 ms と仮定*4すると、周波数 12 MHz のメインクロックでは 240,000 サイクルに相当するので、上記の表より MOSCWTCR の [4:0] には 01110 を設定すればよい。 したがって、ソフトウェアで待機する時間は、
{t_{MAINOSC} (262144 + 16384)/ f_{MAIN} = 20.0 + (262144 + 16384) / 12000 = approx. 43.23 ms}
となる。

81 行目から 93 行目にかけてのコードは、これらの処理を行うものである。

   81         mov.l   #MOSCWTCR_ADDR, r1
   82         mov.b   #0eh, [r1]              ; MOSCWTCR.MSTS[4:0]=14
   83 
   84         ; run the main clock oscillator
   85         mov.l   #MOSCCR_ADDR, r1
   86         bclr    #0, [r1]                ; MOSCCR.MOSTP=0
   87         ; confirm that MOSCCR.MOSTP has actually been updated
   88 ?:      movu.b  [r1], r2
   89         tst     r2, r2
   90         bnz     ?-
   91 
   92         mov.l   #44, r1
   93         bsr     mdelay

今回の場合は PLL を使用するので、加えて PLL の安定待ちも必要になる。PLL の発振安定時間は最大 0.5 ms なので、PLL ウェイトコントロールレジスタ(PLLWTCR)には 9 (約 0.6827 ms 相当)を設定する。したがって、ソフトウェアで待機する時間は、
{t_{PLL1} + (65,536 + 131,072)/ f_{MAIN} = 0.5 + (65,536 + 131,072) / 96000 = approx. 2.548 ms}
となる(実際には約 3 ms 待機している)。

  105         mov.l   #PLLWTCR_ADDR, r1
  106         mov.b   #9, [r1]
  107         mov.l   #PLLCR2_ADDR, r1
  108         bclr    #0, r1                  ; PLLCR2.PLLEN=0
  109 
  110         mov.l   #3, r1
  111         bsr     mdelay

この辺りの詳しい内容は、以下のアプリケーションノートに記載されている。

RX63Nグループ、RX631グループ 初期設定例:
http://documentation.renesas.com/doc/products/mpumcu/apn/rx/r01an1245jj0110_rx63n.pdf

*1:当然だが、asrx や rlink がインストールされているディレクトリに PATH を通しておく必要がある。

*2:実は書き込みだけであれば、E1 は不要らしい

*3:このプログラムでは、初期化が必要なのは B セクションのみ

*4:配線パターン、発振定数により、安定するまでの時間は異なる。GR-SAKURA ボードに関する資料を漁ってみたが、この値は記載されていなかった