SBC1802ルーズキットの製作(ELF対応編)

2021/05/14

 CDP1802は、以前から気になっていたCPUで、インターシルを買収したルネサスからMILスペックのCDP1802Aが宇宙用途や軍事用途向けに現在でも販売されています。CDP1802は、1976年に発表され、当時としては最新の Silicon on Sapphire (SOS)CMOSプロセスで製造されていたたため、低消費電力、放射線に強い特徴があります。これは、サブストレーションとMOSFETのアイソレーションにPN接合ではなく絶縁体のサファイアを使用しているため、漏れ電流がほとんどない、放射線による電荷の発生が少ないためです。


CDP1802の手持ちがあったので、いつか動かしみたいと思っていました。COSMAC ELFといったマイコンシステムを再現した例もありましたが、SBC1802ルーズキットが発売されましたので、さっそく製作することにしました。


SBC1802ルーズキットでは、BASIC(RCA BASIC3)が走りますが、COSMAC ELF系統のソフトウェア資産も魅力的です。そこで、下記を実現できるようにしました。


・64KB EEPROM(W27C512)に複数のソフトウェアを焼けるようにする

・"The 1802 Membership Card"や"Pico/Elf"のROMに対応してみる

・シリアル入力RXDを-EF2と-EF3に接続する

・CPUクロック周波数の切り替えができるようにする


①64KB EEPROM(W27C512)に複数のソフトウェアを焼けるようにする

今回は、27256、AT28C256、W27C512に対応させるようにしました。W27C512の場合、2個の32KBのROM、1個の32KBのROM+2個の16KBのROM、4個の16KBのROMのバリエーションに対応できます。

ROM-1pinとROM-27pinをプルアップ抵抗(4.7KΩ)を入れた後、下記のような構成のジャンパ切替ヘッダーを追加しました。

CPU-A14  ◉ ◉ CPU-A14
ROM-1pin ◉ ◉ ROM-27pin
GND ◉ ◉ GND

使用例:

・27256の場合、CPU-A14<->ROM-27pin

・AT28C256の場合、CPU-A14<->ROM-1pin

・W27C512(最初の32KBのROM)の場合、ROM-1pin<->GND、CPU-A14<->ROM-27pin

・W27C512(2番目の32KBのROM)の場合、CPU-A14<->ROM-27pin


64KB EEPROM(W27C512)には、"The 1802 Membership Card"の"MCSMP20A.bin"と"Pico/Elf"の"pico_diskless.hex"を焼くことにしました。


②"The 1802 Membership Card"や"Pico/Elf"のROMに対応してみる

当初のCOSMAC ELFは、0000Hに配置されたSRAMしかなく、プログラムは、SWによって、SRAMにプログラムを書き込んだ後、実行を開始するようになっていました。CDP1802には、このための"LOAD"という状態があり、プログラムの書き込みができるように設計されていました。当時の汎用コンピュータ(メインフレーム)でもブートストラップという起動プログラムは、パネルから手入力するようになっていました。


さて、COSMAC ELFの進化過程で互換性を保つため、ROMは8000Hに配置されるようになり、ROMプログラムは、8000Hを前提にしているものが多くあります。


"The 1802 Membership Card"では、0000Hに配置できるROMもあります。RCA BASIC3も、0000Hに配置できるROMです。一方、"Pico/Elf"では、基本的にROMは、8000Hに配置されるようになっています。


"The 1802 Membership Card"は、Lee A. Hartさんのプロジェクトで、いろいろなガジェットキットを頒布しています。"Pico/Elf"は、Mike Rileyさんのプロジェクトで、COSMAC ELFを進化させ、専用OSや、専用基板の頒布をしています。


SBC1802に"Pico/Elf"の"Disk-less ROM"を焼いてみましたが、やっぱり動きません。CPUは、0000Hから実行しますが、ROMプログラムは、8000H用ですから当たり前です。


"Pico/Elf"の回路を確認すると、まずリセット後、8000HからRAM領域、0000HからROM領域になり、A15=0のプログラムが、A15=1の領域にブランチしてフェッチするとROM領域とRAM領域をスワップして、0000HからRAM領域、8000HからROM領域になります。今回は、SBC1802の回路をできるだけそのままに、まずリセット後、0000HからのROM領域のみ有効にし、A15=0のプログラムが、A15=1の領域にブランチしてフェッチすると0000HからRAM領域、8000HからROM領域にしました。


下記が追加した参考回路です。手持ちの部品で実現してみました。今回は、CPUの横にソケットを配置していただいたため、ピギーバック基板を容易に作成でき、助かりました。


③シリアル入力RXDを-EF2と-EF3に接続する

"RCA BASIC3"では、-EF3シリアル入力RXD用に使用していますが、"Pico/Elf"では、-EF2を使っていますので、-EF2と-EF3を接続しました。特に副作用は無いようです。


④CPUクロック周波数の切り替えができるようにする

"RCA BASIC3"は、1.79MHzのクロックを前提にしていますが、"Pico/Elf"では、4MHzを使用しています。少々オーバースペックですが、シリアルI/Fの速度の関係がありますので、切替できるように考えました。


水晶振動子で、1.79MHzは、市場で見つかりませんでしたので、セラミック振動子を使用することにしました。幸いeBayで、1.8MHzが見つかったからです。5%程度の誤差はありますが、シリアルI/Fのクロック偏差の範囲で使用可能と判断しました。


セラミック振動子のために、Q1水晶振動子端子の真ん中に接地用のソケットを追加し、3ピン化して、水晶振動子でもセラミック振動子でも差し替えて使用可能にしました。


MA7の切り替えは、ヘッダーにソケットを裏表逆に差し込んで切り替えられるようにしました。

下記は、SBCモードにした状態の全体像です。



下記は、"The 1802 Membership Card"のモニタ起動後、RCA BASIC3に入った状態です。



次は、"ELF"モードで、CPUクロック用に4MHzのセラミック振動子に切替えた状態の全体像です。



下記は、"Pico/Elf"のディスクレスROMで、メニューが表示された状態です。

FORTH、LISPも動作させることができました。



以上、CDP1802に触れることができました。参考にしていただければと思います。

CPUのコネクタは、結構便利で気に入っています。

(再クロール更新:2022/12/22)

ORANGE-ESPerで、MicroPython + FabGLを実現 ~ ESP-WROVER編

2021/05/01

 ①MicroPython + FabGLが実現できたが、ヒープが減少

FabGLを組み込む前は、99KB程度MicroPythonのヒープ空きサイズがありましたが、さすがにVGA表示メモリが必要になるので、22KBまで減ってしまっていました。WiFiや、Bluetoothなどが使用できる上にVGA表示とてんこ盛りしましたが、簡単なプログラムなら使えるのではないでしょうか。


そこで、ヒープを増やすため調査すると、幸いにも、SPIRAM搭載のESP-WROVERを搭載したESP32-DevKitC開発ボードを見つけましたので、これを使うことにしました。


②ビルドで"IRAM0 segment data does not fit."が発生

menuconfigで、SPIRAMを有効にし、ビルドオプションを指定してビルドを行いましたが、リンカーのASSERTに引っ掛かり、"IRAM0 segment data does not fit."となってしまいました。


下記は、リンカーが割り当てた、各セグメントの割り振りです。"iram0_0_seg"は、128KBしかなく、これが溢れたようです。この領域は、CPUが高速にアクセスできる高速処理を必要としている関数群に割り当てることになっていますが、MAPファイルを確認すると、SPIRAM関連のMicroPython・FabGLの関数でオーバーフローしたようです。

Memory Configuration

Name Origin Length Attributes
iram0_0_seg 0x0000000040080000 0x0000000000020000 xr
iram0_2_seg 0x00000000400d0020 0x000000000032ffe0 xr
dram0_0_seg 0x000000003ffbdb5c 0x000000000001e6a4 rw
drom0_0_seg 0x000000003f400020 0x00000000003fffe0 r
rtc_iram_seg 0x00000000400c0000 0x0000000000002000 xrw
rtc_data_seg 0x000000003ff80000 0x0000000000002000 rw
rtc_slow_seg 0x0000000050000200 0x0000000000000e00 rw
extern_ram_seg 0x000000003f800000 0x0000000000400000 xrw
*default* 0x0000000000000000 0xffffffffffffffff

"IRAM0 segment data does not fit."の対処方法を調査すると、不要と思われる関数(機能)を外すという消極的な対策程度しか確認できませんでした。半分あきらめ気味で、FabGL内で"IRAM_ATTR"属性が付けられている関数を確認すると、"displaycontroller.cpp"内は、それほど高速性でなくても行けそうな感じでした。


そこで、ESP-IDFの機能で、リンカーへのセグメント割り当てオプションができる"Linker Script"があることがわかりましたので、"linker_fragment_file.lf"を作成し、"CMakeLists.txt"に必要な定義を追加しました。これで、"IRAM0 segment data does not fit."が回避できました。


"displaycontroller.cpp"内の関数は、全てFlashROMに移動させました。実行時は、CPUのCacheメモリ経由でアクセスされますので、大きな問題はないと思われます。CPUクロック周波数を240MHz、Flashクロックを80MHzに指定して、特に表示のちらつきは、無いようです。


ESP-WROVERの恩恵で、無事MicroPythonの空きヒープサイズがが4MBになりましたので、かなりの大きさのプログラムでも問題無いはずです。


③ESP-WROVERでSDカードが使えない

ここまでなんとか来ましたが、SDカードの動作確認をすると、エラーが出て使えませんでした。ESP-WROVER内部とESP32-DevKitC開発ボードの回路図を確認すると、ORENGE-ESPerでSDカード信号に使用しているPin(17)とPin(16)は、SPIRAMで使用しているため、ESP32-DevKitC開発ボード上は、NCになっていました。


そこで、オプションのシリアルI/Fで使う、Pin(2)とPin(34)に変更しました。ジャンパ線が必要ですが、ESP-WROVERに依存していますので、下記のようにESP32-DevKitC開発ボード上でジャンパ線を追加しました。これで、ORENGE-ESPerは、ESP-WROVERでもESP-WROOM両者で使えます。


但し、GPIO2は、ESP32のプログラミング書き込みに使用されているため、浮遊容量が増え、不安定になる場合があるようですので、ESP32のプログラミング書き込みは、ORENGE-ESPerから外してESP32-DevKitC開発ボード単独で行うことをお勧めします。また、シリアルポート番号が変わることがありますので、注意ください。

Pin(17) <-> Pin(2)
Pin(16) <-> Pin(34)

下記は、ジャンパ線を飛ばした後のESP-WROVER版ESP32-DevKitC開発ボードです。


④おまけモジュールの追加

最後に、MicroPythonを使いやすくするために、下記のモジューを"micropython/port/esp32/modules"

に配置し、ビルドするとコンパイル済mpy形式でビルドイメージに組み込まれます。


・uwipye.py    軽量pythonエディター

・upysh.py     ファイル操作shellライクコマンド

・mount_sd.py  SDカードのマウント('/sd'にマウント)


⑤ソース・バイナリの公開

MicroPythonは、MITライセンス、FabGLは、GPLライセンスで公開されていますので、今回は、GPLライセンスに従い、ソースとバイナリをtarボールで公開します。変更箇所は、Winmergeなどで差分を確認できると思いますが、オリジナルを変更した部分には"jr2xzy"の記述を入れています。

ソース・バイナリのtgz

ファイルサイズの関係で、fullclean状態になりますので、MicroPythonのソースツリーの"micripython/port/esp32"に上書きし、menuconfig後、ビルドしてください。また、ESP-WROVERの場合、"linker_fragment_file.lf"を有効にしてください。

## ESP-WROOM for ESPer ##

cd ~/esp32/micropython/ports/esp32
idf.py menuconfig

Serial flasher config --->
Flash SPI mode (QIO) --->
Flash SPI speed (80 MHz) --->
Flash size (4 MB) --->

Driver configurations --->
RTCIO configuration --->
[*] Support array `rtc_gpio_desc` for ESP32

Component config --->
ESP32-specific --->
CPU frequency (240 MHz) --->
[ ] Support for external, SPI-connected RAM

idf.py build

## ESP-WROVER for ESPer ##
cd ~/esp32/micropython/ports/esp32
idf.py menuconfig

Serial flasher config --->
Flash SPI mode (QIO) --->
Flash SPI speed (80 MHz) --->
Flash size (4 MB) --->

Driver configurations --->
RTCIO configuration --->
[*] Support array `rtc_gpio_desc` for ESP32

Component config --->
ESP32-specific --->
CPU frequency (240 MHz) --->
[*] Support for external, SPI-connected RAM

SPI RAM config --->
Set RAM clock speed (80MHz clock speed) --->
[*] Initialize SPI RAM during startup (NEW)

idf.py -D MICROPY_BOARD=GENERIC_SPIRAM build

バイナリは、"micropython/port/esp32"配下の"ESP-WROOM"及び、"ESP-WROVER"に置いています。ORANGE-ESPer用の"esptool.exe"と"upload.batを適当に変更して使えば、ビルドしなくても焼くことができるはずです。


下記は、"import mount_sd"を実行後、MicroPythonの空きヒープサイズが"4095296"になっています。"from uwipye import pye"を実行後、pye()エディターのキー機能が表示されます。



以上、ESP32(ESP-IDF)には結構苦労しましたので、Arduinoの有難さがわかります。ESP32の能力をフルに出すためには、ESP-IDFが必要な感じです。今後、FabGLのMicroPythonインタフェースが追加できれば、用途が広がるかもしれません。いずれにしろ、ORENGE-ESPerを楽しんだり、ESP32のデバッグに役立ていただければと思います。

(再クロール更新:2022/12/22)

ORANGE-ESPerで、MicroPython + FabGLを実現 ~ FabGLのESP-IDFへのポーティング

2021/04/30

 ①ESP-IDFの"hello_world_main.c"にAnsi-Terminalを追加

まず、FabGLライブラリの配置です。ESP-IDFでは、ソースツリーに"components"というサブディレクトリを配置すると、自動的に"CMakeLists.txt"をスキャンしてビルドの対象にしてくれます。

  +hello_world
+components
| +fabgl
| +arduino ESP32用Arduinoライブラリ
| +cores Arduinoコアライブラリ
| +libraries Arduinoオプションライブラリ
| +variants ボード依存ヘッダー
| +src FabGLライブラリ
| +CMakeLists.txt ビルド用スクリプト
+main メインプログラム

次に、CMakeLists.txtの記述です。

Ansi-Terminalが必要とする最低限のモジュールを順番にビルド対象にしていきます。コンパイルン時にヘッダーファイルが見つからない場合は、ヘッダーパスを追加し、エラーを解消していきます。ただ、ESP-IDFのバージョンが異なるため、ヘッダーファイルの内容が変更されている部分があるため、未定義の型・マクロの定義部分を見つけてソースコードを変更していきます。


コンパイルエラーを解消後、リンカーで未定義変数・関数が出てきますので、対象ライブラリを追加すると、CMakeLists.txtが完成します。この段階で、必要に応じコンパイラ・リンカーのオプションを追加します。


リンクが完了し、バイナリイメージをESP32に焼いて実行できるようになりました。Arduinoと同じVGA画面が表示でき、ホットしたのもつかの間、設定変更のダイアログを終了させると、"Guru Meditation Error"で落ちてしまいます。Arduinoでは、何ら問題ありませんでした。

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400eb927 PS : 0x00060f30 A0 : 0x800ea9a2 A1 : 0x3ffebd90
A2 : 0xfefefefe A3 : 0x3ffebdc5 A4 : 0x00000000 A5 : 0x00000000
A6 : 0xffffffff A7 : 0x3ffed290 A8 : 0x80082d08 A9 : 0x3ffebd50
A10 : 0x3ffe3efc A11 : 0x00000000 A12 : 0x3ffb7028 A13 : 0x00000000
A14 : 0x00000000 A15 : 0x00060023 SAR : 0x00000008 EXCCAUSE: 0x0000001c
EXCVADDR: 0xfefeff3a LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0x00000000

ELF file SHA256: 1e6c549e1a65be00

Backtrace: 0x400eb927:0x3ffebd90 0x400ea99f:0x3ffebdc0 0x400d3492:0x3ffebdf0
0x400d3616:0x3ffebe10 0x401163e9:0x3ffebe30 0x400f5b99:0x3ffebe50 0x40088bb6:0x3ffebe90

バックトレースアドレス・リンカーマップの確認、ログ追加で問題個所を特定はできました。通常問題なく通過していますが、問題がある場合、途中で落ちてしまいます。ESP32マイクロコントローラでは、命令RAMは、書き込み保護が掛かっているので、プログラムの破壊がなくても落ちてしまったわけです。


ESP32について調べていくと、例外が発生した場合には、対象アドレスが、EXCVADDRに表示される仕組みになっているので、NULLポインター・未確保ヒープ・解放済ヒープなのか切り分けができるようです。下記のオプションで有効になります。

Heap memory debugging  --->
Heap corruption detection (Comprehensive) --->


前記の例では、"EXCVADDR: 0xfefeff3a"でしたので、解放済ヒープでの値"0xfefefefe"に近い値で、このアドレスに対するオフセットで変数をアクセスしていることが想定できました。


ソースコードから、問題となったクラス変数ポインターが特定できました。そのクラスの生成・消滅を追っかけると、ダイアログの消去処理中に消滅が起こりえる部分を見つけました。わずかなタスク実行タイミングの差で発生する潜在バグでると考えられます。完全な修正ではないですが、回避できるタイミングで対象クラスの消滅を行うように変更しました。


これで、FabGLライブラリをESP-IDF上に移植できました。


②MicroPythonのソースツリーへの組込みとビルド

"hello_world"の場合と同様に、micropython/portの配下にFabGLライブラリを移行します。MicroPpythonのメインプログラム"main.c'から、MicroPpythonを開始する前にFabGLを初期化しておくようしました。MicroPpythonは、最大確保可能なヒープをMicroPpythonのヒープに割り当てますので、起動前に一時的なヒープの使用を終わらせておきます。"AnsiTerminal.ino.cpp"を変更し"fabgl_setup.cpp"を作成します。Ansi-Terminalの設定ダイアログを閉じると、MicroPpythonを起動させるシーケンスにします。

  +esp32
+components
| +fabgl
| +arduino ESP32用Arduinoライブラリ
| +cores Arduinoコアライブラリ
| +libraries Arduinoオプションライブラリ
| +variants ボード依存ヘッダー
| +src FabGLライブラリ
| +CMakeLists.txt ビルド用スクリプト
+main メインプログラム
 +modules 追加のMicroPythonモジュール

MicroPythonとFabGLの統合に合わせて"CMakeLists.txt"を変更して、ビルドしたところ、partitionのサイズが足らなくなったので、"partitions.csv"変更して、なんとか動作させることができました。但し、この段階では、MicroPpythonは、USBシリアル経由のみの動作です。


③MicroPythonの入出力をFabGL Ansi-Terminalに接続

MicroPythonにUSB-Serial(RX)とAnsi-Terminal(Keyboard)のどちらからでも入力できるようにします。MicroPythonからは、USB-Serial(TX)とAnsi-Terminal(VGA Display)へ出力させます。これで、マルチターミナルになり、どちらからも透過的に使用できます。TeraTermがなくても、Ansi-Terminalで、Micropythonパソコンになります。

USB-Serial(RX) -+-> MicroPython -+-> USB-Serial(TX)
| |
Ansi-Terminal(Keyboard) +-> Ansi-Terminal(VGA Display)

"mphalport.c"を変更して、上記を実現しました。ただ1点、スクリーンエディタなどで、Ansi-Terminalからのレポート情報を取得する"ESC [ 5 n"と"ESC [ 6 n"のエスケープシーケンスがあることがわかりました。両方にこのシーケンスを送ると、双方からレスポンスがあり、混信してしまいます。そのため、このエスケープシーケンスをUSB-Serial(TX)へ送らないようにブロックする機能を追加しました。


以上、全体をビルドしました。下記は起動画面です。FabGL Ansi-Terminalの設定ダイアログが出ますが、変更が無ければ、"Enter"でMicroPythonが起動します。



MicroPythonが起動した画面です。



かんぱぱさんのasciiartを動かしてみました。PC上のTeraTermとVGAモニタが同じ表示になります。


(再クロール更新:2022/12/22)