🎻

Azure RTOS ThreadX を動かす。

2020/11/26に公開

はじめに

Azure RTOS ThreadX が面白そうだったので、microbit V1.5上で動かしてみました。
microbitで動かすまでに調べた内容についてまとめます。
他のボードで動かす場合にも参考になるかと思います。
記載してみましたが、記載した順番に実施したわけではなく、
こっちを調べて動作を見て、おかしいから今度はこっちを見て・・・という感じで言ったり来たりしながら進めていました。

コード一式

作成したコードはいかに起きました。
HELLO!という文字が横方向、縦方にスクロールします。
https://github.com/SaitoYutaka/threadx

動作している様子は以下。

ThreadX

コードは https://github.com/azure-rtos/threadx にあります。
ここの /ports内をみると各種ARMのコードがありました。この中の
ports/cortex_m0/gnu が参考になりそうです。
なんで cortex_m0 かというと、 microbit hardwareに以下の記載があるためです。

Core variant	Arm Cortex-M0 32 bit processor

ちなみにmicrobit V2では
Cortex-M4 となっています。

sample code

cortex_m0/gnu/example_build/ 内にある
build_threadx.batbuild_threadx_sample.batがビルドスクリプトです。
とてもシンプルな書き方です。

Linker script

cortex_m0/gnu/example_build/sample_threadx.ld
をmicrobit用に修正をします。修正をするのは RAMとFLASHのアドレス、サイズになります。

MEMORY
{
  // 省略
  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x4000
  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000
}

Linker scriptについて参考にした情報は以下。

Vector table

cortex_m0/gnu/example_build/tx_vectors.s
はmicrobit用(nRF51用)に修正をします。

割り込みについてはnRF51 Series Reference Manual (PDF)
を見て確認でもいいですが、Nordic提供の以下のコード(nrfx/blob/master/mdk/gcc_startup_nrf51.S)も参考になります。

https://github.com/NordicSemiconductor/nrfx/blob/master/mdk/gcc_startup_nrf51.S

SysTick

ここを見ると、nRF51はSysTickがないとの回答がされています。

Unfortunately for you, we've chosed to not implement a SysTick timer in the nRF51822. This is an optional feature in Cortex-M0, and since we believe that all use cases are covered by the other timers, we chose to not implement a separate SysTick timer.

リファレンスマニュアルにも以下のような記載があります。
(RTC TICK eventを使えって書いているのでしょうか?)

nRF51 Series Reference Manual (PDF)

19.1.5 The TICK event
The TICK event enables low power RTOS implementation as it optionally provides a regular interrupt source for a RTOS
without the need to use the ARM® SysTick feature. Using the RTC TICK event rather than the SysTick allows the CPU to
be powered down while still keeping RTOS scheduling active.
Note: The TICK event is disabled by default.

なので、tx_initialize_low_level.Sの処理を RTC割り込みから呼び出すように修正をします。

crt0

サンプルコードの中に、以下のようにcrt0が含まれています。
https://github.com/azure-rtos/threadx/blob/master/ports/cortex_m0/gnu/example_build/cortexm0_crt0.s

ちなみにctrはC runtimeの略だそうです。
https://en.wikipedia.org/wiki/Crt0

このようにcrt0を自前で用意しているケースというはあまり見たことがなかったです(自分が知らないだけかもしれませんが)。
普段は以下のものを利用したりしていました。
https://sourceware.org/newlib/

nrfx

nRF51のレジスタアクセスなどを、データシートを見てゼロから実装はしんどいので
以下のライブラリを使用します。
https://github.com/NordicSemiconductor/nrfx

一部CMSISのファイルも使用している箇所があるのでこちらも必要となります。

エミュレータ(qemu)

microbit V1.5はqemuにも対応しているので、動作確認にとても助かります。
なので、新しいボードで動作させることに挑戦する基準にエミュレータがあるかどうかというのも判断基準にいれてもいいのかなと思いました。

memset

https://github.com/NordicSemiconductor/nrfxを利用して実装をすすめていたら
memsetがないよとビルドエラーが出ることがありました。
エラーの発生箇所はここ

memsetの実装を自前で用意する必要があります。
https://sourceware.org/newlib/を利用できていればこの中にmemsetも含まれていたのですが、今回は使用しない方向で進めます。

さがしたら以下のものが見つかりました。こちらを利用します。
https://github.com/bobbl/libaeabi-cortexm0

pyOCD

pyOCDを使用すると、microbit上でデバッグができます。詳細は以下。
Debugging the BBC micro:bit with pyOCD and GDB

以下の手順でデバッグできます。
まず、pyocd-gdbserverを起動。

$ pyocd-gdbserver -t nrf51 -bh -r
0005222:WARNING:gdb_server:pyocd-gdbserver is deprecated; please use the new combined pyocd tool.
0005479:INFO:board:Target type is nrf51
0005674:INFO:dap:DP IDR = 0x0bb11477 (v1 MINDP rev0)
0005698:INFO:ap:AHB-AP#0 IDR = 0x04770021 (AHB-AP var2 rev0)
0005747:INFO:rom_table:AHB-AP#0 Class 0x1 ROM table #0 @ 0xf0000000 (designer=244 part=001)
0005760:INFO:rom_table:[0]<e00ff000:ROM class=1 designer=43b part=471>
0005760:INFO:rom_table:  AHB-AP#0 Class 0x1 ROM table #1 @ 0xe00ff000 (designer=43b part=471)
0005776:INFO:rom_table:  [0]<e000e000:SCS-M0+ class=14 designer=43b part=008>
0005789:INFO:rom_table:  [1]<e0001000:DWT-M0+ class=14 designer=43b part=00a>
0005799:INFO:rom_table:  [2]<e0002000:BPU class=14 designer=43b part=00b>
0005809:INFO:rom_table:[1]<f0002000:MTB-M0 class=9 designer=43b part=9a3 devtype=13 archid=0000 devid=0:0:0>
0005816:INFO:cortex_m:CPU core #0 is Cortex-M0 r0p0
0005825:INFO:dwt:2 hardware watchpoints
0005830:INFO:fpb:4 hardware breakpoints, 0 literal comparators
0005880:INFO:server:Semihost server started on port 4444 (core 0)
0006001:INFO:gdbserver:GDB server started on port 3333 (core 0)

次に、別ウィンドウでgdbをきどうします。
以下の例では、main.outをロードしてmain関数にブレークポイントを設定して実行している様子です。

$ arm-none-eabi-gdb
GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00005c3a in ?? ()
(gdb) file main.out 
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from main.out...done.
(gdb) load main.out 
Loading section .vectors, size 0xc0 lma 0x0
Loading section .init, size 0x118 lma 0xc0
Loading section .text, size 0x6610 lma 0x1d8
Loading section .rodata, size 0x4d0 lma 0x67e8
Loading section .data, size 0x8 lma 0x6cb8
Start address 0xc0, load size 27840
Transfer rate: 9 KB/sec, 1546 bytes/write.
(gdb) b main 
Breakpoint 1 at 0x1f50: file sample_threadx.c, line 76.
(gdb) c
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at sample_threadx.c:76
warning: Source file is more recent than executable.
76	    SystemInit();
(gdb) 

Discussion