この文書は PaPoo での新規作成テスト用です。
リンク例: PaPoo.
2026/1/1 ver.e テスト書き込み
やっと完成が見えてきた?
やりたい方向性はかなり現実的です。ポイントは「1走査線まるごと保持するラインバッファ無し」でも、タイル単位(8px分など)のシフタと、走査線開始時にスプライト用シフタ群を仕込むことで、80年代VDPっぽいラスター合成が作れます。
以下、Tang Nano 9K(GW1NR-9)前提での設計の骨格です。Tang Nano 9K はHDMIコネクタや27MHzクロック、PLL等を持つ前提で話します。
Sipeed Wiki
Tang Nano 9KはHDMI出力例が既にあり(PicoRV32 + HDMI端末など)、まずはその系統(DVI相当のHDMI出力)を土台にするのが近道です。
GitHub
+1
最初のターゲット解像度は、いきなり 720p より:
640×480@60 (VGAタイミングをHDMI/DVIで出す)
が圧倒的に楽です。TMDSのシリアライズ周波数が低く、FPGAのI/Oとクロック設計が安定しやすいです。
TMDS差動出力(OBUF)の種類でハマりやすく、Tang Nano 9Kでは ELVDS を使う/使わないで挙動が変わったという報告もあります。
Reddit
大ブロックはこうです。
Timing Generator(x/yカウンタ、hsync/vsync/de、ブランキング)
BGパイプライン(タイルフェッチ + シフタ)(BG0..BGn)
Spriteパイプライン(走査線評価 + シフタ群)
Priority/Mixer(BGとSPRを優先度で合成、透明判定)
Palette(インデックス→RGB)
HDMI/DVI PHY(TMDSエンコード→シリアライズ)
「ラインバッファ無し」を成立させるコツは、BGもSPRも “今すぐ出す1px” はシフタから出す、そして 必要なデータは数〜数十サイクル先読みでシフタへ装填です。
BGがタイル(8×8, 4bppなど)なら、シフタは典型的にこうします。
1レイヤにつき
shift0..shift3(各plane 8bit)または(4bppなら)shift_nibble[0..7] みたいな8px分の保持
表示中、毎ピクセル
pixel = {shift3[7],shift2[7],shift1[7],shift0[7]} を取り出して
シフタを左シフト
x_in_tile == 7 になったら次タイル行のパターンを読んで8px分を再ロード
必要なのは「走査線まるごと」ではなく、各レイヤ 8pxぶんのシフタだけです。
BGの先読み(メモリ待ち吸収)
VRAMがBRAM/PSRAMでも、読み出しレイテンシは出ます。
そこで、例えば “表示位置xより 8〜16px 先” のタイルを先に読んでおくために、タイル境界ごとに小さなステートマシンを回します。
Stage A: NameTable(tile id / attr)読む
Stage B: PatternTable(該当行の4バイト等)読む
Stage C: シフタへロード
この3段を、1タイル(8px)ごとに回すだけで、ラインバッファ無しで安定します。
80年代VDPの定番どおり、
走査線yが確定した時点で OAM を走査し、当たるスプライトだけ抽出(例:最大 N 枚/line)
そのスプライトについて
行番号(y−sprite_y)に対応するパターンデータを読んで
スプライトごとのシフタへロード
x_countdown(表示開始までの待ち)をセット
表示中(active video)では:
x_countdown > 0 の間はカウントダウンするだけ(シフタは動かさない)
x_countdown == 0 になったらそのスプライトは毎ピクセルシフトして出力
つまり「画面全幅のスプライトラインバッファ」は不要で、
**“アクティブなスプライトのシフタ群”**だけで済みます。
注意:同時に何枚まで重なる設計にするかで規模が決まります。
32枚同時を“毎ピクセル完全並列”でやると、Tang Nano 9K規模だと他ロジックと合わせて厳しくなりやすいので、まず 8枚/line とかに制限して動かすのが安全です(後で増やす)。
ラインバッファ無しは、「必要な時に必要な分だけ読む」=読み出し頻度が増えるので帯域設計が重要です。
タイルBG(8px単位ロード)の場合、1レイヤあたり概算で:
1タイルにつき:pattern 4byte(4bpp想定) + tile属性(別読みなら追加)
1ラインのタイル数:H_ACTIVE / 8
例えば VGA 640px だと 80タイル/line。
BGが2面なら pattern 4byte×80×2 = 640byte/line 程度(+属性)で済みます。
Tang Nano 9K のGW1NR系は“内蔵pSRAM”を特徴としており、速度条件も公表されています(製品系列として16-bit/166MHz等)。
GoWinSemi
+1
ただし実効帯域はアービトレーションや実装に依存するので、最初は VRAMのホット部分をBRAM側へ置く(キャッシュ)のが堅いです。
内部解像度を 256×240 や 320×240 にして、HDMIは 640×480 にするのは簡単です。
横:同じピクセルを2回出す(xを内部1カウント進めるのは2ドットに1回)
縦:同じ走査線を2回出す(内部yを進めるのは2ラインに1回)
これも完全にラスター処理で、フレームバッファ不要です。
まずSipeed公式/既存のHDMI(DVI)出力例を動かす(テストパターンでも可)
GitHub
+1
Timing Generator を自作して、RGBを直接出してHDMIへ乗せる
BG0を1面だけ:タイル+シフタで出す(スクロール無し)
スクロール、BG面追加
スプライト:まず1枚→8枚/lineに拡張
優先度、透明色、パレット、ウィンドウ/ラスタ割り込み等
もし、あなたの想定VDP仕様(例:BG面数、タイルサイズ、4bpp/2bpp、スプライト最大サイズ、1line最大枚数、priorityルール)を箇条書きで投げてくれたら、その条件に合わせて
BGフェッチのステートマシン(何サイクル先読みが必要か)
VRAMのアドレスマップ案(Name/Pattern/Palette/OAM)
Sprite評価のやり方(HBlankで間に合うか、何クロック必要か)
を、かなり具体的な形に落とし込めます。