FPGAで構築する3DFX Voodoo(モダンRTLツール使用)

2026/03/22 22:24

FPGAで構築する3DFX Voodoo(モダンRTLツール使用)

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

チームはクラシックなVoodoo 1グラフィックスアクセラレータをSpinalHDLで再実装し、設計をGitHubに公開しました。
Voodoo 1はソフトウェア変換やライティングなしで三角形を描画する固定機能GPUです。その430の設定レジスタは、タイミング(FIFO、FIFO+ストール、直接、またはフロートエイリアス)に注意して書き込む必要があります。SpinalHDLの

RegIf
抽象化を拡張し、各レジスタのカテゴリ・アドレス・型・リセット値・フロートエイリアスフラグを含めることで、チームはこれらフィールド用の制御ロジックを自動生成しました。

デバッグにはconetraceというネットリスト認識トレースを使用し、ピクセルがラスター化器、テクスチャユニット、カラーコンバイン、およびフレームバッファステージを通過する様子を追跡しました。このワークフローは微妙な精度の欠陥を発見しました:

W
の早期量子化、ミップマップ境界近くでの不正確なパースペクティブテクスコーディングラウンド、ブレンド係数計算においてダイタ―減算カラーではなく拡張された宛先カラーを使用していた点です。これら小さなエラーを修正することで、かつて半透明オーバーレイやテキストに現れていたランダムな透明度アーティファクトが排除されました。

SpinalHDLの採用により散在したレジスタロジックは単一の高水準記述へと凝縮され、conetraceは手動波形検査から自動ツールへの再構築を移行させることで、1人のエンジニアがデバッグできるようにしました。これらの最新RTLツールの組み合わせは、レガシーGPU設計をより少ないバグ、明確な意図、および容易な保守で再活性化できることを示しており、レトロゲーミング愛好家、教育者、開発者全員に恩恵をもたらします。

本文

目次

  • 見た目より難しい固定機能チップ
  • レジスタ書き込みがすべて同じ振る舞いをしない理由
  • Voodoo の四つのレジスタ挙動
  • SpinalHDL でレジスタ意味論をエンコードする方法
  • 波形スクロールではなく実行クエリでデバッグ
  • 実際に変わった現代 RTL ツール

この Screamer 2 のフレームは、元の 3dfx カードやエミュレーターによって作られたものではなく、私が SpinalHDL で書いた Voodoo 1 の FPGA 再実装です。GitHub で公開しています。

驚きだったのは単に動くという事実だけではありませんでした。こうした設計を「一人で」記述・シミュレーション・デバッグできるようになったことです。ツールがアーキテクチャを直接表現し、適切な抽象レベルで実行を検査できると、この難解なチップも扱いやすくなるのです。

Voodoo 1 は古いですが単純ではありません。変換・ライティングハードウェアやプログラマブルシェーダがないため、グラフィック動作は全てシリコンに固定されています:Gouraud シェーディング用の勾配、テクスチャサンプリング、ミップマッピング、バイリニア・トリリニアフィルタリング、アルファクリッピング、クリッピング、深度比較、フォグなど。現代 GPU は多くの複雑さを柔軟なプログラマブルユニットに集中させますが、Voodoo では多数のハードウェア固定レンダリング動作に集約されています。

このプロジェクトを実感したバグの一つは、フレームバッファの危険性のように見えたものでした。部分的に透明なテキストやオーバーレイピクセルが不思議に透明になり、ほとんどのフレームは正常でした。本当の問題は、一つの壊れたサブシステムではなく、いくつかの小さなハードウェア精度ミスマッチが正しい順序で重なることでした。このバグはプロジェクト全体を要約する良い例になりました。重要なのは「三角形を描画できるようにする」ではなく、Voodoo の正確な挙動と一致させて誤ったピクセルが出現しないようにすることでした。

この投稿はその実現を可能にした二つの抽象化について述べます。第一は SpinalHDL で Voodoo のレジスタ意味論を表現した方法です。第二は Conetrace を使った深いグラフィックパイプラインのデバッグ手法です。


見た目より難しい固定機能チップ

一見、Voodoo は非常に控えめに見えるかもしれません。メモリマップ型アクセラレータで、一つの仕事しかありません:三角形をできるだけ速くレンダリングすることです。後続のアクセラレータとは異なり、変換とライティングは行わず、その重い 3D 計算はホスト CPU が担います。

これによりハードウェアが実際より単純に聞こえるかもしれません。たとえ一つの三角形であっても、補間された色、テクスチャサンプリング、ミップレベル選択、バイリニア・トリリニアフィルタリング、アルファクリッピング、深度比較、クリッピング、フォグなどが必要です。これらの操作はすべて現代的にプログラム可能ではなく、シリコンに組み込まれています。

ここが中心的な対比です。現代 GPU では複雑さは柔軟性から来ます。一方、オリジナル Voodoo は直接固定機能ハードウェアに多くのレンダリング動作をエンコードすることで複雑さを生み出します。


レジスタ書き込みがすべて同じ振る舞いをしない理由

レジスタインターフェースはこれを明確に示しています。Voodoo では

triangleCmd
または
ftriangleCmd
に書き込むと三角形が起動します。他のレジスタはその三角形をどのようにラスタライズするかを記述します:使用する勾配、テクスチャサンプリング方法、実行すべきテストなど。

Voodoo は深くパイプライン化されているため、ピクセルのレンダリングは一連のステージ—勾配進展、テクスチャサンプリング、色合成、深度バッファとの比較など—を経ます。パイプライニングは作業単位をステージに分割し、複数ピクセルを同時にフライトできるようにします。これがチップのスループットをソフトウェアでは実現不可能なレベルに達する方法です。

図 1: 重要なのはどのレジスタ書き込みが即座に適用され、どれがフライト中の作業と共に動くべきか、またパイプラインが空になるまで待つべきかを決定することです。

パイプライニングはレジスタモデルに問題をもたらします。三角形 A がまだパイプライン内を移動している間に CPU が三角形 B を設定し始めると、レンダリング設定が早すぎて変更されると、三角形 A の後半ピクセルが三角形 B 用の状態を参照する可能性があります。結果は微妙な破損:一部のピクセルが誤ったテクスチャモードやブレンディングモード、深度動作で描画されることです。

回避策は二つあります。1) レジスタ書き込みをパイプラインがドレインするまで待たせる、または 2) 書き込みをフライト中の作業と同期して進め、各三角形が属する状態を見えるようにする。言い換えれば、Voodoo のレジスタ書き込みは単なる設定更新ではなく、機械のタイミング契約の一部です。


Voodoo の四つのレジスタ挙動

私のモデルでは、Voodoo レジスタを以下の四種類に分類します:

種類振る舞い
FIFO順序通りキューイングして適用
FIFO + Stallキューイングはするがパイプラインが空になるまで適用しない
Direct即座に適用
Float変換後、固定小数点レジスタへ書き込み

重要なのはこれらのカテゴリがアーキテクチャ的であり、単なるソフトウェアインターフェースではないことです。レジスタタイプは状態が即座に変更できるか、フライト中の作業と共に動くべきか、あるいは機械が静止するまで待つべきかを示します。

図 2: これらカテゴリが存在する理由――新しい状態が古い作業に流れ込むことを防ぐためです。

この区別は HDL に直接モデル化すると自然なものになります。


SpinalHDL でレジスタ意味論をエンコード

Voodoo は多くのレジスタにわたる 430 の設定フィールドを持ち、各レジスタが上記カテゴリのいずれかに属します。Verilog のような従来 HDL では、この違いは複数箇所(レジスタ宣言、バス制御ロジック、パイプライン側処理、外部ドキュメント)に散らばります。

SpinalHDL は

RegIf
という便利な抽象化を提供します。これを使うとレジスタを自然に宣言し、周辺の制御ロジックを多く自動生成できます。私はそれを拡張して、Voodoo 固有の意味論(FIFO 動作、同期書き込み、float アリース)を直接レジスタ宣言でエンコードできるようにしました。

val startR = busif
  .newRegAtWithCategory(0x020, "startR", RegisterCategory.fifoNoSync)
  .field(AFix.SQ(24 bits, 12 bits), AccessType.WO, 255 << 12, "Starting red value")
  .withFloatAlias()
  .asOutput()

この宣言は勾配歩行器で使用される開始 R 値を定義します。1 回の場所でアドレス、名前、カテゴリ、データ型、アクセスモード、リセット値、および浮動小数点エイリアスの存在を指定しています。設計内では

startR
は通常の信号として登場します。

float エイリアスは特に有用です。オリジナル Voodoo には固定小数点レジスタより 128 アドレス上に浮動小数点書き込みを受け取り、変換後に固定小数点レジスタへ格納する別のレジスタがあります。この挙動はレジスタインターフェース自体の一部であるため、ロジックを分散させるよりもここで表現する方が理にかなっています。

レジスタメタデータが明示的になると、

RegIf
はヘッダーや SystemRDL など他形式へマップを書き出すことも可能です。私の場合はさらにそのメタデータを使って Voodoo のレジスタ意味論をエミュレートする
PciFifo
コンポーネントを駆動させました。FIFO スタイルの書き込みはキューイングされ、同期書き込みはパイプラインが空になるまで停止し、float アリースは浮動小数点から固定小数点への変換後、元アドレスへ再書き込みされてキューに入ります。

ここで得られる利点はコード行数の削減だけではありません。レジスタのアーキテクチャ的意味が一箇所に集約されることで、単なるドキュメントではなく「機械がどのように振る舞うか」の実行可能な記述になります。


波形スクロールではなく実行クエリでデバッグ

設計を記述することは RTL 作業の半分です。デバッグはもう一方です。

本当に私をこのワークフローに納得させたバグは、透明オーバーレイとテキストで現れました。ほぼ全体は正しいように見えましたが、小さなピクセルクラスタが不思議に消えてしまいました。出力色ブレンディングは既存フレームバッファ値を読み取るため、最初の仮説はメモリ順序バグでした:古い読み取り、読込/書き込み競合、あるいは新しいフラッシュキャッシュが古いデータを返すケースです。

図 3: ハードウェア(左)対参照(右)。症状はフレームバッファ危険性のように見えました:数ピクセルだけが失われ、残りは正しく表示される。

この仮説は十分に妥当であったため、書き込み優先度を変更し、真に直接キャッシュレス経路を追加し、代替バッファ読み取りを比較しました。アーティファクトはほぼ動かず、その結果が転換点でした。フレームバッファ危険性のように見えたものですが、証拠はその説明と合致しませんでした。

ここで Netlist‑aware トレース(Conetrace)が従来の波形ビューアよりも遥かに役立ちました。大量の信号を手作業で時系列に合わせて確認する代わりに、失敗したピクセルをラスタライザ、TMU、カラーコンビネーションロジック、最後にフレームバッファ出力まで段階的に追跡できました。対象ピクセルの終端までトレースできたことで、キャッシュ仮説は崩れました:誤りはフレームバッファ経路が説明できるよりも前から存在していました。

Terminal
$ conetrace rv path core_1.rasterizer_1.o core_1.writeColor.i_fromPipeline --track 52410001core_1.rasterizer_1.o @ cycle 5241000 payload={x: 396, y: 189, W: 1.972412, S: 124.492, T: 57.031}2-> core_1.tmu_1.io_output @ cycle 5241001 payload={S': 63.469, T': 13.984, lod: 0, texel: 0x58}3-> core_1.fbAccess.read @ cycle 5241002 payload={dst565: 0x4A29}4-> core_1.colorCombine_1.o @ cycle 5241002 payload={src: 0x56C9, dst_blend: 0x4A29, out: 0x39E7}5-> core_1.writeColor.i_fromPipeline @ cycle 5241003 payload={final565: 0x39E7}

注釈

  1. RasterizerSame フラグメントは両パスに入り、微小な W 精度損失が既に存在。
    ref:

    {x: 396, y: 189, W: 1.972427, S: 124.492, T: 57.031}

  2. First divergencePerspective ラウンドとピクセルごとの LOD は TMU パスで既に差異。
    ref:

    {S': 63.492, T': 14.031, lod: 1, texel: 0x6B}

  3. Framebuffer readDestination 色は正確に一致し、キャッシュ理論を除外。
    ref:

    {dst565: 0x4A29}

  4. Second divergenceThe 参照ブレンドパスは実際にデイタリングされた destination color を使用。
    ref:

    {src: 0x5A8C, dst_blend: 0x49E7, out: 0x4A69}

  5. Visible symptom RTL は最終書き込みで参照よりかなり暗くなる。
    ref:

    {final565: 0x4A69}


実際の問題は一つの壊れたブロックではなく、可視化されるまでにしか見えない小さなハードウェア精度ミスマッチの積み重ねでした。

  • 第一の問題は浮動小数点三角形
    W
    が TMU パスで早期に量子化されていたことです。
  • 第二は視差テクスチャ座標のラウンドとピクセルごとの LOD 調整がミップ境界近くで少しずれたことです。
  • 第三はブレンディング:拡張 destination color をブレンド係数計算に使用したものの、実際の Voodoo はデイタリングされた destination color を想定していました。

それぞれの挙動は単独ではほぼ正しいものでしたが、正確なクラスのテクスチャ付きブレンディングプリミティブで組み合わせると目に見える誤ったピクセルを生成しました。これがバグがランダムに見えた理由です:フレーム全体は正常であり、失敗パスも状態空間の狭い角度だけでしか正しくありませんでした。

修正は最初に妥当だと感じた仮説を放棄し、機械を段階ごとに一致させることでした。広い

W
S
T
アキュムレータを保持し、視差ラウンドと LOD 数学を修正し、デイタリングされた destination color をブレンド係数計算へ入力しました。これで「メモリ順序バグ」が消え、実際には存在しなかったことが明らかになりました。

従来の波形ビューアは関与するすべての信号を表示できますが、多くの再構築はエンジニアに委ねます。Netlist‑aware クエリツールはその再構築の一部をツール自身に移します。Voodoo のような設計では、この違いが「妥当な理論」と「実際の説明」の間のギャップになります。


実際に変わった現代 RTL ツール

Voodoo 1 が単純でない理由は古さだけではありません。非常に特定の方法で難しいのです:その挙動はシリコンに固定されているため、複雑さの多くは制御パス、レジスタ意味論、パイプラインタイミングにあります。

私が実感した現代 RTL ツールの変化は、設計自体の複雑さではなく、その複雑さを一度に頭に収める必要があった量です。

SpinalHDL はアーキテクチャ的意図をソース内で直接エンコードでき、宣言、バスロジック、ドキュメントへの散在を排除します。Conetrace は原始波形よりも設計構造に近い単位で実行を検査できます。

この組み合わせが、Voodoo の FPGA 再実装を一人で扱えるようにしました。機械は依然として複雑ですが、適切な抽象化があればその複雑さの多くが表現可能・クエリ可能・管理しやすいものになります。

同じ日のほかのニュース

一覧に戻る →

2026/03/23 3:23

**PC Gamer 推奨RSSリーダー(37 MBの記事でダウンロードが止まらない場合)** - **Feedly** - クラウドベースでデバイス間同期が可能。 - カテゴリー分けやタグ付け機能が充実しています。 - **Inoreader** - 高度なフィルタリングと検索機能を備えています。 - オフライン閲覧モードもサポートします。 - **The Old Reader** - シンプルで軽量、Googleアカウント連携が可能です。 - 共有リストやコメント機能があります。 - **NewsBlur** - AIによるトピック分類と学習機能を提供。 - モバイルアプリも充実しています。 - **Reeder (macOS/iOS)** - Appleデザインに合わせた直感的なUIです。 - 多数のリーダーサービスと連携可能です。 **注意点** - 大容量の記事をダウンロードし続ける場合は、**「オフライン保存」機能**をご利用ください。 - **キャッシュクリア**や**ブラウザ拡張機能無効化**で問題が解決することもあります。 - それでも解決しない場合は、PC Gamerのサポートへ問い合わせるか、別のリーダーを試してください。

## Japanese Translation: PC Gamerの記事は、読者に通知ポップアップ、背景を暗くするニュースレターオーバーレイ、そして少なくとも5つの閉じにくいバナー広告でページを襲撃していることを示しています。ウェルカムマットを回避した後でも、その広告は記事のタイトルとサブタイトルの横に残ります。初期ページロードは37 MBです;5分以内にサイトはさらに約0.5ギガバイトの広告素材をダウンロードします。NetNewsWire、Unread、Current、Reeder など多くの RSS リーダーはこれらの侵襲的要素をフィルタリングでき、よりクリーンな閲覧体験を提供します。これはユーザーが PC Gamer サイトの煩わしさを避けるために広告なしの RSS フィードに切り替える可能性があることを示唆しており、出版社は読者の関与を維持するために過度な広告戦術を減らす圧力を受けるかもしれません。

2026/03/23 4:02

「最適化のゴールドスタンダード:ローラーコースター・タイクーンの内部を探る」

## Japanese Translation: クリス・ソーヤーの *RollerCoaster Tycoon*(1999)は、ほぼすべてのコードをアセンブリで書き、細部にわたる低レベル最適化を施したことで、滑らかなゲームプレイのベンチマークを確立しました。金額は最大想定範囲にちょうど合ったデータ型(ショップ価格は1バイト、総公園価値は4バイト)で保存されており、後にオープンソース再実装 OpenRCT2 ではこれらを統一的な8バイト変数へ移行し、現代のCPUアーキテクチャに合わせました。乗算・除算の代わりにビットシフト(`<<`/`>>`)が使用されており、コンパイラが自動で行うはずだった処理を手動で実装しています。 ゲームデザインの決定は性能制約と密接に結びついています。ソーヤーはデザイナー兼プログラマーとして、CPUフレンドリーな計算を優先する設計選択が可能でした。ゲストの移動はアトラクションへ向かう完全な経路探索ではなくランダムウォークに依存しており、多数のエージェントによる高価な計算を大幅に削減しました。パスファインディングは特定のシナリオ(例:乗物修理のメカニック、出口を探すゲスト)でのみ呼び出され、深さ制限が設けられています—デフォルトでは5つのジャンクション、条件に応じて7または8に増加し、フレームスパイクを回避します。混雑した道では同一タイルに複数のゲストが存在でき、衝突回避は完全に省かれ、近接による幸福度計算のみが影響を受けます。 OpenRCT2 はこの元のロジックをリバースエンジニアリングし、現代CPU向けに変数サイズを標準化し、パスファインダーの制限を拡張することで更新しました。これにより、レガシートリックが新しいハードウェアに適応できることが示されました。将来のアップデートでは、衝突チェックや厳密なデータサイズといった古い制約を緩和しつつ、今日のマシンで性能を損なわずにコア体験を保持することが可能です。 これらの洞察は、デザイナーとプログラマーの緊密な協働と意図的な低レベル最適化が、小規模チームでも高性能ゲームを構築できることを示しており、大手スタジオも採用すべきアプローチです。 ## Text to translate (including missing points):** Chris Sawyer’s *RollerCoaster Tycoon* (1999) set a benchmark for smooth gameplay by writing almost all of its code in Assembly and applying meticulous low‑level optimizations. Money values were stored in data types sized exactly to their maximum expected range (1‑byte for shop prices, 4‑bytes for total park value), and the original engine later shifted these to uniform 8‑byte variables in the open‑source reimplementation OpenRCT2 to match modern CPU architecture. Bit shifting (`<<`/`>>`) was used instead of multiplication/division by powers of two, a manual trick that compilers no longer perform automatically. Game‑design decisions were tightly coupled with performance constraints: Sawyer served as both designer and programmer, allowing design choices to favor CPU‑friendly calculations. Guest movement relied on random walking rather than full pathfinding toward attractions, drastically reducing expensive calculations for thousands of agents. Pathfinding was invoked only in specific scenarios (e.g., mechanics repairing rides, guests seeking exits) and had a depth limit—default 5 junctions, increased to 7 or 8 under certain conditions—to avoid frame‑spikes. Overcrowded paths allowed multiple guests on the same tile; collision avoidance was omitted entirely, with only happiness calculations affected by proximity. OpenRCT2 reverse‑engineered this original logic and modernized it—standardizing variable sizes for current CPUs and extending pathfinder limits—showing how legacy tricks can be adapted to new hardware. Future updates could relax some of these old constraints (such as collision checks or strict data sizing) without harming performance on today’s machines while still preserving the core experience. These insights underscore that close collaboration between designers and programmers, coupled with deliberate low‑level optimization, enables small teams to build high‑performance games—an approach larger studios might emulate.

2026/03/23 0:16

**バージョン管理の未来** バージョン管理は、従来型のリポジトリやブランチモデルを超えて進化しています。新たに浮上している動向としては、AI 主導の変更分析、分散したチーム間でのリアルタイム協働、および継続的デリバリー・パイプラインとの緊密な統合が挙げられます。コードベースがより大規模かつ複雑化するにつれて、これらの革新はワークフローを合理化し、マージコンフリクトを減少させ、全体的なソフトウェア品質を向上させることを約束しています。

## Japanese Translation: **Manyana** は、Conflict‑Free Replicated Data Types(CRDTs)がバージョン管理にどのように利用できるかを示すデモプロジェクトです。ユーザー体験を向上させます。 ファイルは *weave* として表現されます——1 つのデータ構造が、追加または削除された各行とメタデータを記録し、行順序を永続化し、同時挿入に対してマージ全体で一貫した順序を提供します。 CRDTs は順序非依存ですので、マージが失敗することはありません。衝突はファイルの同じ部分を編集したときだけ発生し、不透明なマージブロブではなく明確な競合マーカーが生成されます。 システムはまた、リベースが履歴を破壊せずに行えることも示しています:コミットは新しいベース上で再実行され、「プライマリー・アニサスター」注釈によって完全な祖先関係が保持されます。 チェリーピッキングとローカル Undo はまだ実装されていませんが、470 行の Python デモ(パブリックドメイン)は、CRDTs がバージョン管理における難しい UX 問題を解決し、現在のツールよりも明確な競合表示を提供できることを示しています。