第2章:Wishbone SoCの骨格 ― アーキテクチャ設計とIP調達の初実演
第1章で、PicoRV32の中身を分解しました。コアは「外部のメモリと、valid→readyの握手でやり取りする」ことが分かりました。本章では、この理解を土台に、いよいよSoC(System on Chip)の骨格を組みます。骨格とは、「CPUと、いくつもの部品(メモリや周辺機器)を、共通の通り道でつなぐ仕組み」のこと。その共通の通り道がバスです。本章では、なぜバスを揃えるのか、なぜWishboneを選ぶのかを腑に落とし、序章で掲げたIP調達ワークフロー(7ステップ)を、PicoRV32を題材に初めて通して実演します。
2.1 なぜ「共通のバス」に揃えるのか
SoCには、CPUのほかに、メモリ・タイマー・通信・表示……といった部品(IP)が次々に加わります。もし部品ごとに繋ぎ方がバラバラだったら、新しい部品を足すたびに専用の配線を考えなければならず、すぐに破綻します。そこで、「部品はみんな、同じ作法の口を持つ」と決めておきます。これがバスです。
バスがないと(自由配線)
部品ごとに繋ぎ方が違う。1つ足すたびに専用配線を設計。部品が増えるほど、組み合わせが爆発して手に負えなくなる。
バスがあると(共通の口)
全部品が同じ作法の口(バスインターフェース)を持つ。新しい部品も、その口さえ備えればパズルのように差し込める。これがSoCを育てられる理由。
たとえるなら: バスは「電源タップ」のようなものです。コンセントの形(作法)が世界共通だから、新しい家電を買っても、そのまま挿せば動きます。もし家電ごとに差込口の形が違ったら、毎回専用の工事が要ります。SoCのバスは、回路の部品にとっての「共通コンセント」です。
2.2 なぜ Wishbone を選ぶのか ― 主バスの決定
共通バスの「作法」には、いくつかの規格があります。代表的なのが Wishbone・AXI4-Lite・APB です。本講座は Wishbone を主バスに選びました。その理由を、正直に並べます。
Wishbone(本講座の主役)
オープンソースハードウェアの事実上の共通語。ロイヤリティフリーで、握手が cyc/stb/ack とシンプル。OpenCores由来の既製IPに採用例が多く、「IPをパズルのように繋ぐ」目的に最適。教えやすく、回路も軽い。
AXI4-Lite
業界で広く使われる強力な規格。読み・書きのチャネルが分かれ高性能だが、信号が多く握手が複雑。学習の最初の一歩としては重い。本講座では、AXIのIPを使いたいときにブリッジで取り込む方針(第5章)。
APB
低速な周辺機器向けの簡素な規格。シンプルさは魅力だが、採用IPの広がりではWishboneに一日の長。これもブリッジで取り込めます。
★決め手は「公式の後押し」もあった: 第1章の分解で、picorv32.v の中に picorv32_wb(公式のWishbone殻)が同梱されているのを見つけました。主役のCPUが最初からWishbone対応の口を用意してくれている――これは、Wishboneを主バスに選ぶ強い後押しになります。本章2.4で、この picorv32_wb の中身を現物確認し、採用を確定します。
2.3 【IP調達・初実演】最初のIPは、CPUそのもの
序章で「IPが登場するたびに7ステップの道順を見せる」と約束しました。記念すべき最初のIPは、SoCの主役――PicoRV32(CPUコア)そのものです。基礎編では「公式から取ってきて使う」とだけ書きましたが、ここではなぜそれを選び、どう確かめたかを、7ステップで丁寧にたどります。
IP調達ワークフロー:PicoRV32 を例に
───────────────────────────────────────────────────────────────
1. 要求を言語化 ・RV32Iの32bit RISC-Vコア
・FPGAに数%で載る小ささ(基礎編で確認済み)
・標準バス(できればWishbone)に繋げる口がある
・組み込み可能なライセンス
2. 探索先 RISC-V公式の周辺, GitHub("picorv32"), 各種解説・論文
3. 候補を絞る PicoRV32 / VexRiscv / Ibex 等。本講座は実績と小ささ・
学習しやすさで PicoRV32 を第一候補に
4. 現物を確認 公式リポジトリの picorv32.v を取得し自分の目で:
・先頭のライセンス=ISC形式(寛容)、著作権者 Claire Xenia Wolf
・picorv32_wb(Wishbone殻)が同梱されている ← 要求3に合致!
5. 採否を判断 採用。理由=小ささ・実績・Wishbone殻の同梱・寛容ライセンス
6. 統合 picorv32_wb を SoC のバスマスタとして据える(2.4〜2.6)
7. 検証 最小SoCで動かし、シミュ→FPGAで確認(2.7〜2.8)
ステップ4で実際に行う取得は、基礎編第6章と同じ一行です(無改造で使うので、取ってきてそのまま読みます)。
# CPUコア本体+各種ラッパ(picorv32_wb 含む)を公式リポジトリから取得(無改造で使用) curl -O https://raw.githubusercontent.com/YosysHQ/picorv32/master/picorv32.v
※ステップ4が要(AIの提案を鵜呑みにしない): 「PicoRV32にはWishbone殻がある」という情報は、AIや解説記事からも得られます。しかし本講座では、必ず自分でファイルを開いて確かめます。実際、第1章で picorv32_wb モジュールの存在と、その中の握手変換FSMを、現物で確認しました。固有名(モジュール名・作者名・ライセンス)は取り違えが起こり得ます。釣った魚は、自分の目で見てから料理する――これが規律です。
2.4 【現物確認】picorv32_wb の中身 ― 採用を確定する
採用の前に、picorv32_wb が「本当に素直なWishboneマスタか」を中身で確かめます。読んでみると、構造は明快でした。内部でCPU本体(素I/F版)を1個インスタンス化し、その素の握手を、小さな状態機械でWishboneの握手へ翻訳しているだけです。
picorv32_wb の中の翻訳 FSM(素I/F → Wishbone)
───────────────────────────────────────────────────────────────
IDLE CPUが mem_valid=1 にしたら、
番地/データ/書込許可/バイト選択を Wishbone 信号へ写し、
stb_o=1, cyc_o=1 を立てて要求開始 → WBSTART へ
WBSTART スレーブが ack_i=1 を返したら、
dat_i を mem_rdata に取り込み、mem_ready=1 でCPUへ返す。
stb_o/cyc_o を下ろして → WBEND へ
WBEND 後始末(mem_ready を戻す)→ IDLE へ
───────────────────────────────────────────────────────────────
・we_o(書込)= wstrb のいずれかが立っていれば1
・sel_o(バイト選択)= mem_wstrb をそのまま
・リセットは wb_rst_i(押す=1の正論理。Basys 3 の btnC にそのまま合う)
第1章で見た「valid→ready」の握手が、ここで「cyc/stb → ack」の握手へ、きれいに置き換わっています。一度に1件ずつ確実に処理する素直な作りで、教材SoCにそのまま使えます。採用を確定します。これでCPUは「Wishboneマスタ」として、バスの主人公になりました。
※この章の現物確認の結論(実験ログ): 「素I/FをWishbone化する回路を自作するか、公式の picorv32_wb を使うか」という、序章以来の保留(設計の分岐)に、ここで結論が出ました。公式の picorv32_wb を採用します。自作の薄ブリッジは不要です。実績ある公式実装に乗るほうが、検証の手間も少なく、堅実です。
2.5 Wishbone B4 の最小作法 ― 握手の信号
CPU(マスタ)と部品(スレーブ)が、Wishboneでやり取りする際の信号を、最小限おさえます。難しくありません。第1章の握手に、いくつか名前が増えただけです。
Wishbone の主な信号(マスタ → スレーブ/スレーブ → マスタ)
───────────────────────────────────────────────────────────────
マスタ → スレーブ
cyc 「いまバス取引が進行中」(取引のあいだ1に保つ)
stb 「この相手に有効な要求を出している」(ストローブ)
adr 要求する番地
dat_o 書き込むデータ(書き込み時)
we 書き込みか読み出しか(1=書き込み)
sel どのバイトを対象にするか(4ビット)
スレーブ → マスタ
ack 「要求を受理し、用意できた」(これで1取引が成立)
dat_i 返すデータ(読み出し時)
───────────────────────────────────────────────────────────────
1取引の流れ: cyc=1 & stb=1 で要求 → スレーブが ack=1 で応答 → 成立
本講座が使うのは、この「1取引ずつ、確実に」進める素直なやり方(クラシックなWishbone)です。高速化のためにパイプライン化する方式もありますが、まずは分かりやすさを優先します。picorv32_wb も、この素直な方式で作られていました。
2.6 部品を住み分ける ― アドレスデコーダとメモリマップ
バスにはCPU(マスタ)1人に対し、メモリやLED、UARTなど複数の部品(スレーブ)がぶら下がります。CPUが「この番地のデータがほしい」と要求したとき、どの部品が応えるべきかを振り分ける係が要ります。それがアドレスデコーダです。そして、どの番地にどの部品を割り当てるかの一覧表がメモリマップです。
本講座のSoCのメモリマップを、まず設計します(番地の割り当ては設計判断。サイズや具体値は実装で詰めます)。
本SoCのメモリマップ(設計案。上位ニブル adr[31:28] で住み分け。番地・サイズは実装で確定)
───────────────────────────────────────────────────────────────
0x0000_0000 〜 RAM(本章は命令+データ) ← PROGADDR_RESET をここに置く(第4章でFlash XIPへ移行)
0x1000_0000 〜 LED(MMIO) ← まずはここに書いてLEDを点ける
0x2000_0000 〜 GPIO(第3章で自作) ← いまは予約(席だけ用意)
0x3000_0000 〜 タイマ(第3章で自作) ← いまは予約
: :(PWM=0x4 / 外部Flash=0x5 / UART=0x6 … も後章で予約)
───────────────────────────────────────────────────────────────
※ 上位ニブル(adr[31:28])で「どの部屋か」を見分ける。本章で使うのは 0x0 と 0x1 だけ。
アドレスデコーダの考え方は単純です。CPUが出した番地の上位ビットを見て、「これはRAMの部屋」「これはLEDの部屋」と判断し、その部品にだけ stb を渡します。応答(ack)と読み出しデータ(dat_i)は、選ばれた部品のものをCPUへ返します。骨組みだけ示すと、次のイメージです。
// ※これは骨格イメージ(未検証)。完全な実装は 2.8 のビルド工程で組み立て・検証します。 // 上位ビットで住み分け(例:adr[31:28]) wire sel_ram = (wbm_adr[31:28] == 4'h0); // 0x0xxx_xxxx → RAM wire sel_led = (wbm_adr[31:28] == 4'h1); // 0x1xxx_xxxx → LED // 選ばれた部品にだけ stb を渡す assign ram_stb = wbm_stb & sel_ram; assign led_stb = wbm_stb & sel_led; // 応答(ack)と読み出しデータを、選ばれた部品から集めてCPUへ返す assign wbm_ack = (sel_ram & ram_ack) | (sel_led & led_ack); assign wbm_dat_i = sel_ram ? ram_dat : led_dat;
※これは「考え方」を示す骨格です(未検証): 上の断片は、住み分けの発想を伝えるためのスケッチです。実際に動かすには、Wishboneの作法(cycの扱い、複数スレーブの選択、未割り当て番地への応答など)を漏れなく実装し、シミュレーションで確かめる必要があります。完全なコードは、勝手に進めず、2.8で設計合意のうえ一括して組み立て・検証します。本講座の流儀(設計を固めてからコード)どおりに進めます。
2.7 最小SoC を設計する ― CPU+RAM+出力で「バスが通った」を見る
骨格の部品がそろいました。最初のゴールは大げさなものではありません。「CPUが、Wishbone越しに、メモリと出力にちゃんと届く」――この一点を、最小の構成で確かめます。具体的には、(1) Wishboneマスタ=picorv32_wb、(2) プログラムを置くRAM(Wishboneスレーブ)、(3) 出力=LED(MMIO)。この3つをデコーダでつなぎます。
最小SoC の構成(まず「バスが通る」ことを確かめる)
───────────────────────────────────────────────────────────────
┌─────────────┐ Wishbone ┌──────────────┐
│ picorv32_wb │ ──────────► │ アドレス │ ──► RAM(0x0000_0000〜)
│ (CPUマスタ) │ ◄────────── │ デコーダ │ ──► LED(0x1000_0000)
└─────────────┘ 握手 └──────────────┘
clk = 100MHz(W5), rst = btnC(U18) → wb_rst_i(押す=1)
───────────────────────────────────────────────────────────────
動作:RAM上の小さなプログラムが、LEDの番地へ書き込む → LEDが点く
=「CPU→バス→デコーダ→MMIO」の経路が、端から端まで通った証拠
CPU(picorv32_wb)の置き方は、第1章・2.4で読んだポートに素直につなぐだけです。骨格を示します(ポート名は現物のとおり。これも完全な実装は2.8で検証込みに組みます)。
// ※骨格イメージ(未検証)。picorv32_wb を SoC のバスマスタとして置く
picorv32_wb #(
.BARREL_SHIFTER(0), .COMPRESSED_ISA(0),
.ENABLE_MUL(0), .ENABLE_DIV(0), .ENABLE_IRQ(0), // まず最小構成(第1章の判断)
.PROGADDR_RESET(32'h0000_0000) // RAM先頭から実行開始
) cpu (
.trap (),
.wb_clk_i (clk), // 100MHz(必要なら分周)
.wb_rst_i (rst), // btnC:押す=1(正論理。そのまま合う)
.wbm_adr_o (wbm_adr),
.wbm_dat_o (wbm_dat_o),
.wbm_dat_i (wbm_dat_i),
.wbm_we_o (wbm_we),
.wbm_sel_o (wbm_sel),
.wbm_stb_o (wbm_stb),
.wbm_ack_i (wbm_ack),
.wbm_cyc_o (wbm_cyc),
.irq (32'b0),
.mem_instr ()
// PCPI・trace 等は未使用(開けておく)
);
※「Hello相当」をどう見せるか(設計判断・実験ログ): ロードマップでは最小SoCに「UARTでHello相当」と書いていました。ただしUARTは、序章の規律に従えば第5章でIP調達7ステップを通して正式に迎えるのが筋です。そこで本章では、「バスが端まで通った」ことを示す出力として、まずMMIOのLED書き込み(基礎編第6章でやった sw 命令の発展)を使います。文字を送る本物のUARTは第5章で。本章のゴールは「Wishboneの経路が通る」ことに絞り、通信は適切な章へ送ります。この線引きでよいか、最後にご確認ください。
2.8 【確認】最小SoC を、設計合意のうえで組み立て・検証する
ここまでで、最小SoCの設計(バス方式・メモリマップ・各部品の役割・骨格)が固まりました。本講座の流儀は「設計を固めてからコード」。次の一歩は、この設計に基づいて完全なRTLを一括で組み立て、シミュレーションとFPGAで検証することです。検証は、序章で示した段階どおりに進めます。
- RTLを一式そろえる:
picorv32.v(公式・無改造)/最小SoCトップ(デコーダ+RAMスレーブ+LED)/RAMに置く小さなプログラム(LED番地へ書き込む)。 - シミュレーションで確かめる: Verilator か Icarus Verilog で、CPUがRAMから命令を読み、LED番地への書き込みが
ackで成立するまでの握手の波形を見ます(第12章で本格化)。 - FPGA(Basys 3)で確かめる: 合成・書き込みののち、LEDが点けば「CPU→Wishbone→デコーダ→MMIO」の経路が実機で通った証拠です。
- 記録する: 利用率(CPU+バス+RAMでどれだけ使うか)と、つまずいた点を実験ログとして残します。
※完全なコードは、次の一手で: 本章は設計の合意までを担います。完全な最小SoCのRTL(cat一発のフルコード)と、その構文・シミュ確認は、ご了承いただいた次の工程で一括して用意します(勝手に進めず、確認を取ってから)。これが本講座の「実験の並走記録」としての誠実な進め方です。
2.9 この章のまとめ
- バスを揃える意味を理解した: 共通の口(バス)があるから、部品をパズルのように足してSoCを育てられる。
- 主バスを決めた: Wishbone を主役に採用。理由は共通語・簡潔・IPの広がり、そして公式のWishbone殻の存在。
- IP調達7ステップを初実演した: 最初のIP=PicoRV32を、要求の言語化から現物確認・採否まで通してたどった。
- 現物確認で分岐を解消した:
picorv32_wbの中身(IDLE→WBSTART→WBEND の握手翻訳)を読み、自作ブリッジ不要・公式採用を確定した。 - 骨格を設計した: Wishbone B4の作法、アドレスデコーダ、メモリマップ、そして最小SoC(CPU+RAM+LED)の構成を固めた。
SoCの骨格ができました。次の第3章では、この骨格に自作の周辺IP(GPIO・タイマ・PWM)を、Wishboneスレーブとして足していきます。「コアは無改造、まわりをMMIOで足す」という基礎編第6章の思想を、いよいよ本格的に展開します。