PaPoo
cover

PaPoo 新規作成テストPage

この文書は PaPoo での新規作成テスト用です。
リンク例: PaPoo.

2026/1/1 ver.e テスト書き込み

やっと完成が見えてきた?


タスクリスト(GFM)


やりたい方向性はかなり現実的です。ポイントは「1走査線まるごと保持するラインバッファ無し」でも、タイル単位(8px分など)のシフタと、走査線開始時にスプライト用シフタ群を仕込むことで、80年代VDPっぽいラスター合成が作れます。

以下、Tang Nano 9K(GW1NR-9)前提での設計の骨格です。Tang Nano 9K はHDMIコネクタや27MHzクロック、PLL等を持つ前提で話します。
Sipeed Wiki

  1. まずHDMI側の現実(解像度のおすすめ)

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

  1. “ラインバッファ無し・シフトレジスタ合成” の全体構成

大ブロックはこうです。

Timing Generator(x/yカウンタ、hsync/vsync/de、ブランキング)

BGパイプライン(タイルフェッチ + シフタ)(BG0..BGn)

Spriteパイプライン(走査線評価 + シフタ群)

Priority/Mixer(BGとSPRを優先度で合成、透明判定)

Palette(インデックス→RGB)

HDMI/DVI PHY(TMDSエンコード→シリアライズ)

「ラインバッファ無し」を成立させるコツは、BGもSPRも “今すぐ出す1px” はシフタから出す、そして 必要なデータは数〜数十サイクル先読みでシフタへ装填です。

  1. BG:タイル方式の“8pxシフタ”で流す(ラインバッファ不要)

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)ごとに回すだけで、ラインバッファ無しで安定します。

  1. スプライト:走査線開始時に“その線に当たるスプライト”だけシフタへ装填

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 とかに制限して動かすのが安全です(後で増やす)。

  1. VRAM/帯域の考え方(ラインバッファ無しの核心)

ラインバッファ無しは、「必要な時に必要な分だけ読む」=読み出し頻度が増えるので帯域設計が重要です。

タイル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側へ置く(キャッシュ)​のが堅いです。

  1. “拡大表示”で80年代っぽい内部解像度をHDMIへ(フレームバッファ無し)

内部解像度を 256×240 や 320×240 にして、HDMIは 640×480 にするのは簡単です。

横:同じピクセルを2回出す(xを内部1カウント進めるのは2ドットに1回)

縦:同じ走査線を2回出す(内部yを進めるのは2ラインに1回)

これも完全にラスター処理で、フレームバッファ不要です。

  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で間に合うか、何クロック必要か)

を、かなり具体的な形に落とし込めます。

同じ著者の記事