Arduino IDEでulptoolを使う

概要

ULPのアセンブラを行うesp32ulp-elf-binutilsを、Arduino IDEから使うためのツールである、ulptoolをWindowsにセットアップしてみました。

esp32ulp-elf-binutilsとは?

ULPのアセンブラ環境一式です。

Windows、Linux、Macの環境に対応しています。結構大掛かりなビルドツールで、資料もないので直接コマンドでアセンブルするのは困難です。

ulptoolとは?

Arduino IDEからesp32ulp-elf-binutilsを呼び出すためのツールです。

上記のページにセットアップ方法が書いてあります。

ハマりどころ

ulptoolはPythonを使っています。事前にセットアップしておく必要がありますが、2系じゃないと動きません。

C:\Users\%USERNAME%\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\platform.local.txt

compiler.s.cmd=C:\Python\Python27\python "{tools.ulptool.path}{tools.ulptool.cmd}"

Python3系が入っている人は、上記の行で、pythonのみで指定している場所を、2系のPythonを指定するようにするのがよいと思います。

UlpDebugも組み合わせてみる

ulptoolのスケッチ例のulp_READMEを自作ライブラリであるESP32 ULP Debuggerでデバッグしてみました。

事前にライブラリの管理からULPで検索してESP32 ULP Debuggerを追加しておいてください。

#include "esp32/ulp.h"
// include ulp header you will create
#include "ulp_main.h"
// include ulptool binary load function
#include "ulptool.h"
#include "UlpDebug.h"
// Unlike the esp-idf always use these binary blob names
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");
static void init_run_ulp(uint32_t usec);
void setup() {
    Serial.begin(115200);
    delay(1000);
    init_run_ulp(100 * 1000); // 100 msec
}
void loop() {
    // ulp variables data is the lower 16 bits
    Serial.printf("ulp count: %u\n", ulp_count & 0xFFFF);
    ulpDump(0, 20, 0);
    delay(100);
}
//hash: 5c389934265d8016df226704091cd30a
static void init_run_ulp(uint32_t usec) {
    // initialize ulp variable
    memset(RTC_SLOW_MEM, 0, 8000);
    ulp_set_wakeup_period(0, usec);
    esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
    // ulp coprocessor will run on its own now
    ulp_count = 0;
    err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
    if (err) Serial.println("Error Starting ULP Coprocessor");
}

上記が変更後のコードです。ハイライトの3行が変更した行になります。memsetは必要はありませんが、不要なデータがあるとわかりにくいので、クリアしています。

ulp count: 846
====================================
0000 : 72800053 PROG MOVE   R3,   5			// R3 = 5
0001 : D000000C PROG LD     R0, R3,   0			// R0 = MEM[R3+0]
0002 : 72000010 PROG ADD    R0, R0,   1			// R0 = R0 + 1
0003 : 6800000C PROG ST     R0, R3,   0			// MEM[R3+0] = R0
0004 : B0000000 PROG HALT				// HALT
0005 : 0063034E PROG NOP				// NOP

シリアルへの出力結果が上記になります。

/* Define variables, which go into .bss section (zero-initialized data) */
    .data
/* Store count value */
    .global count
count:
    .long 0
/* Code goes into .text section */
    .text
    .global entry
entry:
    move    r3, count
    ld      r0, r3, 0
    add     r0, r0, 1
    st      r0, r3, 0
    halt

上記が元のアセンブリコードになります。

コード部分はきれいにでていますが、データ部分はちょっと拾えていませんね。index:5がデータになるのですが、ulpDumpだとコードより前にデータがある前提で組んでいるので、NOPと判定されています。

0005 : 0063034E PROG NOP // NOP

上記の下2バイト分がデータになるので、0x034e(846)が正しい表示のはずです。データの上位2バイトは書き込みを行った行数が入っていますが、データ判定されていないのでうまく表示されていません。

まとめ

データとプログラムの判定をulpDumpの引数で渡していましたが、データを見て自動判定しないとダメそうです。

それ以外は比較的うまく動いているかな?

個人的にはulptoolはちょっと敷居が高いので、ESP32 ULP Debuggerのスケッチ例で使っているマクロを使った方法をおすすめします。

あと、ulptoolはArduino IDEやESP32のバージョンアップをすると消える場所にセットアップするので気をつけてください、、、

コメント