picoZ80 テクニカルガイド
picoZ80 テクニカルガイド
このガイドでは、picoZ80 のハードウェアアーキテクチャ、RP2350 PIO バスインターフェース、メモリモデル、JSON 設定リファレンス、仮想デバイスフレームワーク、デバッグ手順について説明します。内部構造を理解したい、新しいドライバーを書きたい、新しいホストマシンにファームウェアを移植したい、またはファームウェアレベルの問題をデバッグしたいデベロッパーを対象としています。
エンドユーザーのセットアップとウェブインターフェースの使用方法についてはpicoZ80 ユーザーマニュアルを参照してください。プロジェクト概要とビルド手順についてはpicoZ80 プロジェクトページを参照してください。
ハードウェアアーキテクチャ
picoZ80 は DIP-40 パッケージのフットプリント内に収まるように設計されたコンパクトな単一 PCB に 5 つのサブシステムを統合しています。すべてのロジックは 3.3V で動作し、Z80 バスインターフェースが 5V ホストバスのレベル変換と電流駆動を処理します。
システムブロック図
┌─────────────────────────────────────────────────────────────────────────┐
│ picoZ80 PCB │
│ │
│ ┌────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ RP2350B │ │ ESP32-S3 │ │
│ │ (Cortex-M33、デュアルコア)│ │ │ │
│ │ │ │ ┌──────┐ ┌──────────────┐ │ │
│ │ コア 0:USB、ファイル I/O、│◄────►│ │ SD │ │ ウェブサーバー│ │ │
│ │ ESP32 リレー │ FSPI │ │ カード│ │ (Bootstrap) │ │ │
│ │ コア 1:Z80 バスホットループ│ UART │ └──────┘ └──────────────┘ │ │
│ │ │ │ │ │
│ │ PIO 0,1,2:バスインターフェース│ │ WiFi ─── 802.11 b/g/n AP │ │
│ │ │ │ またはクライアントモード│ │
│ │ 16MB SPI フラッシュ │ └──────────────────────────────┘ │
│ │ 8MB PSRAM(SPI) │ │
│ └────────────────────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ Z80 バスインターフェース│ │
│ │ (40 ピン DIP 出力) │ │
│ └────────┬────────┘ │
│ │ 5V バス(A0–A15、D0–D7、MREQ、IORQ、RD、WR...) │
└────────────────┼────────────────────────────────────────────────────────┘
│
┌───────┴───────┐
│ ホスト Z80 │
│ DIP-40 ソケット│
│ (レガシー │
│ コンピューター)│
└───────────────┘
主要コンポーネント
| コンポーネント | デバイス | 役割 |
|---|---|---|
| プライマリ MCU | RP2350B(QFN-80) | デュアル Cortex-M33、150MHz(最大 300MHz OC)、512KB SRAM、12 PIO ステートマシン、48 GPIO ピン |
| フラッシュ | W25Q128(16MB SPI) | ブートローダー、デュアルファームウェアスロット、設定パーティション |
| PSRAM | 8MB SPI PSRAM | Z80 アドレス空間用の 64 × 64KB RAM/ROM バンク |
| コプロセッサ | ESP32-S3-PICO-1 | WiFi、SD カード、ウェブサーバー、OTA |
| USB ハブ | CH334F | USB ハブ、ファームウェアアップデートブリッジ |
| 電源供給 | TLV62590BV | 5V → 3.3V 同期バックコンバーター |
RP2350B GPIO 割り当て
RP2350B QFN-80 パッケージは 48 本の GPIO ピンを提供します。picoZ80 はほぼすべてのピンを使用します。割り当てはボードの設計に固定されており PIO プログラムに反映されています:
| GPIO 範囲 | シグナル | 方向 |
|---|---|---|
| GPIO 0–15 | A0–A15(Z80 アドレスバス) | 出力(PIO が駆動) |
| GPIO 16–23 | D0–D7(Z80 データバス) | 双方向(PIO トライステート) |
| GPIO 24 | MREQ |
出力 |
| GPIO 25 | IORQ |
出力 |
| GPIO 26 | RD |
出力 |
| GPIO 27 | WR |
出力 |
| GPIO 28 | M1 |
出力 |
| GPIO 29 | RFSH |
出力 |
| GPIO 30 | BUSREQ |
入力 |
| GPIO 31 | BUSACK |
出力 |
| GPIO 32 | HALT |
出力 |
| GPIO 33 | INT |
入力 |
| GPIO 34 | NMI |
入力 |
| GPIO 35 | WAIT |
出力 |
| GPIO 36 | CLK |
入力(ホストクロック) |
| GPIO 37 | RESET |
入力 |
| GPIO 38–41 | ESP32 FSPI(CS、CLK、MOSI、MISO) | SPI |
| GPIO 42–43 | ESP32 UART(TX、RX) | UART |
| GPIO 44–45 | PSRAM SPI | SPI |
| GPIO 46–47 | USB(D+、D–) | USB |
ファームウェアアーキテクチャ
RP2350 ファームウェアは、RP2350-arm-s プラットフォームをターゲットとした Raspberry Pi Pico SDK 2.x でビルドされます。ファームウェアは 2 つの独立した実行ファイルに分かれています:ブートローダーとアプリケーション。
フラッシュメモリレイアウト
| パーティション | アドレス範囲 | サイズ | 内容 |
|---|---|---|---|
| ブートローダー | 0x10000000 – 0x1001FFFF |
128KB | USB ブリッジ、ファームウェアアップデート、パーティションセレクタ |
| App スロット 1 | 0x10020000 – 0x1051FFFF |
5MB | Z80 ファームウェア — アクティブアプリケーション(スロット 1) |
| App スロット 2 | 0x10520000 – 0x10A1FFFF |
5MB | Z80 ファームウェア — アクティブアプリケーション(スロット 2) |
| App 設定 1 | 0x10A20000 – 0x10C9FFFF |
2.5MB | ROM イメージ + 最小化設定 JSON(スロット 1) |
| App 設定 2 | 0x10CA0000 – 0x10F1FFFF |
2.5MB | ROM イメージ + 最小化設定 JSON(スロット 2) |
| 一般設定 | 0x10F20000 – 0x10FFEFFF |
892KB | コア設定、スクラッチスペース |
| パーティションテーブル | 0x10FFF000 – 0x11000000 |
4KB | アクティブスロット番号、チェックサム、メタデータ |
デュアルコアの責任
2 つの Cortex-M33 コアは完全に別々の責任を割り当てられており、コア間メッセージキュー(
queue_t)を介して通信します。この分離により、コア 0 の非リアルタイム作業がコア 1 の Z80 バストランザクションにジッターを導入しないことが保証されます。
| コア | 責任 |
|---|---|
| コア 0 | USB CDC シリアルブリッジ;ファームウェアアップデート調整;ファイル I/O(UART 経由で ESP32 にリレー);ESP32 コマンドディスパッチ(ディスクイメージ変更、設定リロード、バージョンクエリ);パーティション管理;コア間メッセージディスパッチ。 |
| コア 1 | Z80 バスエミュレーションホットループ — 独占的に実行。PIO FIFO をサービスし、各バストランザクションをメモリマップに対して解決し、物理ホストハードウェア(PHYSICAL)、PSRAM(RAM/ROM)、仮想デバイスハンドラ(FUNC)のいずれかにディスパッチする。内部ループは SRAM に配置。 |
PIO バスインターフェース
Z80 バスインターフェースは RP2350 PIO アセンブリ(
z80.pio)で完全に実装されています。RP2350 は 3 つの PIO ブロック(PIO 0、PIO 1、PIO 2)を提供し、それぞれ 4 つのステートマシンを持ちます — 合計 12 ステートマシンで、Z80 ファームウェアはそれらすべてを使用します。
PIO プログラムは Cortex-M33 コアとは独立して実行されます。バスインターフェースはコア 1 が PSRAM アクセスや仮想デバイス関数呼び出しで占有されているときでも確定的に応答し続けます。ステートマシンはポーリングではなく PIO IRQ フラグを介して通信し、マシン間のレイテンシーを排除します。
PIO プログラムテーブル
| PIO | ステートマシン | プログラム | 機能 |
|---|---|---|---|
| 0 | SM 0 | z80_addr |
16 ビットアドレス(A0–A15)をバスに出力し、サイクル開始を SM 2 にシグナルする。 |
| 0 | SM 1 | z80_data |
トライステート制御で D0–D7 を駆動またはサンプリング;BUSRQ 中に解放。 |
| 0 | SM 2 | z80_cycle |
トップレベルバスサイクルシーケンサー — フェッチ、読み取り、書き込み、I/O、DRAM リフレッシュサイクルをオーケストレート。 |
| 0 | SM 3 | z80_fetch |
オペコードフェッチサイクル(M1 + MREQ + RD)。 |
| 1 | SM 0 | z80_mem_read |
メモリ読み取りサイクル(MREQ + RD)。 |
| 1 | SM 1 | z80_mem_write |
メモリ書き込みサイクル(MREQ + WR)。 |
| 1 | SM 2 | z80_io_read |
I/O 読み取りサイクル(IORQ + RD)。 |
| 1 | SM 3 | z80_io_write |
I/O 書き込みサイクル(IORQ + WR)。 |
| 2 | SM 0 | z80_busrq |
BUSREQ/BUSACK を管理;/IORQ、/MREQ、/RFSH、/M1、/HALT、/WR、/RD を解放。 |
| 2 | SM 1 | z80_nmi |
NMI アサーションを検出してコア 1 にシグナルする。 |
| 2 | SM 2 | z80_clk_sync |
PIO ステートマシンをホスト Z80 CLK シグナルに同期させる。 |
| 2 | SM 3 | z80_int_ack |
割り込みアクノリッジサイクル(M1 + IORQ)を処理する。 |
PIO IRQ シグナル規約
ステートマシン間の通信には PIO IRQ フラグを使用します。コア 1 はホットループでこれらのフラグを監視して各バスイベントでアクションを取ります:
| IRQ | イベント |
|---|---|
| IRQ 0 | アドレス有効 / サイクル開始 — 新しいバスサイクルが始まり A0–A15 が安定している。 |
| IRQ 1 | データフェーズ — データバスの方向が解決された;D0–D7 を駆動またはサンプリングすべき。 |
| IRQ 2 | T1 検出 — 現在のサイクルの T1 の立ち上がりエッジ。内部操作をホストクロックに同期させるために使用。 |
| IRQ 3 | RESET イベント — ホストの RESET ラインがアサートされた。コア 1 はエミュレーション状態を再初期化すべき。 |
| IRQ 4 | NMI 検出 — ホストの NMI ラインがアサートされた。 |
| IRQ 6 | BUSRQ アクティブ — ホストが BUSREQ をアサートした;PIO がバスを解放中。 |
ウェイトステート生成
PIO 2 SM 0 の
z80_wait PIO プログラムは /WAIT をアサートすることでホストバスに設定可能な T サイクルウェイトステートを挿入します。追加ウェイトステートの数は config.json の tcycwait パラメータでメモリまたは I/O ブロックごとに制御されます。
ウェイトステートは、RP2350 がホストバスにデータを提示する前に PSRAM アクセスや仮想デバイス関数呼び出しを完了するために追加時間が必要な場合に必要です。tcycsync パラメータは T1 同期を有効化し(PIO 2 SM 1 の z80_sync)、PSRAM アクセスウィンドウを各バスサイクルの T1 立ち上がりエッジにロックし、カセット、シリアルビットバンギングなど精密なタイミングのためにホストクロックに依存するアプリケーションのタイミングドリフトを防ぎます。
PIO アーキテクチャ — バスサイクルの再現方法
RP2350 のプログラマブル I/O(PIO)サブシステムは、picoZ80 がサイクル精度の Z80 バスタイミングを再現するための中核技術です。PIO ステートマシンがどのように連携して動作するかを理解することは、バスインターフェースの変更やタイミング問題のデバッグに不可欠です。
RP2350 PIO の基礎
各 PIO ブロックには、共有 32 命令メモリからプログラムを実行する 4 つの独立したステートマシン(SM)が含まれています。ステートマシンは Cortex-M33 コアとは独立してシステムクロック周波数(最大 300 MHz)で動作します。picoZ80 が使用する主要な PIO リソース:
- TX FIFO — CPU からステートマシンへの 4 エントリーキュー。コア 1 の C コードがデータ(アドレス、制御ワード、インジェクトされる命令)を FIFO にプッシュし、PIO プログラムが
outまたはpullを使用して読み取ります。 - RX FIFO — ステートマシンから CPU への 4 エントリーキュー。PIO が
inを使用してデータバスサンプルを FIFO にプッシュし、コア 1 が各バスサイクル完了後に読み取ります。 - IRQ フラグ — PIO ブロック内のすべてのステートマシンで共有される 8 つのフラグ(IRQ 0〜7)。フラグは PIO ブロック間でも参照可能(あるブロックの IRQ 0〜3 は隣接ブロックの IRQ 4〜7 にマップ)。ステートマシンは
irq set/irq wait/irq clearを使用して互いおよび C コードと同期します。 - スクラッチレジスタ X と Y — SM ごとに 2 つの 32 ビットレジスタ。ループカウンターや一時値に使用されます。
out exec— TX FIFO から値を取得し PIO 命令として実行する特殊命令。C コードがバスサイクルシーケンスを動的に制御するメカニズムです(後述)。set pins/out pins— GPIO ピンを直接駆動。setは即値 5 ビットを使用、outは出力シフトレジスタ(OSR)からピンにデータをシフトします。in pins— GPIO ピンを入力シフトレジスタ(ISR)にサンプリングし、RX FIFO に自動プッシュします。wait gpio— 特定の GPIO ピンが指定レベルに達するまで SM をストールさせます。ホスト Z80 クロック信号との同期に広く使用されます。- サイドセット — 命令サイクルを使用せずに、任意の命令の副作用として 1 つまたは 2 つの GPIO ピンを駆動可能。picoZ80 は 2 ビットサイドセットを使用して
/RDと/WRを他の操作と同時に制御します。 - JMP PIN — 指定 GPIO ピンレベルに基づく条件分岐。
/WAIT、BUSREQ、/NMI、/RESETのテストに使用されます。
out exec メカニズム — 動的命令インジェクション
picoZ80 PIO 設計の最も特徴的な機能は、
z80_cycle オーケストレーターステートマシンにおける out exec, 16 の使用です。この命令は TX FIFO から 16 ビット値を取得し、PIO 命令として即座に実行します。値はデータではなく、ステートマシンが次に実行する命令そのものです。
このメカニズムにより、コア 1 の C コードがリアルタイムでバスサイクルシーケンスを制御できます。各サイクルタイプの固定 PIO プログラムをロードするのではなく、コア 1 がプリエンコードされた PIO 命令のシーケンスを TX FIFO にプッシュし、サイクル SM がそれを一つずつ実行します:
// z80_cycle SM(PIO 0 SM 2)— オーケストレーター // // .program z80_cycle // .side_set 2 opt // public start_cycle: // wait 0 irq 6 ; BUSACK がアクティブなら一時停止(バス放棄中)。 // irq set 0 ; 「新しいサイクル準備完了」を通知。 // wait 0 irq 0 ; C コードが IRQ 0 をクリアするまで待機(アドレスロード済み)。 // wait 1 gpio Z80_PIN_CLK ; ホストクロックの T1 立ち上がりエッジに同期。 // cycle_exec: // out exec, 16 ; ← TX FIFO から次の命令を取得し実行。 // jmp cycle_exec ; ループ:インジェクトされた命令を実行し続ける。 // // C コードはエンコード済み PIO 命令のシーケンスを FIFO にプッシュします。 // 各命令はバスサイクルの1ステップ(/MREQ アサート、クロックエッジ待機、 // データバス読み取りなど)を制御します。シーケンスの最後は start_cycle への // JMP で、オーケストレーターを再起動して次のバストランザクションに備えます。
C コードはこれらの命令シーケンスを起動時に各サイクルタイプ(フェッチ、メモリリード、メモリライト、I/O リード、I/O ライト、リフレッシュ、割り込みアクノリッジ)用にプリコンパイルします。実行中、コア 1 は適切なプリビルドシーケンスを選択して FIFO にプッシュします。このアプローチには 2 つの重要な利点があります:
ステートマシンの連携
- プログラム空間の効率性 — 各 PIO ブロックには 32 命令スロットしかありません。命令を動的にインジェクトすることで、サイクル SM はすべてのサイクルタイプをオーケストレートするためにわずか 7 命令分のプログラムメモリしか必要としません。実際のサイクルタイププログラム(フェッチ、リード、ライトなど)は PIO 常駐プログラムではなく、エンコード済み命令の C 配列として存在します。
- 柔軟性 — C コードはインジェクトする命令シーケンスを実行時に変更でき、特殊なケース(追加ウェイトステートの挿入、リフレッシュフェーズのスキップ、デバッグ用の非標準サイクルの生成など)に対応できます。
12 個のステートマシンは協調パイプラインとして動作します。以下の図は典型的なメモリリードサイクルのフローを示しています:
コア 1(C コード) PIO ステートマシン
────────────── ──────────────────
1. メモリマップから z80_cycle: IRQ 0 セット
アドレスを解決 (作業待ち)
│
2. アドレス → TX FIFO プッシュ ────────────→ z80_addr: アドレス受信
IRQ 0 クリア A0–A15 をピンに出力
│
3. サイクル命令プッシュ ──────────────────→ z80_cycle: out exec, 16
(例: mem_read シーケンス) 実行: /MREQ ロー設定
サイクル SM TX FIFO へ 実行: /RD ロー設定
│ 実行: CLK エッジ待機
4. RX FIFO 待機 ←──────────────────────── z80_data: D0–D7 サンプリング
(バスからのデータバイト) RX FIFO にプッシュ
│
5. RX FIFO から z80_cycle: JMP start_cycle
データ読み取り (次のサイクル準備完了)
│
6. PSRAM またはドライバー
ハンドラーにディスパッチ
IRQ ベースのハンドシェイクにより、制御信号がアサートされる前にアドレスバスが安定していること、およびバスサイクルの正しいポイントでデータがサンプリングされることが保証されます。ステートマシンはポーリングを一切行いません。
Z80 フェッチサイクル(M1 サイクル)— ステップバイステップ
wait 0 irq N を使用して関連イベントが発生するまでスリープし、待機中の CPU サイクル消費はゼロです。
オペコードフェッチは最も複雑な Z80 バスサイクルです — メモリリードとリフレッシュサイクルを組み合わせます。
z80_fetch プログラムはホストクロックの 4 T サイクルにわたって実行されます:
ホスト CLK: ──┐ ┌──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │ │ │
└──┘ └──┘ └──┘ └──┘
T1 T2 T3 T4
A0–A15: ══╤═══ PC アドレス ═════╤═══ リフレッシュアドレス ══╗
│ │ ║
/M1: ──┘ └──────────────────────────╜── (T1–T2 ロー、T3–T4 ハイ)
/MREQ: ────┘ ┌────┘ ┌──── (T1↓–T3↑ ロー、T3↓–T4↓ リフレッシュ用)
/RD: ────┘ ┌─────────────────────── (T1↓–T3↑ ロー)
/RFSH: ────────────────────┘ ┌── (T3↑–T4↓ ロー)
D0–D7: ═══════════════╤═══╗ (T3↑ でサンプリング)
│ ║
オペコード読み取り
PIO プログラムはこれを以下のように実装します:
メモリリードとライトサイクル
- T1 立ち上がりエッジ —
z80_addrSM が PC 値を A0–A15 に出力。z80_fetchがset pinsで/M1をローにアサート。 - T1 立ち下がりエッジ —
/MREQと/RDがローにアサート(set pinsとサイドセットを使用)。アドレスが有効になり、メモリシステムが応答を開始可能。 - T2 — SM がウェイトステートループに入る:CLK 立ち上がりから立ち下がりエッジを待ち、
jmp pinで/WAITピンを確認。/WAITがローなら SM はループ(Tw サイクル追加)。ハイなら T3 に進む。 - T3 立ち上がりエッジ —
in pins, 8で D0–D7(オペコードバイト)をサンプリングし RX FIFO にプッシュ。IRQ 1 をセットしてz80_data/z80_addrにリフレッシュアドレスの出力を通知。/M1、/MREQ、/RDをデアサート。/RFSHをローにアサート。 - T3 立ち下がりエッジ —
/MREQを再度アサート(リフレッシュ行ストローブ用)。 - T4 — リフレッシュ継続。T4 終了時に
/MREQと/RFSHをデアサート。サイクル SM はstart_cycleに戻り、次のバストランザクションの準備完了。
メモリリードとライトサイクルはフェッチよりシンプルです — リフレッシュフェーズなしで 3 T サイクルにわたります。
メモリリード:
ホスト CLK: ──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │
└──┘ └──┘ └──┘
T1 T2 T3
A0–A15: ══╤═══ アドレス ══════╗
/MREQ: ────┘ ┌──── (T1↓–T3↓ ロー)
/RD: ────┘ ┌──── (T1↓–T3↓ ロー)
D0–D7: ═══════════════╤═══╗ (T3↓ でサンプリング)
メモリライト:
ホスト CLK: ──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │
└──┘ └──┘ └──┘
T1 T2 T3
A0–A15: ══╤═══ アドレス ══════╗
D0–D7: ══════╤═══ データ ════╗ (T2 以降駆動)
/MREQ: ────┘ ┌──── (T1↓–T3↓ ロー)
/WR: ──────────┘ ┌──── (T2↓–T3↓ ロー)
リードの場合、
I/O リードとライトサイクル
z80_mem_read SM は T1 立ち下がりエッジで /MREQ と /RD をアサートし、T2 を待機(/WAIT でウェイトステートを確認)、その後 T3 立ち下がりエッジで in pins, 8 を使用してデータバスをサンプリングします。ライトの場合、z80_mem_write は T1 立ち下がりエッジで /MREQ をアサートし、z80_data がデータバスを駆動した後、T2 立ち下がりエッジで /WR をアサートします。どちらも T3 の終わりにすべての制御信号をデアサートします。
Z80 I/O サイクルは
/MREQ の代わりに /IORQ を使用し、T2 と T3 の間に常に自動ウェイトステート(Tw)が挿入されます。これは Z80 のアーキテクチャ上の特徴で、低速な I/O デバイスに応答する時間を与えます:
I/O リード:
ホスト CLK: ──┐ ┌──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │ │ │
└──┘ └──┘ └──┘ └──┘
T1 T2 Tw T3
A0–A15: ══╤═══ ポートアドレス ═══════╗
/IORQ: ──────┘ ┌──── (T2↑–T3↓ ロー)
/RD: ──────┘ ┌──── (T2↑–T3↓ ロー)
D0–D7: ═══════════════════════╤═══╗ (T3↓ でサンプリング)
z80_io_read SM は T2 立ち上がりエッジで /IORQ と /RD をアサートします(メモリサイクルの T1 ではなく — これは Z80 仕様に準拠)。自動 Tw ウェイトステートはメモリサイクルと同じ jmp pin / wait ループパターンで実装されます。I/O ライトは /RD の代わりに /WR を使用する同様のパターンに従います。
z80_busrq SM(PIO 2 SM 0)はホストの /BUSREQ 入力ピンを監視します。/BUSREQ がアクティブ(ロー)になると、SM は以下を実行します:
- IRQ 6 をセットして、バスリクエストが保留中であることをサイクル SM に通知。
- 現在のバスサイクルの完了を待機(
wait 1 irq 0)。 - TX FIFO から 32 ビット制御ワードを取得し、バス解放状態のピン方向と値を指定 — アドレスバスとデータバスをトライステートにし、
/BUSACKをローにアサート。 /BUSREQがインアクティブ(ハイ)になるまでjmp pinでスピン。- 2 番目の 32 ビットワードを取得して通常のピン方向を復元し、
/BUSACKをデアサート。 - IRQ 6 をクリアし、サイクル SM の再開を許可。
wait 0 irq 6 で毎サイクルの開始時に IRQ 6 を確認します。フラグがセットされていると、バスリクエストが完了するまで SM がストールします。これにより、バス解放はサイクル間でクリーンに発生し、サイクル途中では決して発生しません。
すべてのサイクルタイプ SM は
割り込みアクノリッジサイクル
wait 1 gpio Z80_PIN_CLK(立ち上がりエッジ待機)と wait 0 gpio Z80_PIN_CLK(立ち下がりエッジ待機)を使用してホスト Z80 クロックに同期します。これは以下を意味します:
- PIO プログラムはクロック周波数に依存しない — DC から RP2350 が追跡可能な最大レートまで、あらゆるホストクロック速度で動作します(PIO システムクロックと GPIO サンプリングレートによって制限)。
- RP2350 の 300 MHz PIO クロックは 3.5 MHz の Z80 T ステートあたり約 85 PIO サイクルを提供し、クロックエッジ間で PIO 命令の実行、FIFO のプッシュ/プル、IRQ フラグの確認に十分すぎる時間があります。
z80_syncSM(PIO 2 SM 1)は T1 同期 IRQ を提供し、C コードが PSRAM アクセスをホストクロックに合わせて整列させ、クロックセンシティブなホストソフトウェアのタイミングドリフトを防ぎます。z80_clk_syncSM(PIO 2 SM 2)はホストクロックを別の GPIO に再生成し、外部モニタリングやロジックアナライザーのトリガー用にクリーンなクロック出力を提供します。
z80_int_ack SM(PIO 2 SM 3)は Z80 割り込みアクノリッジシーケンスを実装します。C コードが割り込み条件を検出すると、int_ack プログラムをロードします。このサイクルはフェッチに似ていますが、重要な違いがあります:
/M1は T1 でアサートされます(フェッチと同様)が、ウェイトステート(Tw1)で/MREQの代わりに/IORQがアサートされます。- 2 つの自動ウェイトステート(Tw1、Tw2)が挿入され、割り込みデバイスがデータバスにベクターを配置する時間を確保します。
- ベクターバイトが D0–D7 から読み取られ、RX FIFO にプッシュされます。
- フェッチのリフレッシュフェーズと同じリフレッシュサイクルが続きます。
メモリモデル
メモリアクセスはレイテンシーが増加する 3 つの階層を通じて解決されます。この 3 層設計により、一般的なケース(PSRAM バックの RAM/ROM)が高速でありながら、仮想デバイスと物理ホストパススルーに最大限の柔軟性を提供します。
階層 1 — RP2350 SRAM ディスパッチテーブル
RP2350 の 512KB オンチップ SRAM に常駐する 32 ビット
membankPtr 値の 128 エントリ配列が、すべてのバストランザクションに対して O(1) ブロックタイプルックアップを提供します。1 エントリが 64KB Z80 アドレス空間の 512 バイトブロックをカバーします(128 × 512 = 65,536 バイト)。各エントリはエンコードされています:
- ブロックタイプ(PHYSICAL、RAM、ROM、FUNC など)。
- PSRAM バックブロックの場合:PSRAM バンク番号とオフセット。
- FUNC ブロックの場合:仮想デバイス関数ポインターテーブルへのインデックス。
階層 2 — 外部 PSRAM
8MB の PSRAM は以下のように編成されています:
- 64 バンク × 64KB — Z80 アドレス空間の RAM または ROM イメージデータ。
- 64KB
memPtr— PTR タイプブロックのバイト単位リダイレクトポインター配列。 - 64KB
memioPtr— メモリマッピング FUNC デバイスの関数ポインター配列。 - 64KB
ioPtr— I/O ポート FUNC デバイスの関数ポインター配列。
階層 3 — 16MB SPI フラッシュ
ROM イメージは起動時にフラッシュ(または ESP32 経由の SD カード)から PSRAM に読み込まれます。実行時にはバストランザクション用にフラッシュにアクセスしません — すべての ROM データは PSRAM から提供されます。フラッシュは以下に使用されます:
- ブートローダーとアプリケーションファームウェア。
- 最小化された
config.json(各起動時に SD カードからキャッシュ)。 - App 設定パーティションの ROM イメージ(SD カードがない場合に使用)。
メモリブロックタイプ
| タイプ | 説明 |
|---|---|
PHYSICAL |
パススルー — RP2350 がバスを解放し、物理ホストメモリが応答する。ホストのネイティブ ROM と RAM に使用。 |
PHYSICAL_VRAM |
PHYSICAL と同じだがホストビデオ RAM タイミング用の追加ウェイトステートあり。MZ-700/MZ-80A VRAM 領域に適切。 |
PHYSICAL_HW |
ホストハードウェアレジスタのパススルー(メモリ空間の I/O マッピングデバイス)。 |
RAM |
読み書き — PSRAM バンクによってバック。RP2350 が PSRAM への読み書きをサービスする。 |
ROM |
読み取り専用 — PSRAM バンクによってバック。書き込みサイクルはサイレントに無視される(ホストは通常のバスタイミングを見るがデータは保存されない)。 |
VRAM |
PSRAM バックビデオ RAM。書き込みサイクルは PSRAM と物理ホスト VRAM の両方に同時にミラーリングされる。 |
FUNC |
仮想デバイス — 各アクセスは memioPtr または ioPtr 関数ポインターテーブルを介して C 関数呼び出しをトリガーし、任意の I/O エミュレーションを可能にする。 |
PTR |
バイト単位リダイレクト — 512 バイトブロックの各バイトが独立して他のブロックタイプまたは PSRAM の場所を指すことができる。 |
設定リファレンス
すべての picoZ80 の動作は SD カードの
config.json で制御されます。RP2350 は起動時にこのファイルを読み取り最小化して、フラッシュに保存します。その後の起動では SD カードがない場合はフラッシュのコピーを使用します。
トップレベルの JSON 構造:
{
"esp32": {
"core": { ... },
"wifi": { ... }
},
"rp2350": {
"core": { ... },
"z80": [ { "memory": [...], "io": [...], "drivers": [...] } ]
}
}
esp32.core
| キー | タイプ | 説明 |
|---|---|---|
device |
文字列 | CPU ペルソナ — picoZ80 には "Z80"、pico6502 には "6502"、pico6512 には "6512"。 |
mode |
整数 | デフォルト WiFi 起動モード:0 = クライアント(ステーション)、1 = アクセスポイント。 |
esp32.wifi
| キー | タイプ | 説明 |
|---|---|---|
override |
0/1 | マスタースイッチ:1 = 以下のすべての設定を適用;0 = 永続化された NVS 設定を使用。 |
wifimode |
文字列 | "ap" = アクセスポイントモード;"client" = ステーション/クライアントモード。 |
ssid |
文字列 | 作成する(AP)または参加する(クライアント)WiFi ネットワーク名。 |
password |
文字列 | WiFi パスフレーズ。 |
ip |
文字列 | 固定 IP アドレス(例:"192.168.1.192")。 |
netmask |
文字列 | サブネットマスク(例:"255.255.255.0")。 |
gateway |
文字列 | デフォルトゲートウェイ(例:"192.168.1.1")。 |
dhcp |
0/1 | クライアントモード:1 = DHCP;0 = 固定 IP 設定を使用。 |
webfs |
文字列 | SD カード上のウェブファイルシステムのルートディレクトリ(デフォルト "webfs")。 |
persist |
0/1 | 1 = 再起動をまたいで永続化するために解決された設定を NVS に書き込む。 |
rp2350.core
| キー | タイプ | 説明 |
|---|---|---|
cpufreq |
整数 | RP2350 システムクロック(Hz 単位、例:300000000)。最大安定周波数は PSRAM 周波数とコア電圧に依存する。 |
psramfreq |
整数 | PSRAM SPI クロック(Hz 単位、例:133000000)。 |
voltage |
浮動小数点 | RP2350 コア電圧(ボルト単位、例:1.10)。高クロック速度にはより高い電圧が必要。 |
z80[].memory — メモリマップエントリ
memory 配列は Z80 メモリマップを定義します。エントリはアドレス順に並べる必要があります。領域は 512 バイトに揃え、512 バイトの倍数でサイズを指定する必要があります。エントリ間のギャップは PHYSICAL パススルーとして扱われます。
| キー | タイプ | 説明 |
|---|---|---|
enable |
0/1 | このエントリがアクティブかどうか。無効なエントリは起動時に無視される。 |
addr |
16 進文字列 | Z80 アドレス空間の開始アドレス(例:"0x0000")。512 バイト境界に合わせる必要がある。 |
size |
16 進文字列 | バイト単位の領域サイズ(例:8KB の場合は "0x2000")。512 の倍数でなければならない。 |
type |
文字列 | ブロックタイプ — メモリブロックタイプを参照。 |
bank |
整数 | RAM/ROM/VRAM/FUNC タイプの PSRAM バンク番号(0–63)。 |
tcycwait |
整数 | この領域の各アクセスに挿入する追加 T サイクルウェイトステート。 |
tcycsync |
0/1 | この領域に T1 同期を有効化する。タイミングセンシティブな領域に必要。 |
task |
文字列 | FUNC タイプブロックのオプションタスク識別子(ドライバーバインディング文字列)。 |
file |
文字列 | 起動時に PSRAM バンクにプリロードする ROM イメージへの SD カードパス(例:"/ROM/mz700.rom")。 |
fileofs |
整数 | ROM イメージファイルから読み取りを開始するバイトオフセット。 |
z80[].io — I/O ポートマップエントリ
io 配列は Z80 I/O ポート範囲をブロックタイプにマッピングします。I/O エントリには PHYSICAL と FUNC タイプのみが意味を持ちます。
| キー | タイプ | 説明 |
|---|---|---|
enable |
0/1 | この I/O エントリがアクティブかどうか。 |
addr |
16 進文字列 | 開始 I/O ポートアドレス(例:"0xE0")。 |
size |
16 進文字列 | 連続するポートの数(例:ポート E0–E3 の場合は "0x04")。 |
type |
文字列 | PHYSICAL = ホストにパス;FUNC = C ハンドラ関数を呼び出す。 |
task |
文字列 | FUNC タイプエントリのドライバーバインディング文字列。 |
z80[].drivers — ドライバーインスタンス
drivers 配列は仮想デバイスドライバーをインスタンス化してメモリまたは I/O 領域にバインドします。各ドライバーにはタイプ(C ドライバーモジュール)、名前(インスタンス識別子)、そのドライバーインスタンスの ROM イメージ、アドレスマップ、I/O マップ、パラメータを定義する 1 つ以上のインターフェースオブジェクトがあります。
"drivers": [
{
"type": "WD1773",
"name": "FDC",
"interfaces": [
{
"name": "FDC_0",
"type": "FDC",
"rom": [],
"addrmap": [],
"iomap": [
{ "enable": 1, "addr": "0xE0", "size": "0x04", "type": "FUNC" }
],
"param": [
{ "name": "tracks", "value": "80" },
{ "name": "heads", "value": "2" },
{ "name": "sectors", "value": "8" },
{ "name": "image", "value": "/DSK/disk1.dsk" }
]
}
]
}
]
組み込みドライバー(INCLUDE_SHARP_DRIVERS)
ファームウェアが
INCLUDE_SHARP_DRIVERS でビルドされた場合、以下のドライバーモジュールがコンパイルされ drivers 配列を介してインスタンス化できます:
| ドライバー | タイプ文字列 | 説明 |
|---|---|---|
MZ700.c |
MZ700 |
Sharp MZ-700 バンク切り替え、ビデオ、キーボード I/O |
MZ80A.c |
MZ80A |
Sharp MZ-80A ドライバー — モニター ROM (SA-1510)、VRAM、Intel 8253 PIT エミュレーション、8255 PPI、MEMSW/MEMSWR メモリスワップ(CP/M バンク切り替え含む)、物理+仮想ミックスドモード対応、RFS/MZ80AFI/MZ-1E14/MZ-1E19/MZ-1R12/MZ-1R18 サブインターフェース対応 |
MZ2000.c |
MZ2000 |
Sharp MZ-2000 — 物理モード(実 MZ-2000 ハードウェアでのドロップイン Z80 置換、ブート/通常モード自動検出)と仮想モード(PSRAM ベースの完全エミュレーション、IPL ROM ミラーリング)の両方をサポート。BST/NST メモリモード切替、キャラクター+グラフィックス VRAM オーバーレイ、8253 PIT、8255 PPI、Z80 PIO、MB8866 FDC |
MZ80AFI.c |
MZ80AFI |
Sharp MZ-80A フロッピーインターフェース — MZ-80A AFI フロッピーディスクコントローラーのエミュレーション |
WD1773.c |
WD1773 |
WD1773 FDC — 80 トラック、2 ヘッド、8 セクター DSK、RAW、D88 イメージフォーマット対応 |
QDDrive.c |
QDDRIVE |
Sharp QuickDisk ドライブ — フル Z80 SIO/2 エミュレーション、スパイラルトラックデータ、モーター制御、非同期 SD カード I/O |
RFS.c |
RFS |
ROM ファイリングシステム — SD カードからの MZF 読み込み、CP/M、BASIC |
TZFS.c |
TZFS |
TranZPUter ファイリングシステム(開発中) |
MZ-1E05.c |
MZ1E05 |
Sharp MZ-1E05 フロッピーディスクインターフェースユニット(WD1773 ベース) |
MZ8BFI.c |
MZ8BFI / E0054PA |
MZ-2000 フロッピーディスクインターフェース — ドライバー ROM なし MB8866 FDC(コードは IPL 内)。D88 フォーマット対応。 |
MZ-1E14.c |
MZ1E14 |
BIOS ROM 付き MZ-1E14 QuickDisk コントローラ(MZ-700/MZ-800) |
MZ-1E19.c |
MZ1E19 |
BIOS ROM なし MZ-1E19 QuickDisk コントローラ |
MZ-1R12.c |
MZ1R12 |
32KB バッテリーバックアップ RAM ボード(SD カードに永続化) |
MZ-1R18.c |
MZ1R18 |
64KB RAM 拡張ボード |
PIT8253.c |
PIT8253 |
スタンドアロン Intel 8253 PIT エミュレーション — 全 6 カウンターモード、BCD/バイナリ、ラッチ、LSB/MSB 読み書きモード対応 |
PPI8255.c |
PPI8255 |
スタンドアロン Intel 8255 PPI エミュレーション — モード 0 I/O、ビットセット/リセット、出力コールバック、入力インジェクション対応 |
仮想デバイスフレームワーク
FUNC ブロックタイプは各バスアクセスで C ハンドラ関数を呼び出すことで任意の I/O エミュレーションを可能にします。メモリの任意の 512 バイトブロックまたは I/O ポートの範囲を関数でバックすることができます。
ハンドラ関数シグネチャ
メモリ FUNC ハンドラは PSRAM の
memioPtr テーブルに保存されます。I/O FUNC ハンドラは ioPtr テーブルに保存されます。関数シグネチャは以下の通りです:
/* メモリ読み取りハンドラ */ uint8_t mem_read_handler(uint16_t addr, void *ctx); /* メモリ書き込みハンドラ */ void mem_write_handler(uint16_t addr, uint8_t data, void *ctx); /* I/O 読み取りハンドラ */ uint8_t io_read_handler(uint8_t port, void *ctx); /* I/O 書き込みハンドラ */ void io_write_handler(uint8_t port, uint8_t data, void *ctx);
ハンドラ関数はコア 1 のホットループから直接呼び出されます。現在のバスサイクルのウェイトステートが切れる前に完了しなければなりません — ハンドラは短く、ブロッキング操作(ファイル I/O、UART など)は避けてください。ハンドラがより長い操作(例えばディスクセクターの読み込み)をトリガーする必要がある場合は、コア間キューを介してコア 0 にメッセージを送信して直ちに返すべきです。実際の I/O はコア 0 に委ねます。
新しいドライバーの作成
新しいペリフェラルまたはホストマシンのサポートを追加するには:
src/drivers/ディレクトリに新しい.c/.hファイルを作成する。- 上記のシグネチャに一致する読み取りと書き込みハンドラ関数を実装する。
- ドライバー初期化中に
memioPtrまたはioPtrテーブルにハンドラ関数ポインターを登録する。 - CMakeLists.txt ビルドターゲットにドライバーを追加する。
- JSON 設定パーサーが名前でドライバーをインスタンス化できるようにタイプ文字列エントリを追加する。
- ドライバーのヘッダーファイルにドライバーの
paramキーを文書化する。
config.json が解析された後、起動時に一度呼び出されます。ドライバーはインターフェース設定ブロックへのポインターを受け取り、この時点で内部状態を設定してハンドラを登録すべきです。
ICE(デバッグシェル)
picoZ80 には USB CDC チャネル 1(ボードを USB 接続した際に列挙される 2 番目のシリアルポート)で動作する組み込み ICE(インサーキットエミュレーター)デバッグシェルが含まれています。シェルはコア 0 で動作し、Z80CPU コンテキスト構造体の共有フラグを介してコア 1 のエミュレーションループと通信します。
アーキテクチャ
- 入出力:USB CDC チャネル 1、115200 baud。シェルプロンプトは
dbg>。コマンド履歴(16 エントリ)とキャラクターエコーをサポート。 - ブレークポイント:最大 8 個の同時ブレークポイントを
cpu->dbgBpAddr[]に格納。コア 1 は各オペコードフェッチ前にブレークポイント配列をチェックし、ヒット時にcpu->hold = trueを設定してdbgBpHit経由でコア 0 に通知。 - シングルステップ:
stepコマンドはcpu->dbgStepCountを設定。コア 1 は各命令後にこのカウンターをデクリメントし、ゼロに達すると自動的にホールド。各ステップで前後のレジスタ状態とディスアセンブルされた命令が表示される。 - 実行トレース:512 エントリのリングバッファ(
cpu->dbgTrace[])がトレース有効時に各実行命令の PC、オペコード、フラグレジスタを記録。各 32 ビットエントリは[31:16]=PC, [15:8]=opcode, [7:0]=F registerにパック。 - メモリアクセス:物理メモリアクセス(
dm p、wm p)は PIO ステートマシンを介して実 Z80 バスサイクルを駆動。仮想アクセス(dm v、wm v)は PSRAM を直接読み書き。自動モード(修飾子なしのwm)はメモリマップに従う。RP2350 アクセス(dm r)は範囲検証付きでホストマイクロコントローラーのアドレス空間を読み取り。 - ホールド/リリース:
holdコマンドはcpu->hold = trueを設定。コア 1 はcpu->holdAckで応答し、シェルが共有状態にアクセスする前に CPU が静止していることを保証。
コマンドリファレンス
| コマンド | 構文 | 説明 |
|---|---|---|
help |
help |
全コマンドの一覧表示 |
regs |
regs |
全 Z80 レジスター、フラグ、サイクルカウントをダンプ |
dm |
dm [p|f|v|r] <addr> [len] |
メモリダンプ(物理 / フェッチ / 仮想 / RP2350) |
dis |
dis [p|v] [addr] [count] |
Z80 コードをディスアセンブル |
asm |
asm [addr] |
インタラクティブ Z80 アセンブラー |
memmap |
memmap [block] |
メモリバンクポインターテーブルを表示 |
memptr |
memptr [addr] |
PSRAM memPtr テーブルを表示 |
iomap |
iomap [port] |
I/O ポートハンドラーテーブルを表示 |
status |
status |
システムステータス(CPU 周波数、PSRAM、稼働時間) |
ver |
ver |
ファームウェアバージョンとパーティション情報 |
drivers |
drivers |
アクティブなドライバーとインターフェースの一覧 |
hold |
hold |
CPU エミュレーションを一時停止 |
release |
release |
CPU エミュレーションを再開 |
go |
go |
実行を継続(ホールド解除、ブレークポイントはアクティブ) |
cont |
cont |
go のエイリアス。現在のホールドポイントから実行を続行。ブレークポイントはアクティブなまま |
step |
step [n] |
n 命令をシングルステップ実行 |
bp |
bp <addr> |
ブレークポイントを設定(最大 8 個) |
bc |
bc <n|*> |
ブレークポイント n または全てをクリア |
bl |
bl |
ブレークポイントの一覧表示 |
wm |
wm [p|v] <addr> <byte>... |
メモリに書き込み(物理/仮想/自動) |
fill |
fill [p|v] <addr> <len> [w|d] <val> |
メモリを定数値で充填 |
in |
in <port> |
Z80 I/O ポートを読み取り |
out |
out <port> <byte> |
Z80 I/O ポートに書き込み |
trace |
trace <on|off|dump [n]|clear|rt|byte> |
実行トレースの制御。rt でリアルタイムトレース出力。byte でバイトレベルトレース |
set |
set <reg|flags|memmap|memptr|iomap> |
Z80 レジスター、フラグ、メモリマップ、memPtr、I/O マップエントリを実行時に変更 |
hist |
hist [n] |
コマンド履歴を表示(ESP32 NVS に保存) |
save |
save |
コマンド履歴を ESP32 NVS に即座に強制保存 |
fdctrace |
fdctrace <on|off|dump> |
FDC I/O トレースログの有効/無効切替。dump で最後の 64 回の操作をダンプ |
qdtrace |
qdtrace <on|off|dump> |
Quick Disk I/O トレースログの有効/無効切替。dump で最後の 64 回の操作をダンプ |
piodbg |
piodbg [clear] |
全 3 PIO ブロックの RP2350 PIO ハードウェア診断を表示。clear で FDEBUG スティッキーフラグをリセット |
cmp |
cmp [f] <phys> <virt> <len> |
物理バスメモリと仮想 PSRAM を比較。f でフェッチサイクルを使用 |
copy |
copy <pv|fp|vp> <src> <len> <dst> |
物理メモリと仮想メモリ間でコピー |
memtest |
memtest <addr> <len> [pattern] |
物理メモリを 3 パスでテスト(書き込み+読み取り、書き込み+フェッチ、インターリーブ) |
verify |
verify <on|off> |
全オペコードフェッチ検証の切り替え |
fwait |
fwait <0-4> |
M1(オペコードフェッチ)サイクルに追加ウェイトステートを強制。0 = オフ(デフォルト)。パラメータなしで現在の設定を表示 |
iowait |
iowait <0-8> |
I/O 読み書きサイクルに追加ウェイトステートを強制。0 = オフ(デフォルト)。パラメータなしで現在の設定を表示 |
corrupt |
corrupt [clear] |
検出されたフェッチ破損を表示。clear でログをリセット |
echo |
echo [on|off] |
ターミナルエコーの切り替え |
reset |
reset |
Z80 をリセット |
ESP32 コプロセッサ
ESP32-S3-PICO-1 モジュールはすべてのネットワークとストレージ機能を処理するコプロセッサとして機能します。2 つのインターフェースで RP2350 と通信します:
- FSPI(50MHz、4 線 SPI) — Binary IPC Protocol v1.1 — 高速バルクデータ転送(ROM イメージ、ディスクセクター読み書き、設定ファイルダウンロード)。プロトコルは固定 64 バイトバイナリフレームヘッダーと CRC32 整合性チェック(以前の XOR チェックサムを置き換え)を使用します。DMA チャネルは初期化時に事前割り当てされ解放されないため、転送ごとの claim/unclaim オーバーヘッドと競合状態を排除します。バーストセクター転送は単一の SPI トランザクションで最大 16 × 512 バイトセクター(8KB)を可能にし、フロッピーおよび QuickDisk イメージの読み込み時間を大幅に短縮します。RX DMA チャネルは HIGH PRIORITY に昇格され、コア 1 の PSRAM QMI バス競合による FIFO オーバーフローを防止します。
- UART(460.8kbaud) — 制御メッセージ、ステータスクエリ、短いデータ交換のためのコマンド/レスポンスプロトコル。
ネットワーキングモード
ESP32 ファームウェアは 3 つのネットワーキングモードをサポートしており、ビルド時にプリビルト
sdkconfig ファイルで選択します:
| モード | 設定ファイル | WiFi | USB NCM | コンソール | FCC/RED 必要 |
|---|---|---|---|---|---|
| WiFi のみ | sdkconfig.mode_wifi_only |
Yes | No | USB Serial/JTAG | Yes |
| WiFi + NCM | sdkconfig.mode_wifi_and_ncm |
Yes | Yes | TinyUSB CDC-ACM | Yes |
| NCM のみ | sdkconfig.mode_ncm_only |
No | Yes | TinyUSB CDC-ACM | No |
USB NCM (Network Control Model) は ESP32-S3 USB OTG ポート(GPIO 19/20)上に CDC-NCM Ethernet アダプターを提供します。コンポジット USB デバイスが CDC-ACM シリアルポート(デバッグログ用)と NCM ネットワークインターフェースの両方を公開します。内蔵 DHCP サーバーが
192.168.7.0/24 サブネットからホストに IP アドレスを割り当て、picoZ80 は 192.168.7.1 でアクセス可能です。リース時間は 120 分です。
WiFi+NCM モードでは、HTTP サーバーは INADDR_ANY:80 にバインドし、両方のインターフェースを同時に提供します。WiFi は非同期で接続するため、USB NCM インターフェースは電源投入後すぐに利用可能です。
NCM のみが有効な場合、WiFi 無線は完全に無効化され、WiFi マネージャーページはウェブインターフェースから非表示になります。ESP32-S3 アンテナ整合回路は PCB 上に実装する必要がありません。
SD カードインターフェース
ESP32 は SPI インターフェースを介して SD カードを管理します。SD カードは FAT32 としてマウントされ、RP2350 からのすべてのファイルアクセスは ESP32 が仲介します — RP2350 は FSPI/UART リンク経由でファイル I/O コマンドを送信し、ESP32 が実際の FAT32 読み書き操作を実行します。
SD カードは ESP32 ウェブサーバーからも直接アクセス可能で、
webfs/ ディレクトリからファイルを提供し、ファイルマネージャーが HTTP 経由でカードの内容を参照・変更することを可能にします。
ウェブサーバー
ESP32 はポート 80 で HTTP サーバーを実行します(TLS なし — ローカルネットワーク使用のみ)。すべてのウェブアセット(HTML、CSS、JavaScript)は SD カードの
webfs/ ディレクトリから提供され、ESP32 ファームウェアを再フラッシュせずにウェブインターフェースを更新できます。ウェブサーバーは以下を処理します:
- SD カードの
webfs/ディレクトリからの静的ウェブアセットの提供。 - JSON データ(システムステータス、設定の読み書き、ファイル操作)の REST API エンドポイント。
- RP2350 と ESP32 の両方の OTA ファームウェアアップロードエンドポイント。
- リアルタイムダッシュボードステータス更新のための WebSocket 接続。
RP2350 ↔ ESP32 コマンドプロトコル
RP2350(コア 0)は UART リンクを介したシンプルなコマンド/レスポンスプロトコルで ESP32 と通信します。コマンドはオプションのペイロードバイトを持つ 1 バイトのオペコードです。ESP32 は各コマンドに対してステータスバイトとその後の応答データで応答します。
一般的なコマンドカテゴリ:
- ファイル I/O — オープン、読み取り、書き込み、クローズ、ディレクトリリスト、ファイル情報。
- 設定 — config.json の内容を要求、更新された設定を書き込む、リロードリクエスト。
- ディスク — ディスクイメージのマウント/アンマウント、セクター読み書き(WD1773 および QDDrive エミュレーションからリレー)。現在マウントされているフロッピーおよび QuickDisk イメージのファイル名は ESP32 が追跡し、ウェブインターフェースの Actions メニューに表示されます。
- システム — バージョンクエリ、再起動リクエスト、NVS 読み書き。
SWD デバッグ — RP2350
RP2350 は ARM シリアルワイヤデバッグ(SWD)を介したフルソースレベルデバッグをサポートします。CMSIS-DAP 互換プローブ(Raspberry Pi Debug Probe、Black Magic Probe、または類似品)をデバッグヘッダーのピン 1(SWCLK)、ピン 2(SWDIO)、ピン 5(GND)に接続します。
OpenOCD セットアップ
picoZ80 は、コアごとに独立した GDB ポートを持つ SMP デバッグを有効化するために標準の OpenOCD RP2350 ターゲットスクリプトへの小さな変更を必要とします:
sudo cp /usr/local/share/openocd/scripts/target/rp2350.cfg \
/usr/local/share/openocd/scripts/target/rp2350_tzpu.cfg
rp2350_tzpu.cfg を編集します — if {[string compare $_USE_CORE SMP] == 0} ブロック内の target smp 行を見つけて先頭の # を削除します:
# 変更前:
#target smp $_TARGETNAME_0 $_TARGETNAME_1
# 変更後:
target smp $_TARGETNAME_0 $_TARGETNAME_1
この 1 つの変更により OpenOCD はコア 0 を GDB ポート 3333 に、コア 1 を GDB ポート 3334 に登録し、独立したコアごとの GDB セッションを可能にします。GDB を起動する前に OpenOCD を起動します:
openocd -f interface/cmsis-dap.cfg -f target/rp2350_tzpu.cfg -c "adapter speed 5000"
GDB 設定
ディレクトリごとの
.gdbinit ファイルの自動読み込みを許可するために以下を ~/.gdbinit に追加します(プロジェクトの場所に合った絶対パスで):
set history save on set history filename ~/.gdb_history set history size 65536 add-auto-load-safe-path /path/to/project/build/bin/model/BaseZ80/.gdbinit add-auto-load-safe-path /path/to/project/build/bin/model/Bootloader/.gdbinitブートローダーのデバッグ
# ターミナル 1 — コア 0(ポート 3333) cd build/bin/model/Bootloader cp ../../../../.gdbinit.bootloader.3333 .gdbinit gdb-multiarch Bootloader.elf # ターミナル 2 — コア 1(ポート 3334) cd build/bin/model/Bootloader cp ../../../../.gdbinit.bootloader.3334 .gdbinit gdb-multiarch Bootloader.elfメインファームウェアのデバッグ
# ターミナル 1 — コア 0(ポート 3333) cd build/bin/model/BaseZ80 cp ../../../../.gdbinit.3333 .gdbinit gdb-multiarch BaseZ80_0x10020000.elf # ターミナル 2 — コア 1(ポート 3334) cd build/bin/model/BaseZ80 cp ../../../../.gdbinit.3334 .gdbinit gdb-multiarch BaseZ80_0x10020000.elf # メモリダンプ(GDB プロンプトから)— 16 進 + ASCII: (gdb) xac 0x20000000 64
xac <アドレス> <カウント> GDB コマンドは .gdbinit.3333 / .gdbinit.3334 ファイルで定義されています。メモリを 16 進と ASCII の組み合わせ出力としてダンプし、PSRAM バンクの内容やメモリマッピングデバイスの状態を調べるのに便利です。
ESP32 USB デバッグ
ESP32-S3 コプロセッサには組み込みの USB-JTAG インターフェースがあります — 外部デバッグプローブは不要です。ホスト PC から picoZ80 ボードの ESP32 USB ポートに USB ケーブルを接続します。
# ESP32-S3 用に OpenOCD を起動する openocd -f board/esp32s3-builtin.cfg # 2 番目のターミナルで — Xtensa GDB を起動する xtensa-esp32s3-elf-gdb esp32/build/main.elf (gdb) target extended-remote :3333
シンボルとアドレスが正しく一致するよう、ELF がデバイスで実行されているファームウェアと同じソースリビジョンからビルドされていることを確認してください。
ビルドシステム
picoZ80 ファームウェアは Raspberry Pi Pico SDK 2.x を使用した CMake を使用します。ビルドシステムは 2 つの独立したファームウェアイメージを生成します:ブートローダーとアプリケーション(異なるフラッシュオフセットの 2 つのスロット)。ESP32 ファームウェアは Docker 経由で管理される ESP-IDF v5.4 を使用して別途ビルドされます。
CMake ビルドターゲット
| ターゲット | 出力 | フラッシュアドレス |
|---|---|---|
Bootloader |
Bootloader.elf、Bootloader.uf2 |
0x10000000 |
BaseZ80_0x10020000 |
BaseZ80_0x10020000.elf、.bin |
0x10020000(スロット 1) |
BaseZ80_0x10520000 |
BaseZ80_0x10520000.elf、.bin |
0x10520000(スロット 2) |
主要 CMake ビルドフラグ
| フラグ | 効果 |
|---|---|
INCLUDE_SHARP_DRIVERS |
すべての Sharp MZ ペリフェラルドライバー(MZ700、WD1773、QDDrive、RFS、TZFS、MZ-1E05、MZ8BFI、MZ-1E14、MZ-1E19、MZ-1R12、MZ-1R18)をコンパイルイン。 |
INCLUDE_DBGSH |
ICE デバッグシェル(dbgsh.c)をコンパイルイン。定義時は DBGSH バリアントとしてビルドされ、USB CDC チャネル 1 で 35 コマンドの ICE デバッガーが有効化される。 |
CMAKE_BUILD_TYPE=Debug |
デバッグシンボルを有効化し最適化を無効化。ソースレベル GDB デバッグに必要。 |
CMAKE_BUILD_TYPE=Release |
フル最適化(-O3)。本番ファームウェアに使用。 |
ビルドコマンド
# 初回:SDK をクローンしてビルドする ./get_and_build_sdk.sh # 標準リリースビルド(RP2350 のみ) ./build_tzpuPico.sh # デバッグビルド ./build_tzpuPico.sh DEBUG # フルビルド:RP2350 + ESP32(ESP32 は Docker 経由でビルド) ./build_tzpuPico.sh ALL # ESP32 のみ、Docker idf54 エイリアスを使用 cd projects/tzpuPico/esp32 idf54 build
build_tzpuPico.sh スクリプトはビルド成功時にバージョン番号を自動的にインクリメントし、バージョンスタンプ付きの出力ファイルを fw/uf2/(ブートローダー UF2)と fw/bin/(OTA 用アプリケーションバイナリ)にコピーします。ブートローダー UF2 は初期 USB 大容量ストレージフラッシュにのみ使用されます。アプリケーションスロットのバイナリは非標準のフラッシュアドレスに存在するため(UF2 ではなく)プレーンバイナリ形式を使用します。
ビルドシステムは 4 つのファームウェアバリアントを生成します:標準(デバッグシェルなし)と DBGSH(ICE デバッガー付き)× 2 パーティション。DBGSH バリアントは INCLUDE_DBGSH コンパイル定義で制御されます。
ESP32 ネットワーキングモード選択
ネットワーキングモードを切り替えるには、ビルド前に適切なプリビルト設定ファイルを
sdkconfig にコピーします:
cd esp32/ cp sdkconfig.mode_ncm_only sdkconfig # NCM のみ(FCC/RED 安全) # or: cp sdkconfig.mode_wifi_only sdkconfig # or: cp sdkconfig.mode_wifi_and_ncm sdkconfig idf.py build idf.py flash
ウォッチドッグとブート診断
RP2350 ファームウェアはハードウェアウォッチドッグタイマーを使用して、ブート時のハングやメインループのストールを検出・復旧します。ウォッチドッグはブートシーケンスの初期段階で 30 秒のタイムアウトで有効化され、各主要マイルストーンで kick(
watchdog_update())されます。いずれかのブートステージまたはメインループの反復がタイムアウトを超えた場合、ウォッチドッグは RP2350 を自動的にリセットします。
起動進捗トラッキング
起動進捗は RP2350 のウォッチドッグ scratch レジスタを使用して追跡されます。scratch レジスタはウォッチドッグリセットを生き延びます(電源投入リセットでは生き延びません)。これにより、ウォッチドッグリセット後にファームウェアがハングする前にどのブートステージに到達したかを正確に判断できます。
| Scratch レジスタ | 名前 | 内容 |
|---|---|---|
scratch[0–3] |
ブート履歴 | 直近 4 回のリセット試行 — 各エントリは (attempt_count << 24) | (stage << 16) | (resetCause & 0xFFFF) をエンコード。ウォッチドッグリセットのたびにエントリがシフト:[0]←[1]←[2]←[3]←current。 |
scratch[4] |
SPI 診断 | FSPI リンクのパック済み診断カウンター:breadcrumbs、メッセージタイプ、T1/T3 タイムアウトカウンター、不正フレーム数、OK 数。 |
scratch[5] |
マジックマーカー | scratch レジスタに有効なブート進捗データが含まれていることを示す 0xB00710BE に設定。 |
scratch[6] |
現在のステージ | 最新のブートステージコード(以下の表を参照)。 |
scratch[7] |
リセット原因 | ハードウェアリセットコントローラからのリセット原因コード。 |
ブートステージコードは
0x01(開始)から 0x10(メインループ突入)まで進行します。メインループ内のサブステージ(0x11–0x17)とコア間コマンド処理(0x20–0x27)は詳細なトラッキングを提供します:
| コード | ステージ | 説明 |
|---|---|---|
0x01 |
BOOTP_START |
エントリーポイント到達 |
0x02 |
BOOTP_CLK_SET |
システムクロック設定完了 |
0x03 |
BOOTP_PSRAM_INIT |
PSRAM 初期化開始 |
0x04 |
BOOTP_PSRAM_OK |
PSRAM 初期化成功 |
0x05 |
BOOTP_STDIO_INIT |
USB stdio 初期化完了 |
0x06 |
BOOTP_PIO_INIT |
PIO ステートマシンロード完了 |
0x07 |
BOOTP_Z80_INIT |
Z80 CPU コンテキスト初期化完了 |
0x08 |
BOOTP_USB_INIT |
USB ブリッジ初期化完了 |
0x0A |
BOOTP_ESP_HS_SYNC |
ESP32 SPI ハンドシェイク同期 |
0x0B |
BOOTP_CORE1_LAUNCH |
コア 1 起動 |
0x0D |
BOOTP_FSPI_INIT |
FSPI Binary IPC 初期化完了 |
0x0E |
BOOTP_ESP_INIT |
ESP32 通信準備完了 |
0x10 |
BOOTP_MAIN_LOOP |
メインループ突入 |
0x11–0x17 |
メインループサブステージ | USB ポーリング、コア間通信、SPI NOP/CMD、タスク |
0x20–0x27 |
コア間コマンド | フロッピーロード、QD ロード、RAMFILE ロード、ファイル I/O |
PSRAM 永続ログ(plogf)
8MB PSRAM の最後の 4KB(アドレス
0x117FF000)は、ウォッチドッグリセットを生き延びる永続デバッグログ用に予約されています。plogf() マクロはブート中、通常の debugf() 出力用の USB が利用可能になる前に printf スタイルのメッセージをこのバッファに書き込みます。次回の正常起動時に dump_plog() 関数がキャプチャされたメッセージをデバッグコンソールに出力し、バッファをクリアします。ログは 4 バイトのマジックマーカー(0x504C4F47 = "PLOG")、4 バイトの長さカウンター、3840 バイトの循環テキストバッファというシンプルな構造を使用します。
フォールト診断
ファームウェアはハードフォールト、メモリ管理フォールト、バスフォールト、使用フォールトに対して Cortex-M33 フォールトハンドラをインストールします。フォールト発生時、ハンドラは PSRAM の最後の 256 バイト(アドレス
0x117FFF00)にマジックマーカー(0xFA017000)、フォールトタイプ、すべての関連レジスタ(PC、LR、SP、R0–R3、R12、PSR)、Configurable Fault Status Register(CFSR)、Hard Fault Status Register(HFSR)、Bus Fault Address Register(BFAR)、Memory Management Fault Address Register(MMFAR)、およびコア ID を含む完全な診断スナップショットを保存します。ハンドラはその後無限ループに入り、ウォッチドッグにリセットをトリガーさせます。次回起動時にファームウェアが有効なフォールト診断をチェックし、キャプチャされた情報を debugf() 経由で出力するため、ライブデバッガセッションなしで事後分析が可能です。
フラッシュ設定クリア
RP2350 OTA アップデートメカニズムはファームウェアアップロード以外に 2 つの追加操作をサポートします:
- App Config クリア(
FW_CFGCLEAR_ID = 0xB1D7E5FA)— ターゲットファームウェアスロットに関連付けられた App Config パーティション(ROM イメージとミニファイ済み JSON)を消去します。これにより、次回起動時にファームウェアが SD カードからconfig.jsonを再読み込みすることを強制します。ファームウェアバージョン間で設定スキーマが変更された場合に必要です。 - フラッシュヘッダークリア(
FW_HDRCLEAR_ID = 0xC2E8F6AB)— フラッシュパーティションヘッダーを工場出荷時のデフォルトにリセットします。ブートローダー設定(パーティション 0)は保持されますが、すべてのアプリケーションパーティションメタデータがゼロから再構築されます。パーティションテーブルが破損した場合、または異なるパーティションレイアウトを想定する以前のファームウェアバージョンにダウングレードする場合に使用します。
参考サイト
| リソース | リンク |
|---|---|
| picoZ80 プロジェクトページ | /picoz80/ |
| picoZ80 ユーザーマニュアル | /picoz80-usermanual/ |
| pico6502 プロジェクトページ | /pico6502/ |
| RP2350 データシート | datasheets.raspberrypi.com |
| RP2350 PIO リファレンス | datasheets.raspberrypi.com — 付録 B |
| Pico SDK ドキュメント | raspberrypi.github.io/pico-sdk-doxygen |
| ESP32-S3 技術リファレンス | docs.espressif.com |
| ESP-IDF プログラミングガイド | docs.espressif.com/esp-idf |
| Zilog Z80 CPU ユーザーマニュアル | zilog.com |
| OpenOCD ドキュメント | openocd.org |
| X(Twitter)プロジェクトプレビュー | engineerswork1 |
無線規制に関する注意事項
本デバイスは 2.4 GHz ISM バンドで送信する ESP32-S3-PICO-1 無線モジュールを搭載しており、WiFi ファームウェアがインストールされている場合、世界各国の無線周波数規制(米国の FCC Part 15 Subpart C、欧州連合の無線機器指令 2014/53/EU を含む)において意図的放射器に該当します。
出荷時の構成
出荷時には、picoZ80 ボードは NCM のみのファームウェア(
エンドユーザーはアンテナ整合回路を実装し、WiFi 対応ファームウェア(
本設計に基づいて製作されたデバイスが、管轄区域内の適用されるすべての無線周波数規制に準拠することは、製作者の単独の責任です。著者は本設計を個人使用、教育、および趣味愛好家向けに提供しており、本設計から製作されたデバイスが商業的配布の規制要件を満たすことについて、いかなる表明も行いません。
出荷時には、picoZ80 ボードは NCM のみのファームウェア(
sdkconfig.mode_ncm_only)でフラッシュされています。この構成では WiFi 無線は完全に無効化され、ESP32-S3 アンテナ整合回路の部品は PCB 上に実装されていません。高周波送信が発生しないため、デバイスは意図的放射器に該当せず、FCC、CE/RED、または同等の認証は不要です。規制上の認可なしに販売、配布、または贈与が可能です。
WiFi の追加エンドユーザーはアンテナ整合回路を実装し、WiFi 対応ファームウェア(
sdkconfig.mode_wifi_only または sdkconfig.mode_wifi_and_ncm)をフラッシュすることで、個人使用、実験、または教育目的で WiFi を使用できます。WiFi を有効にすると、デバイスは意図的放射器となり、以下のルールが適用されます:
ESP32-S3-PICO-1 モジュール自体は既存の規制認証(FCC、CE など)を取得していますが、そのモジュールレベルの認証は、モジュールを組み込んだ完成品に自動的に適用されるものではありません。事前認証モジュールの免除規定は、個人の趣味愛好家が個人使用、実験、または教育目的で少数のデバイスを製作する場合に、個別の機器認可を取得せずに行うことを許可するものです。
重要な制限事項- WiFi ファームウェアを搭載したデバイスは、完成品が独自にテストされ、該当する管轄区域で機器認可(例:FCC ID、認定機関による CE マーキング評価)を取得しない限り、第三者への販売、販売の申し出、贈与、またはその他の方法での配布を行ってはなりません。
- 個人使用のために WiFi を有効にして少数を製作することは、趣味愛好家および実験使用の規定(例:FCC § 15.23)に基づき、デバイスが有害な干渉を引き起こさない限り、一般的に許可されています。
- WiFi を有効にした商業販売には、完全な製品レベルの FCC/RED(または同等の)認証が必要です。
- 規制要件は国によって異なります。米国外の製作者は、適用される規則について自国の無線周波数当局に確認してください。
本設計に基づいて製作されたデバイスが、管轄区域内の適用されるすべての無線周波数規制に準拠することは、製作者の単独の責任です。著者は本設計を個人使用、教育、および趣味愛好家向けに提供しており、本設計から製作されたデバイスが商業的配布の規制要件を満たすことについて、いかなる表明も行いません。