SlideShare a Scribd company logo
Designing
Video Game
Hardware in Verilog
第6回 2019/3/27
Chapter 30: Framebuffer Graphics
• フレームバッファとは、コンピュータのメモリの中で、一画面分の
表示状態を丸ごと保存しておく領域。画面に何かを描画する際には
フレームバッファの内容を書き換え、一定のタイミングでフレーム
バッファの内容がディスプレイ等の表示装置に送信されて表示内容
が更新される。
• ファミコンのグラフィック
バックグラウンド(BG)とスプライト(Sprite)と言う2つのレイヤを組
み合わせることによって、色々なグラフィックを表示させている。
主にBGは背景マップの表示に使われ、スプライトはマップ上のオブ
ジェクトに使われることが多い。
Chapter 30: Framebuffer Graphics
CPU16
from_cpu
to_cpu
address_bus[15]
FF
write_enable
ram[address_bus[14:0]]
rom[address_bus[9:0]]
Chapter 30: Framebuffer Graphics
hvsync
generator
display_on
hpos
hpos[2:0]==0
ram[{2’b10,vindex}]
FF
vshift
vshift<<2
Chapter 30: Framebuffer Graphics
hvsync
generator
display_on
FF
rgb
pallete[vshift[15:14]]
0
reg [3:0] palette[0:3] = ’{0,1,4,7};
Chapter 30: Framebuffer Graphics
always @(posedge clk or negedge rst) begin
if(!rst)
ccc <= 0 ;
else if(en==1)
ccc <= aaa ;
else
ccc <= bbb ;
end
en
aaa
bbb
ccc
FF
AND
AND
aaa
bbb
en
ORINV FF
ccc
30.1: Design
30.1: Design
initial begin
rom = '{
__asm
.arch femto16
.org 32768
.len 1024
Start:
mov ax,cx ; ax = cx
mov bx,#0 ; bx = #0
Loop:
mov [bx],ax ; ram[bx] = ax
inc ax ; ax = ax + 1
inc bx ; bx = bx + 1
bnz Loop ; loop until
bx is 0
inc cx
reset ; reset CPU
__endasm
};
end
31: Tilemap Rendering
第15章では、32列x 30行の数字を表示。
ただし、このモジュールにはいくつかの制限があり。
・専用のビデオRAMを使用。
・ビデオスキャナはRAMアドレスバスを独占し、
他の回路(CPUなど)がRAMの帯域幅を共有できない。
・設定可能な色はない。
difit10
31: Tilemap Rendering
7’o00: bits = 5’b11111;// *****
7’o01: bits = 5’b10001;// * *
7’o02: bits = 5’b10001;// * *
7’o03: bits = 5’b10001;// * *
7’o04: bits = 5’b11111;// *****
// digit "1"
7’o10: bits = 5’b01100;// **
7’o11: bits = 5’b00100;// *
7’o12: bits = 5’b00100;// *
7’o13: bits = 5’b00100;// *
7’o14: bits = 5’b11111;// *****
hvsync
generator
ram
[8:0]vpos
[7:0] dout
vpos[2:0]
[3:0]digit
dout[3:0]
[2:0] yofs
g = display_on && bits[~xofs];
[4:0] bits
15章
31: Tilemap Rendering
[15:0] addr
[15:0] dout
[10:0] addr
[7:0] data
[8:0] hpos
[8:0] vpos
hvsync_
generator
rom
ram
vsync
hsync
tile_renderer
[8:0] hpos
[8:0] vpos
[7:0] rom_data
[15:0] ram_read
assign rgb = rom_data[~xofs] ? cur_attr[3:0] : cur_attr[7:4];
ramのアドレスは
ram_addr <= {page_base, 3'b000, row}
page_base=8’h7E
rgb
CPUとRAMを共有。
RAMからtilemap(どの文字がどのセルにあるか
を定義する配列)を読み取る。
実際の文字パターンはROMから取得します。。
31: Tilemap Rendering
行テーブルは、アドレス$ 7E00から$ 7E1F
32個の各アドレスはタイルの行の始まりを指す。
各行には16ビット値のリストが含まれており、
各リストは単一セルのパターン情報と色情報を組
み合わせたもの。
上位8ビットは背景色と前景色を示し、
下位8ビットは文字ビットマップROMへのイン
デックスです。
31.1:Sharing RAM with the CPU
CPU16 cpu(
.clk(clk),
.reset(reset),
.hold(hold), //input
.busy(busy),
.address(address_bus),
.data_in(to_cpu),
.data_out(from_cpu),
.write(write_enable));
CPUはholdを確認すると、busy出力をアサート。
hold信号がクリアされるまで待機。
その後、実行を再開。
tile_rendererがRAMからデータを読み取る前に、
hold信号をCPUに送信。
設計上、CPUはこの信号に応答して停止するまで
最大5クロックサイクルかかることがあるため、
RAMからの読み取りを開始する前に、必ず5サイク
ルholdをアサート。// state 1: select opcode address
S_SELECT: begin
write <= 0;
if (hold) begin
busy <= 1;
state <= S_SELECT;
31.1:Sharing RAM with the CPU
tile_renderer
ram_busy CPUへ
// lookup char and attr
always @(posedge clk) begin
// time to read a row?
if (vpos[2:0] == 7) begin
// read row_base from page table (2 bytes)
case (hpos)
// assert busy 5 cycles before first RAM read
HLOAD-8: ram_busy <= 1;
// deassert BUSY and increment row counter
HLOAD+34: begin
ram_busy <= 0;
end
31.2:The Tile Renderer State Machine
The tile renderer has two conceptual parts:
the fill stage and the output stage.
fillステージの時にRAMからデータを読み込み、
内部バッファへライト
reg [15:0] row_buffer[0:31] タイルデータを保持
hposおよびvpos信号を使用してバッファへライト
31.2:The Tile Renderer State Machine
// start loading cells from RAM at this hpos value
// first column read will be ((HLOAD-2) % 32)
parameter HLOAD = 272;
HLOAD定数は、読み始める水平位置を定義
タイルは8x8なので、8スキャンラインごとに
RAMから読み取る。
// time to read a row?
if (vpos[2:0] == 7) begin
31.2:The Tile Renderer State Machine
case(hpos)
HLOAD-8:ram_busy信号をアサートしてCPUに停止の準備を開始するよう指示。
CPUに停止するまでに5サイクルを与え、現在の命令を終了させる。
// assert busy 5 cycles before first RAM read
HLOAD-8: ram_busy <= 1;
HLOAD-3で、ram_addr(RAMアドレスバスに接続されている)を
ページテーブルの現在のエントリに設定。
// set address for row in page base table
HLOAD-3: ram_addr <= {page_base, 3’b000, row};
31.2:The Tile Renderer State Machine
ram_addrはアドレスバスの値を設定するのに1サイクル使用。
そのため、アドレスバスの値はHLOAD-2まで設定されない。
また、 RAMの読み出し結果を返すには1クロックサイクルかかる(第14章を参照)。
そのため、読み取り結果はHLOAD-1まで利用できない。
この時点で、ram_readバスの値を取得して内部レジスタrow_baseに格納。
// read row_base from page table (2 bytes)
HLOAD-1: row_base <= ram_read;
31.2:The Tile Renderer State Machine
コピー操作は、HLOADからHLOAD + 33まで。
HLOAD+34: begin
ram_busy <= 0;
row <= row + 1;
end
同期RAMの読み出しは2サイクルかかる。(アドレスバスの設定+同期RAMデータの待機)。
if (hpos >= HLOAD && hpos < HLOAD+34) begin
// set address bus to (row_base + hpos)
ram_addr <= row_base + 16'(hpos[4:0]);
// store value on data bus from (row_base + hpos - 2)
// which was read two cycles ago
row_buffer[hpos[4:0] - 2] <= ram_read;
end
31.2:The Tile Renderer State Machine
データの読み出しタイミング
31.3:Rendering the buffer
ROM内の各タイルのパターンを調べ、
それを内部レジスタに設定。
ビットマスクを使用して各ピクセルの前景色または
背景色を選択し、 rgbモジュールポートに出力。
各セルの7番目のピクセル(次のセルが始まる直前)で、
row_bufferから次のセル(col + 1)をラッチ。
// latch character data
(hpos < 256) begin
case (hpos[2:0])
7: begin cur_cell <= row_buffer[col+1];
end
endcase
end
else if (hpos == 308) begin
cur_cell <= row_buffer[0];
end
31.3:Rendering the buffer
cur_cellは2つのバイト、cur_charとcur_attrに分割される。
cur_charとyofsを連結してROMアドレスを計算できる。
(基本的にcur_char * 8 + yofs)
assign rom_addr = {cur_char, yofs};
最後のステップはROMからデータを検索すること。
ROMのビットがオンかオフかに応じて、
cur_attrの下位または上位(前景または背景)を使用。
// extract bit from ROM output
assign rgb = rom_data[~xofs]? cur_attr[3:0]: cur_attr[7:4];
32:Scanline Sprite Rendering
ram
[8:0]vpos
[5:0] addr [5:0]ram_addr
rgb[3:0] rgb <= scanline[read_bufidx]
[15:0] ram_data
rom
[15:0] addr
[15:0] data
[15:0] rom_addr
[15:0] rom_data
[15:0] dout
sprite_
scanline_
renderer
[8:0]hpos
hvsync
generator
[8:0]vpos
[8:0]hpos
32:Scanline Sprite Rendering
32.1: RAM and ROM
input clk, reset; // clock and reset inputs
input [8:0] hpos; // horiz. sync pos
input [8:0] vpos; // vert. sync pos
output [3:0] rgb; // rgb output
output [NB:0] ram_addr; // RAM for sprite data
input [15:0] ram_data; // (2 words per sprite)
output ram_busy; // set when accessing RAM
output [15:0] rom_addr; // sprite ROM address
input [15:0] rom_data; // sprite ROM data
スプライトビットマップのためのROMと
スプライトの位置と色のためのRAM
32.2: Scanline Buffer
RGBスキャンラインバッファは512エントリ(256ピクセルの2スキャンライン)
// 4-bit RGB dual scanline buffer
reg [3:0] scanline[0:511];
// which offset in scanline buffer to read?
wire [8:0] read_bufidx = {vpos[0], hpos[7:0]};
クロックサイクルごとに、スキャンラインバッファのピクセルをリード。
// read and clear buffer
rgb <= scanline[read_bufidx];
scanline[read_bufidx] <= 0;
32.3: Sprite State Machine
スプライトステートマシンには3つのフェーズがある。
1. RAMからスプライトデータをロードし、それらをsprite_xxx配列に代入
(1フレームに1回)。
2.スプライトリストを繰り返して表示されるスプライトを選択し、
それらをスロットにマッピング。(1スキャンラインにつき1回)
3.スプライトスロットを繰り返し、ROM内の各ビットマップスライスを調べて、
2つのスキャンラインバッファのうちの1つにレンダリング。
32.4: Sprites and Slots
NB - フレームあたりのスプライトの最大数。
MB - 最大スロット数(スキャンラインあたりのスプライト)。
NB = 5は2の5乗。32スプライト。
parameter NB = 5;// 2^NB == number of sprites
parameter MB = 3;// 2^MB == slots per scanline
localparam N = 1<<NB;// number of sprites
localparam M = 1<<MB;// slots per scanline
スプライトデータがRAMから読み込まれた後もそのコピーを 保持するローカルメモリ
// copy of sprite data from RAM (N entries)
reg [7:0] sprite_xpos[0:N-1];// X positions
reg [7:0] sprite_ypos[0:N-1];// Y positions
reg [7:0] sprite_attr[0:N-1];// attributes
32.4: Sprites and Slots
スロットがアクティブであるかどうかを示すブール値のマークを付けて、
スキャンラインごとのスロットについて同様のデータを保持
// M sprite slots
reg [7:0] line_xpos[0:M-1];// X pos for M slots
reg [7:0] line_yofs[0:M-1];// Y pos for M slots
reg [7:0] line_attr[0:M-1];// attr for M slots
reg line_active[0:M-1];// slot active?
32.5: Lodaing Sprite Data From RAM
if (reset || vpos[8]) begin
// load sprites from RAM on line 260 // 8 cycles per sprite
// do first sprite twice b/c CPU might still be busy
if (vpos == 260 && hpos < N*2+8) begin
ram_busy <= 1;
case (hpos[0])
0: begin
ram_addr <= {load_index, 1'b0};
// load X and Y position (2 cycles ago)
sprite_xpos[load_index] <= ram_data[7:0];
sprite_ypos[load_index] <= ram_data[15:8];
end
1: begin
ram_addr <= {load_index, 1'b1};
// load attribute (2 cycles ago)
sprite_attr[load_index] <= ram_data[7:0];
end
endcase
end
最初の数個のスプライトは、
CPUがビジー状態(停止するまで最大5サ
イクルかかる可能性がある)なので
破損している可能性があり。
他のスプライトがすべて読み取られた後に、
それらを再度読み取り。
32.6: Itrating The Sprite List
各スキャンラインの始めに、すべてのスプライトをスキャンし、
どれがこのスキャンラインと交差するかを判断。
それから、Yオフセットを保存し、アクティブにして、sprite_to_lineマッピングを更新。
スプライトごとに2サイクル。
32.6: Itrating The Sprite List
各スキャンラインの始めに、すべてのスプライトをスキャンし、
どれがこのスキャンラインと交差するかを判断します。
それから、Yオフセットを保存し、アクティブにして、
sprite_to_lineマッピングを更新します。
これはスプライトごとに2サイクルかかります。
end else if (hpos < N*2) begin
// setup vars for next phase
k <= 0;
romload <= 0;
// select the sprites that will appear in this scanline
case (hpos[0])
0: z <= 8'(vpos - sprite_ypos[i]); // compute Y offset of sprite relative to scanline
1: begin
// sprite is active if Y offset is 0..15
if (z < 16) begin
line_xpos[j] <= sprite_xpos[i]; // save X pos
line_yofs[j] <= z; // save Y offset
line_attr[j] <= sprite_attr[i]; // save attr
line_active[j] <= 1; // mark sprite active
j <= j + 1; // inc counter
end
i <= i + 1; // inc main array counter
end
endcase
32.7: Sprite Setup
スロットを埋めた後、レンダリングに行う。
各スプライトスロットに18サイクル、
セットアップに2、レンダリングに16を使用できる。
セットアップ段階での操作
•spriteのX座標を使用して、ピクセルを書き込むデュアルスキャンラインバッファのオフセットを
計算。
•spriteのROMアドレスを計算し、16ビットのビットマップスライスを取得。
•spriteの属性(RGBカラー)を調べる。
•spriteスロットがアクティブでない場合は、そのビットマップスライスをすべての透明ピクセル
(ゼロ)に設定。
•アクティブビットをクリアして次のスロットに。
これを完了するには少なくとも2クロックサイクルが必要。
そのため、romloadフラグを使用して2つのフェーズを切り替る。
最初のサイクルで、ROMアドレスを設定。
32.7: Sprite Setup
if (out_bitmap == 0) begin
case (romload)
0: begin
rom_addr <= {4'b0, line_attr[k][7:4], line_yofs[k]}; // set ROM address and fetch bitmap
end
1: begin
// load scanline buffer offset to write
write_ofs <= {~vpos[0], line_xpos[k]};
// fetch 0 if sprite is inactive
out_bitmap <= line_active[k] ? rom_data : 0;
// load attribute for sprite
out_attr <= line_attr[k];
// disable sprite for next scanline
line_active[k] <= 0;
// go to next sprite in 2ndary buffer
k <= k + 1;
end
endcase
romload <= !romload;
32.8: Sprite Rendering
スロットが設定されると、レンダリングは比較的簡単になる。
16個のピクセルのそれぞれについて、下位ビットが設定されているかどうかがわかる。
もしそうであれば、走査線バッファにピクセルを書き込む。
それから、out_bitmap変数を1ビット右にシフトし、write_ofsをインクリメント。
// write color to scanline buffer if low bit == 1
if (out_bitmap[0])
scanline[write_ofs] <= out_attr[3:0];
// shift bits right
out_bitmap <= out_bitmap >> 1; // increment to next scanline pixel
write_ofs <= write_ofs + 1; end

More Related Content

What's hot

高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
高速ネットワーク最新動向と具体例 (ENOG58 Meeting)高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
Naoto MATSUMOTO
 
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチプロセスとコンテキストスイッチ
プロセスとコンテキストスイッチ
Kazuki Onishi
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
MITSUNARI Shigeo
 
Spectre/Meltdownとその派生
Spectre/Meltdownとその派生Spectre/Meltdownとその派生
Spectre/Meltdownとその派生
MITSUNARI Shigeo
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
MITSUNARI Shigeo
 
あるコンテキストスイッチの話
あるコンテキストスイッチの話あるコンテキストスイッチの話
あるコンテキストスイッチの話
nullnilaki
 
QP 3min cooking(port 9100 network printing howto)
QP 3min cooking(port 9100 network printing howto)QP 3min cooking(port 9100 network printing howto)
QP 3min cooking(port 9100 network printing howto)
gueste558ec
 
hpingで作るパケット
hpingで作るパケットhpingで作るパケット
hpingで作るパケット
Takaaki Hoyo
 
Altanative macro
Altanative macroAltanative macro
Altanative macro
Motohiro KOSAKI
 
Minix smp
Minix smpMinix smp
Minix smp
Masami Ichikawa
 
あるmmapの話
あるmmapの話あるmmapの話
あるmmapの話
nullnilaki
 
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
Kazuhiro Takahashi
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
MITSUNARI Shigeo
 
Ras piでrt linux
Ras piでrt linuxRas piでrt linux
Ras piでrt linux
Hideki Aoshima
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
MITSUNARI Shigeo
 
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜHUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
basicinc_dev
 
ラズパイでデバイスドライバを作ってみた。
ラズパイでデバイスドライバを作ってみた。ラズパイでデバイスドライバを作ってみた。
ラズパイでデバイスドライバを作ってみた。
Kazuki Onishi
 
あなたの知らないnopたち@ラボユース合宿
あなたの知らないnopたち@ラボユース合宿あなたの知らないnopたち@ラボユース合宿
あなたの知らないnopたち@ラボユース合宿
MITSUNARI Shigeo
 
Spmv9forpublic
Spmv9forpublicSpmv9forpublic
Spmv9forpublic
T2C_
 

What's hot (20)

高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
高速ネットワーク最新動向と具体例 (ENOG58 Meeting)高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
高速ネットワーク最新動向と具体例 (ENOG58 Meeting)
 
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチプロセスとコンテキストスイッチ
プロセスとコンテキストスイッチ
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
 
Memory sanitizer
Memory sanitizerMemory sanitizer
Memory sanitizer
 
Spectre/Meltdownとその派生
Spectre/Meltdownとその派生Spectre/Meltdownとその派生
Spectre/Meltdownとその派生
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
 
あるコンテキストスイッチの話
あるコンテキストスイッチの話あるコンテキストスイッチの話
あるコンテキストスイッチの話
 
QP 3min cooking(port 9100 network printing howto)
QP 3min cooking(port 9100 network printing howto)QP 3min cooking(port 9100 network printing howto)
QP 3min cooking(port 9100 network printing howto)
 
hpingで作るパケット
hpingで作るパケットhpingで作るパケット
hpingで作るパケット
 
Altanative macro
Altanative macroAltanative macro
Altanative macro
 
Minix smp
Minix smpMinix smp
Minix smp
 
あるmmapの話
あるmmapの話あるmmapの話
あるmmapの話
 
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
Raspberry pi 用 toppers ssp シュリンク版(海賊版)の紹介
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
 
Ras piでrt linux
Ras piでrt linuxRas piでrt linux
Ras piでrt linux
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
 
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜHUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
HUで6000万pvのトラフィックを捌くまでに起ったことをありのままに話すぜ
 
ラズパイでデバイスドライバを作ってみた。
ラズパイでデバイスドライバを作ってみた。ラズパイでデバイスドライバを作ってみた。
ラズパイでデバイスドライバを作ってみた。
 
あなたの知らないnopたち@ラボユース合宿
あなたの知らないnopたち@ラボユース合宿あなたの知らないnopたち@ラボユース合宿
あなたの知らないnopたち@ラボユース合宿
 
Spmv9forpublic
Spmv9forpublicSpmv9forpublic
Spmv9forpublic
 

Similar to FPGAでゲーム機を作ろう! 第6回

Designing video game hardware in verilog
Designing video game hardware in verilogDesigning video game hardware in verilog
Designing video game hardware in verilog
Atsuki Takahashi
 
osoljp 2011.08
osoljp 2011.08osoljp 2011.08
osoljp 2011.08
@ otsuka752
 
【学習メモ#3rd】12ステップで作る組込みOS自作入門
【学習メモ#3rd】12ステップで作る組込みOS自作入門【学習メモ#3rd】12ステップで作る組込みOS自作入門
【学習メモ#3rd】12ステップで作る組込みOS自作入門
sandai
 
SECDマシン 実装と動きとその他もろもろについて
SECDマシン 実装と動きとその他もろもろについてSECDマシン 実装と動きとその他もろもろについて
SECDマシン 実装と動きとその他もろもろについて
t-sin
 
あるブートローダの話
あるブートローダの話あるブートローダの話
あるブートローダの話
nullnilaki
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml
ssuser3a4b8c
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングKiwamu Okabe
 
Dbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishDbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishYohei Azekatsu
 
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)Kazuyuki Sato
 
Dive into RTS - another side
Dive into RTS - another sideDive into RTS - another side
Dive into RTS - another sideKiwamu Okabe
 
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
Mr. Vengineer
 
もしCloudStackのKVMホストでPCIパススルーできるようになったら
もしCloudStackのKVMホストでPCIパススルーできるようになったらもしCloudStackのKVMホストでPCIパススルーできるようになったら
もしCloudStackのKVMホストでPCIパススルーできるようになったら
Takuma Nakajima
 
Kiso sekkei 01rev03
Kiso sekkei 01rev03Kiso sekkei 01rev03
Kiso sekkei 01rev03
tetsuya matsuno
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門Yosuke Onoue
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptx
NAIST
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputing
Noboru Irieda
 

Similar to FPGAでゲーム機を作ろう! 第6回 (20)

Designing video game hardware in verilog
Designing video game hardware in verilogDesigning video game hardware in verilog
Designing video game hardware in verilog
 
osoljp 2011.08
osoljp 2011.08osoljp 2011.08
osoljp 2011.08
 
【学習メモ#3rd】12ステップで作る組込みOS自作入門
【学習メモ#3rd】12ステップで作る組込みOS自作入門【学習メモ#3rd】12ステップで作る組込みOS自作入門
【学習メモ#3rd】12ステップで作る組込みOS自作入門
 
仮想記憶の構築法
仮想記憶の構築法仮想記憶の構築法
仮想記憶の構築法
 
SECDマシン 実装と動きとその他もろもろについて
SECDマシン 実装と動きとその他もろもろについてSECDマシン 実装と動きとその他もろもろについて
SECDマシン 実装と動きとその他もろもろについて
 
あるブートローダの話
あるブートローダの話あるブートローダの話
あるブートローダの話
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミング
 
Dbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishDbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publish
 
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)
「前回の COMSTAR ネタに刺激されてしまったので、オレも COMSTAR を使ってみた。」(仮)
 
Dive into RTS - another side
Dive into RTS - another sideDive into RTS - another side
Dive into RTS - another side
 
Part Print
Part PrintPart Print
Part Print
 
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
ZynqMPのブートとパワーマネージメント : (ZynqMP Boot and Power Management)
 
Prosym2012
Prosym2012Prosym2012
Prosym2012
 
Cpu cache arch
Cpu cache archCpu cache arch
Cpu cache arch
 
もしCloudStackのKVMホストでPCIパススルーできるようになったら
もしCloudStackのKVMホストでPCIパススルーできるようになったらもしCloudStackのKVMホストでPCIパススルーできるようになったら
もしCloudStackのKVMホストでPCIパススルーできるようになったら
 
Kiso sekkei 01rev03
Kiso sekkei 01rev03Kiso sekkei 01rev03
Kiso sekkei 01rev03
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptx
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputing
 

FPGAでゲーム機を作ろう! 第6回

  • 1. Designing Video Game Hardware in Verilog 第6回 2019/3/27
  • 2. Chapter 30: Framebuffer Graphics • フレームバッファとは、コンピュータのメモリの中で、一画面分の 表示状態を丸ごと保存しておく領域。画面に何かを描画する際には フレームバッファの内容を書き換え、一定のタイミングでフレーム バッファの内容がディスプレイ等の表示装置に送信されて表示内容 が更新される。 • ファミコンのグラフィック バックグラウンド(BG)とスプライト(Sprite)と言う2つのレイヤを組 み合わせることによって、色々なグラフィックを表示させている。 主にBGは背景マップの表示に使われ、スプライトはマップ上のオブ ジェクトに使われることが多い。
  • 3. Chapter 30: Framebuffer Graphics CPU16 from_cpu to_cpu address_bus[15] FF write_enable ram[address_bus[14:0]] rom[address_bus[9:0]]
  • 4. Chapter 30: Framebuffer Graphics hvsync generator display_on hpos hpos[2:0]==0 ram[{2’b10,vindex}] FF vshift vshift<<2
  • 5. Chapter 30: Framebuffer Graphics hvsync generator display_on FF rgb pallete[vshift[15:14]] 0 reg [3:0] palette[0:3] = ’{0,1,4,7};
  • 6. Chapter 30: Framebuffer Graphics always @(posedge clk or negedge rst) begin if(!rst) ccc <= 0 ; else if(en==1) ccc <= aaa ; else ccc <= bbb ; end en aaa bbb ccc FF AND AND aaa bbb en ORINV FF ccc
  • 8. 30.1: Design initial begin rom = '{ __asm .arch femto16 .org 32768 .len 1024 Start: mov ax,cx ; ax = cx mov bx,#0 ; bx = #0 Loop: mov [bx],ax ; ram[bx] = ax inc ax ; ax = ax + 1 inc bx ; bx = bx + 1 bnz Loop ; loop until bx is 0 inc cx reset ; reset CPU __endasm }; end
  • 9. 31: Tilemap Rendering 第15章では、32列x 30行の数字を表示。 ただし、このモジュールにはいくつかの制限があり。 ・専用のビデオRAMを使用。 ・ビデオスキャナはRAMアドレスバスを独占し、 他の回路(CPUなど)がRAMの帯域幅を共有できない。 ・設定可能な色はない。
  • 10. difit10 31: Tilemap Rendering 7’o00: bits = 5’b11111;// ***** 7’o01: bits = 5’b10001;// * * 7’o02: bits = 5’b10001;// * * 7’o03: bits = 5’b10001;// * * 7’o04: bits = 5’b11111;// ***** // digit "1" 7’o10: bits = 5’b01100;// ** 7’o11: bits = 5’b00100;// * 7’o12: bits = 5’b00100;// * 7’o13: bits = 5’b00100;// * 7’o14: bits = 5’b11111;// ***** hvsync generator ram [8:0]vpos [7:0] dout vpos[2:0] [3:0]digit dout[3:0] [2:0] yofs g = display_on && bits[~xofs]; [4:0] bits 15章
  • 11. 31: Tilemap Rendering [15:0] addr [15:0] dout [10:0] addr [7:0] data [8:0] hpos [8:0] vpos hvsync_ generator rom ram vsync hsync tile_renderer [8:0] hpos [8:0] vpos [7:0] rom_data [15:0] ram_read assign rgb = rom_data[~xofs] ? cur_attr[3:0] : cur_attr[7:4]; ramのアドレスは ram_addr <= {page_base, 3'b000, row} page_base=8’h7E rgb CPUとRAMを共有。 RAMからtilemap(どの文字がどのセルにあるか を定義する配列)を読み取る。 実際の文字パターンはROMから取得します。。
  • 12. 31: Tilemap Rendering 行テーブルは、アドレス$ 7E00から$ 7E1F 32個の各アドレスはタイルの行の始まりを指す。 各行には16ビット値のリストが含まれており、 各リストは単一セルのパターン情報と色情報を組 み合わせたもの。 上位8ビットは背景色と前景色を示し、 下位8ビットは文字ビットマップROMへのイン デックスです。
  • 13. 31.1:Sharing RAM with the CPU CPU16 cpu( .clk(clk), .reset(reset), .hold(hold), //input .busy(busy), .address(address_bus), .data_in(to_cpu), .data_out(from_cpu), .write(write_enable)); CPUはholdを確認すると、busy出力をアサート。 hold信号がクリアされるまで待機。 その後、実行を再開。 tile_rendererがRAMからデータを読み取る前に、 hold信号をCPUに送信。 設計上、CPUはこの信号に応答して停止するまで 最大5クロックサイクルかかることがあるため、 RAMからの読み取りを開始する前に、必ず5サイク ルholdをアサート。// state 1: select opcode address S_SELECT: begin write <= 0; if (hold) begin busy <= 1; state <= S_SELECT;
  • 14. 31.1:Sharing RAM with the CPU tile_renderer ram_busy CPUへ // lookup char and attr always @(posedge clk) begin // time to read a row? if (vpos[2:0] == 7) begin // read row_base from page table (2 bytes) case (hpos) // assert busy 5 cycles before first RAM read HLOAD-8: ram_busy <= 1; // deassert BUSY and increment row counter HLOAD+34: begin ram_busy <= 0; end
  • 15. 31.2:The Tile Renderer State Machine The tile renderer has two conceptual parts: the fill stage and the output stage. fillステージの時にRAMからデータを読み込み、 内部バッファへライト reg [15:0] row_buffer[0:31] タイルデータを保持 hposおよびvpos信号を使用してバッファへライト
  • 16. 31.2:The Tile Renderer State Machine // start loading cells from RAM at this hpos value // first column read will be ((HLOAD-2) % 32) parameter HLOAD = 272; HLOAD定数は、読み始める水平位置を定義 タイルは8x8なので、8スキャンラインごとに RAMから読み取る。 // time to read a row? if (vpos[2:0] == 7) begin
  • 17. 31.2:The Tile Renderer State Machine case(hpos) HLOAD-8:ram_busy信号をアサートしてCPUに停止の準備を開始するよう指示。 CPUに停止するまでに5サイクルを与え、現在の命令を終了させる。 // assert busy 5 cycles before first RAM read HLOAD-8: ram_busy <= 1; HLOAD-3で、ram_addr(RAMアドレスバスに接続されている)を ページテーブルの現在のエントリに設定。 // set address for row in page base table HLOAD-3: ram_addr <= {page_base, 3’b000, row};
  • 18. 31.2:The Tile Renderer State Machine ram_addrはアドレスバスの値を設定するのに1サイクル使用。 そのため、アドレスバスの値はHLOAD-2まで設定されない。 また、 RAMの読み出し結果を返すには1クロックサイクルかかる(第14章を参照)。 そのため、読み取り結果はHLOAD-1まで利用できない。 この時点で、ram_readバスの値を取得して内部レジスタrow_baseに格納。 // read row_base from page table (2 bytes) HLOAD-1: row_base <= ram_read;
  • 19. 31.2:The Tile Renderer State Machine コピー操作は、HLOADからHLOAD + 33まで。 HLOAD+34: begin ram_busy <= 0; row <= row + 1; end 同期RAMの読み出しは2サイクルかかる。(アドレスバスの設定+同期RAMデータの待機)。 if (hpos >= HLOAD && hpos < HLOAD+34) begin // set address bus to (row_base + hpos) ram_addr <= row_base + 16'(hpos[4:0]); // store value on data bus from (row_base + hpos - 2) // which was read two cycles ago row_buffer[hpos[4:0] - 2] <= ram_read; end
  • 20. 31.2:The Tile Renderer State Machine データの読み出しタイミング
  • 21. 31.3:Rendering the buffer ROM内の各タイルのパターンを調べ、 それを内部レジスタに設定。 ビットマスクを使用して各ピクセルの前景色または 背景色を選択し、 rgbモジュールポートに出力。 各セルの7番目のピクセル(次のセルが始まる直前)で、 row_bufferから次のセル(col + 1)をラッチ。 // latch character data (hpos < 256) begin case (hpos[2:0]) 7: begin cur_cell <= row_buffer[col+1]; end endcase end else if (hpos == 308) begin cur_cell <= row_buffer[0]; end
  • 22. 31.3:Rendering the buffer cur_cellは2つのバイト、cur_charとcur_attrに分割される。 cur_charとyofsを連結してROMアドレスを計算できる。 (基本的にcur_char * 8 + yofs) assign rom_addr = {cur_char, yofs}; 最後のステップはROMからデータを検索すること。 ROMのビットがオンかオフかに応じて、 cur_attrの下位または上位(前景または背景)を使用。 // extract bit from ROM output assign rgb = rom_data[~xofs]? cur_attr[3:0]: cur_attr[7:4];
  • 23. 32:Scanline Sprite Rendering ram [8:0]vpos [5:0] addr [5:0]ram_addr rgb[3:0] rgb <= scanline[read_bufidx] [15:0] ram_data rom [15:0] addr [15:0] data [15:0] rom_addr [15:0] rom_data [15:0] dout sprite_ scanline_ renderer [8:0]hpos hvsync generator [8:0]vpos [8:0]hpos
  • 25. 32.1: RAM and ROM input clk, reset; // clock and reset inputs input [8:0] hpos; // horiz. sync pos input [8:0] vpos; // vert. sync pos output [3:0] rgb; // rgb output output [NB:0] ram_addr; // RAM for sprite data input [15:0] ram_data; // (2 words per sprite) output ram_busy; // set when accessing RAM output [15:0] rom_addr; // sprite ROM address input [15:0] rom_data; // sprite ROM data スプライトビットマップのためのROMと スプライトの位置と色のためのRAM
  • 26. 32.2: Scanline Buffer RGBスキャンラインバッファは512エントリ(256ピクセルの2スキャンライン) // 4-bit RGB dual scanline buffer reg [3:0] scanline[0:511]; // which offset in scanline buffer to read? wire [8:0] read_bufidx = {vpos[0], hpos[7:0]}; クロックサイクルごとに、スキャンラインバッファのピクセルをリード。 // read and clear buffer rgb <= scanline[read_bufidx]; scanline[read_bufidx] <= 0;
  • 27. 32.3: Sprite State Machine スプライトステートマシンには3つのフェーズがある。 1. RAMからスプライトデータをロードし、それらをsprite_xxx配列に代入 (1フレームに1回)。 2.スプライトリストを繰り返して表示されるスプライトを選択し、 それらをスロットにマッピング。(1スキャンラインにつき1回) 3.スプライトスロットを繰り返し、ROM内の各ビットマップスライスを調べて、 2つのスキャンラインバッファのうちの1つにレンダリング。
  • 28. 32.4: Sprites and Slots NB - フレームあたりのスプライトの最大数。 MB - 最大スロット数(スキャンラインあたりのスプライト)。 NB = 5は2の5乗。32スプライト。 parameter NB = 5;// 2^NB == number of sprites parameter MB = 3;// 2^MB == slots per scanline localparam N = 1<<NB;// number of sprites localparam M = 1<<MB;// slots per scanline スプライトデータがRAMから読み込まれた後もそのコピーを 保持するローカルメモリ // copy of sprite data from RAM (N entries) reg [7:0] sprite_xpos[0:N-1];// X positions reg [7:0] sprite_ypos[0:N-1];// Y positions reg [7:0] sprite_attr[0:N-1];// attributes
  • 29. 32.4: Sprites and Slots スロットがアクティブであるかどうかを示すブール値のマークを付けて、 スキャンラインごとのスロットについて同様のデータを保持 // M sprite slots reg [7:0] line_xpos[0:M-1];// X pos for M slots reg [7:0] line_yofs[0:M-1];// Y pos for M slots reg [7:0] line_attr[0:M-1];// attr for M slots reg line_active[0:M-1];// slot active?
  • 30. 32.5: Lodaing Sprite Data From RAM if (reset || vpos[8]) begin // load sprites from RAM on line 260 // 8 cycles per sprite // do first sprite twice b/c CPU might still be busy if (vpos == 260 && hpos < N*2+8) begin ram_busy <= 1; case (hpos[0]) 0: begin ram_addr <= {load_index, 1'b0}; // load X and Y position (2 cycles ago) sprite_xpos[load_index] <= ram_data[7:0]; sprite_ypos[load_index] <= ram_data[15:8]; end 1: begin ram_addr <= {load_index, 1'b1}; // load attribute (2 cycles ago) sprite_attr[load_index] <= ram_data[7:0]; end endcase end 最初の数個のスプライトは、 CPUがビジー状態(停止するまで最大5サ イクルかかる可能性がある)なので 破損している可能性があり。 他のスプライトがすべて読み取られた後に、 それらを再度読み取り。
  • 31. 32.6: Itrating The Sprite List 各スキャンラインの始めに、すべてのスプライトをスキャンし、 どれがこのスキャンラインと交差するかを判断。 それから、Yオフセットを保存し、アクティブにして、sprite_to_lineマッピングを更新。 スプライトごとに2サイクル。
  • 32. 32.6: Itrating The Sprite List 各スキャンラインの始めに、すべてのスプライトをスキャンし、 どれがこのスキャンラインと交差するかを判断します。 それから、Yオフセットを保存し、アクティブにして、 sprite_to_lineマッピングを更新します。 これはスプライトごとに2サイクルかかります。 end else if (hpos < N*2) begin // setup vars for next phase k <= 0; romload <= 0; // select the sprites that will appear in this scanline case (hpos[0]) 0: z <= 8'(vpos - sprite_ypos[i]); // compute Y offset of sprite relative to scanline 1: begin // sprite is active if Y offset is 0..15 if (z < 16) begin line_xpos[j] <= sprite_xpos[i]; // save X pos line_yofs[j] <= z; // save Y offset line_attr[j] <= sprite_attr[i]; // save attr line_active[j] <= 1; // mark sprite active j <= j + 1; // inc counter end i <= i + 1; // inc main array counter end endcase
  • 33. 32.7: Sprite Setup スロットを埋めた後、レンダリングに行う。 各スプライトスロットに18サイクル、 セットアップに2、レンダリングに16を使用できる。 セットアップ段階での操作 •spriteのX座標を使用して、ピクセルを書き込むデュアルスキャンラインバッファのオフセットを 計算。 •spriteのROMアドレスを計算し、16ビットのビットマップスライスを取得。 •spriteの属性(RGBカラー)を調べる。 •spriteスロットがアクティブでない場合は、そのビットマップスライスをすべての透明ピクセル (ゼロ)に設定。 •アクティブビットをクリアして次のスロットに。 これを完了するには少なくとも2クロックサイクルが必要。 そのため、romloadフラグを使用して2つのフェーズを切り替る。 最初のサイクルで、ROMアドレスを設定。
  • 34. 32.7: Sprite Setup if (out_bitmap == 0) begin case (romload) 0: begin rom_addr <= {4'b0, line_attr[k][7:4], line_yofs[k]}; // set ROM address and fetch bitmap end 1: begin // load scanline buffer offset to write write_ofs <= {~vpos[0], line_xpos[k]}; // fetch 0 if sprite is inactive out_bitmap <= line_active[k] ? rom_data : 0; // load attribute for sprite out_attr <= line_attr[k]; // disable sprite for next scanline line_active[k] <= 0; // go to next sprite in 2ndary buffer k <= k + 1; end endcase romload <= !romload;