SlideShare a Scribd company logo
1 of 24
Download to read offline
Designing Video Game Hardware in Verilog
chap 17 ~ 22
17 - Sprites
• Sprite とは
• スクリーン上を動くビットマップ
• 左右対称
• この章でやること
• Rece car sprite の描画
• スクリーン上の固定の位置に表示
• 16 x 16 ピクセル
codemodule car_bitmap(yofs, bits);
input [3:0] yofs; // 0 15
output [7:0] bits; // 左右対称なので16pixedの半分でよい
reg [7:0] bitarray[0:15];
assign bits = bitarray[yofs];
initial begin // textに書いてあるbitmapとは若干違う
bitarray[0] = 8 b00000000; // 車の下の方。0にしておく必要がある
bitarray[1] = 8'b00001100;
bitarray[2] = 8'b11001100;
bitarray[3] = 8'b11111100;
bitarray[4] = 8'b11101100;
bitarray[5] = 8'b11100000;
bitarray[6] = 8'b01100000;
bitarray[7] = 8'b01110000;
bitarray[8] = 8'b00110000;
bitarray[9] = 8'b00110000;
bitarray[10] = 8'b00110000;
bitarray[11] = 8'b01101110;
bitarray[12] = 8'b11101110;
bitarray[13] = 8'b11111110;
bitarray[14] = 8'b11101110;
bitarray[15] = 8 b00101110; // 車の上の方
end
endmodule
code
// 定義したROMと接続
reg [3:0] car_sprite_xofs;
reg [3:0] car_sprite_yofs;
wire [7:0] car_sprite_bits;
car_bitmap car(
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
// spriteの位置を定義(固定)
reg [8:0] player_x = 128;
reg [8:0] player_y = 128;
// scanline毎にcar_sprite_yofsを設定

// car_sprite_yofsは基本的に0
always @(posedge hsync)
if (vpos == player_y)
car_sprite_yofs <= 15;
else if (car_sprite_yofs != 0)
car_sprite_yofs <= car_sprite_yofs - 1;

// clock毎(pixel毎)にcar_sprite_xofsを設定
// car_sprite_xofsは基本的に0

always @(posedge clk)
if (hpos == player_x)
car_sprite_xofs <= 15;
else if (car_sprite_xofs != 0)
car_sprite_xofs <= car_sprite_xofs - 1;
code
// car_gfxを定義していく
// しかし car_sprite_bits は 8bit 幅なため以下の定義は不適切
//
// wire car_gfx = car_sprite_bits[car_sprite_xofs]
// car_sprite_xofs をミラーリングする素直な実装
wire [3:0] car_bit = car_sprite_xofs>=8 ? 15-car_sprite_xofs : car_sprite_xofs;
// 以下の実装も等価
// ( 7 = b0111 であり、右 3bit が1であれば動くので15でもok )
// wire [3:0] car_bit = car_sprite_xofs[3] ? car_sprite_xofs ^ 7 : car_sprite_xofs;
wire car_gfx = car_sprite_bits[car_bit[2:0]]
play
• player_x, player_y の値を変えてみる
• bitmapの値を変えてみる(特に0列目と0行目)
• car_bit の実装を変えてみる(xorを使ったバージョン、7を15に変えたバージョン)
• vsync 毎に player_x, player_yの値を変えてみる
18 - Better Sprites
• Finite State Machine
• 人間にとって理解しやすい
• 多くの合成ツールはFSMに対して最適化をかける
• Verilog における State Machine は以下の3コンポーネントで構成される
• State register : 現在の状態
• Next state logic : 状態遷移関数
• Output logic : 出力関数
Example FSM
always @(pasedge clk)
begin
case (state) // 状態管理レジスタ
STATE_LABEL: begin
register <= some_logic; // 出力関数
out <= some_output; // 出力関数
if (condition) state <= NEXT_STATE; // 状態遷移関数
if (other_condition) state <= YET_ANOTHER_STATE; // 状態遷移関数
end
/// .. more case handlers
endcase
end
Sprite renderer state machine
WAIT_FOR_VSTART
WAIT_FOR_LOAD
LOAD1_SETUP
LOAD1_FETCH
WAIT_FOR_HSTART
DRAW
vstart == 1
load == 1
hstart == 1
xcount == 15
count == 15 && ycount == 15
codemodule sprite_renderer(
input clk,
input vstart, // 上辺の描画開始を知らせるシグナル
input load, // sprite のビットマップデータをロードできることを知らせるシグナル
input hstart, // 左辺の描画開始を知らせるシグナル
output [3:0] rom_addr, // ROM のアドレス
input [7:0] rom_bits, // ROM からのinput
output gfx,
output in_progress // vstart を待っているときは 0, それ以外は 1
);
reg [2:0] state; // current state
localparam WAIT_FOR_VSTART = 0;
localparam WAIT_FOR_LOAD = 1;
localparam LOAD1_SETUP = 2;
localparam LOAD1_FETCH = 3;
localparam WAIT_FOR_HSTART = 4;
localparam DRAW = 5;
assign in_progress = state != WAIT_FOR_VSTART;
reg [3:0] ycount; // number of scanlines drawn so far
reg [3:0] xcount; // number of horiz. pixels in this line
reg [7:0] outbits; // register to store bits from ROM
codealways @(posedge clk)
begin
case (state)
WAIT_FOR_VSTART: begin
ycount <= 0; // initialize vertical count
gfx <= 0; // default pixel value (off)
// wait for vstart, then next state
if (vstart)
state <= WAIT_FOR_LOAD;
end
WAIT_FOR_LOAD: begin
xcount <= 0; // WAIT_FOR_HSTARTでよくない?
gfx <= 0;
// wait for load, then next state
if (load)
state <= LOAD1_SETUP;
end
LOAD1_SETUP: begin
rom_addr <= ycount; // load ROM address
state <= LOAD1_FETCH;
end
LOAD1_FETCH: begin
// 1 clock cycle でROMからデータをとれる前提だけど

// これって常にそうなん?
outbits <= rom_bits; // latch bits from ROM
state <= WAIT_FOR_HSTART;
end
WAIT_FOR_HSTART: begin
// wait for hstart, then start drawing
if (hstart)
state <= DRAW;
end
DRAW: begin
// get pixel, mirroring graphics left/right
gfx <= outbits[xcount<8 ? xcount[2:0] : xcount[2:0]];
xcount <= xcount + 1;
// finished drawing horizontal slice?
if (xcount == 15) // pre-increment value
ycount <= ycount + 1;
// finished drawing sprite?
if (xcount == 15 && ycount == 15) // pre-increment value
state <= WAIT_FOR_VSTART; // done drawing sprite
else if (xcount == 15)
state <= WAIT_FOR_LOAD; // done drawing this scanline
end
// unknown state -- reset
default: begin
state <= WAIT_FOR_VSTART;
end
endcase
end
code// car bitmap ROM and associated wires
wire [3:0] car_sprite_addr;
wire [7:0] car_sprite_bits;
car_bitmap car(
.yofs(car_sprite_addr),
.bits(car_sprite_bits));
// convert player X/Y to 9 bits and compare to CRT hpos/vpos
wire vstart = {1'b0,player_y} == vpos;
wire hstart = {1'b0,player_x} == hpos;
wire car_gfx; // car sprite video signal
wire in_progress; // 1 = rendering taking place on scanline
// sprite renderer module
sprite_renderer renderer(
.clk(clk),
.vstart(vstart),
.load(hsync), // sprite データは画面外にいるときに取得される
.hstart(hstart),
.rom_addr(car_sprite_addr),
.rom_bits(car_sprite_bits),
.gfx(car_gfx),
.in_progress(in_progress));
19 - Racing Game
• 複数 sprite と、衝突検知のデモンストレーション
• 2つの sprite rendering module を使用するが、

使うROMは1つだけにしたい
• 言語レベルでは問題ないが、合成時に問題になる
• 実際の回路では同時アクセスに制限がある
• hposが256 259の間はplayer spriteのROMアクセス、

hposが260 以降は enemy spriteのROMアクセスにあてる
code// ROMにアクセスする対象を決める
wire player_load = (hpos >= 256) && (hpos < 260);
wire enemy_load = (hpos >= 260);
// wire up car sprite ROM
// multiplex between player and enemy ROM address
wire [3:0] player_sprite_yofs;
wire [3:0] enemy_sprite_yofs;
wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs;
wire [7:0] car_sprite_bits;
car_bitmap car(
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
// player の sprite を描画するためのシグナルを定義
wire player_vstart = {1'b0,player_y} == vpos;
wire player_hstart = {1'b0,player_x} == hpos;
wire player_gfx;
wire player_is_drawing;
// player sprite generator
sprite_renderer player_renderer(
.clk(clk),
.vstart(player_vstart),
.load(player_load),
.hstart(player_hstart),
.rom_addr(player_sprite_yofs),
.rom_bits(car_sprite_bits),
.gfx(player_gfx),
.in_progress(player_is_drawing));
// signals for enemy sprite generator
wire enemy_vstart = {1'b0,enemy_y} == vpos;
wire enemy_hstart = {1'b0,enemy_x} == hpos;
wire enemy_gfx;
wire enemy_is_drawing;
// enemy sprite generator
sprite_renderer enemy_renderer(
.clk(clk),
.vstart(enemy_vstart),
.load(enemy_load),
.hstart(enemy_hstart),
.rom_addr(enemy_sprite_yofs),
.rom_bits(car_sprite_bits),
.gfx(enemy_gfx),
.in_progress(enemy_is_drawing));
code// signals for enemy bouncing off left/right borders
wire enemy_hit_left = (enemy_x == 64); // 左側 64pixel は無敵ゾーン
wire enemy_hit_right = (enemy_x == 192); // 右側 48pixel は無敵ゾーン(spriteの幅だけ無敵ゾーン小さい)
wire enemy_hit_edge = enemy_hit_left ¦¦ enemy_hit_right;
// player, enemy, track のポジションをフレーム毎に更新する
always @(posedge vsync)
begin
player_x <= paddle_x;
player_y <= 180;
track_pos <= track_pos + {11'b0,speed[7:4]};
enemy_y <= enemy_y + {3'b0, speed[7:4]};
if (enemy_hit_edge)
enemy_dir <= !enemy_dir;
if (enemy_dir ^ enemy_hit_edge)
enemy_x <= enemy_x + 1;
else
enemy_x <= enemy_x - 1;
// collision check?
if (frame_collision)
speed <= 16;
else if (speed < paddle_y)
speed <= speed + 1;
else
speed <= speed - 1;
end
// set to 1 when player collides with enemy or track
reg frame_collision;
always @(posedge clk)
if (player_gfx && (enemy_gfx ¦¦ track_gfx))
frame_collision <= 1;
else if (vsync)
frame_collision <= 0;
// track graphics signals
wire track_offside = (hpos[7:5]==0) ¦¦ (hpos[7:5]==7);
wire track_shoulder = (hpos[7:3]==3) ¦¦ (hpos[7:3]==28);
wire track_gfx = (vpos[5:1]!=track_pos[5:1]) && track_offside;
20 - Sprite Rotation
• 回転するspriteを実現したい
• 各回転におけるbitmapを定義することで実現する
• 上下左右にミラーリングすることで、(N/4) + 1個のbitmap
でNステップの回転を表現できる
code// 16個のrotation stepを5個のbitmapにマッピングするモジュール

module rotation_selector(
input [3:0] rotation,
output [2:0] bitmap_num,
output hmirror, vmirror
);
always @(*) // なんで rotation ではなく * なの?
case (rotation[3:2]) // 0 3, 4 7, 8 11, 12 15 で場合分け
0: begin // 0..3 -> 0..3
bitmap_num = {1'b0, rotation[1:0]};
hmirror = 0;
vmirror = 0;
end
1: begin // 4..7 -> 4..1
bitmap_num = -rotation[2:0];
hmirror = 0;
vmirror = 1;
end
2: begin // 8-11 -> 0..3
bitmap_num = {1'b0, rotation[1:0]};
hmirror = 1;
vmirror = 1;
end
3: begin // 12-15 -> 4..1
bitmap_num = -rotation[2:0];
hmirror = 1;
vmirror = 0;
end
endcase
endmodule
21 - Motion Vectors
• tank動かすぞー
• 衝突の検知
Fixed-Point
• 1フレーム毎に1ピクセル動かすと早すぎる
• 固定小数点の導入
• 100101100101 → 10010110.0101 ( 1/16 )
reg [11:0] player_x_fixed;
wire [7:0] player_x = player_x_fixed[11:4];
wire [3:0] player_x_frac = player_x_fixed[3:0];
The sine function
• サイン関数の実装
• 完全なサイン関数の再現は不可能
• 今回のケースではかなり粗い精度で十分
• 入力値として16パターンだけ受け付ける
• 出力は -7 7 の4bit
• case文による場合分けで実装
• コサイン関数は、入力に90度足してから

サイン関数に渡すことで実現できる
function signed [3:0] sin_16x4;
input [3:0] in; // input angle 0..15
integer y; // 符号付にしたいからintegerにしてる
case (in[1:0]) // 4 values per quadrant
0: y = 0;
1: y = 3;
2: y = 5; // sin(45) * 7
3: y = 6; // sin(67.5) * 7
endcase
case (in[3:2]) // 4 quadrants
0: sin_16x4 = 4 (y); // 0 3 → 0 6
1: sin_16x4 = 4 (7-y); // 4 7 → 7 1
2: sin_16x4 = 4 (-y); // 8 11 → 0 -6
3: sin_16x4 = 4 (y-7); // 12 15 → -7 -1
endcase
endfunction
Move During the Hsync
• サイン関数、コサイン関数の結果を、速度で乗じたい
• verilogに掛け算の機能はない!(1970年代は)
• 「X掛ける」を「X回たす」で実装する
• sin_16x4 回路1つだけで実現できるように、

x座標の計算とy座標の計算を別のタイミングで行う
if (vpos < 9'(player_speed)) begin // ここで「player_speed回たす」を実現している
if (vpos[0])
player_x_fixed <= player_x_fixed + 12 (sin_16x4(player_rot));
else
player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+4));
end
code
module tank_bitmap(
input [7:0] addr,
output [7:0] bits
);
// output は 8bit 幅だが bitarray は 16bit 幅
reg [15:0] bitarray[0:255];
// addrの最下位bitで、16bit の前半と後半どちらを返すのかを決定する
assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0];
initial begin/*{w:16,h:16,bpw:16,count:5}*/
bitarray['h00] = 16'b11110000000;
bitarray['h01] = 16 b11110000000;
…
…
end
endmodule
codemodule sprite_renderer2(
input clk, vstart, load, hstart, // FSMで使用するシグナル
output [4:0] rom_addr, // rom_addr[4:1]はspriteの何行目か、rom_addr[0]はその行の前半後半どちらか、を表す
input [7:0] rom_bits, // ROMから取得した8bit
input hmirror, vmirror, // ROMから取得したデータをミラーリングして表示するか
output gfx, // 現在のycount, xcount (内部レジスタ)で描画するもの
output busy
);
assign busy = state != WAIT_FOR_VSTART;
reg [2:0] state;
reg [3:0] ycount;
reg [3:0] xcount;
reg [15:0] outbits; // ROMからfetchした2byteのデータを格納するレジスタ
localparam WAIT_FOR_VSTART = 0;
localparam WAIT_FOR_LOAD = 1;
localparam LOAD1_SETUP = 2;
localparam LOAD1_FETCH = 3;
localparam LOAD2_SETUP = 4; // 前回と違い、2byte取得する必要がある
localparam LOAD2_FETCH = 5;
localparam WAIT_FOR_HSTART = 6;
localparam DRAW = 7;
always @(posedge clk)
begin
case (state)
… // 前回とほぼ同じ
endcase
end
endmodule
codealways @(posedge vsync or posedge reset)
begin
if (reset) begin
player_rot <= initial_rot;
player_speed <= 0;
end else begin
frame <= frame + 1; // increment frame counter
if (frame[0]) begin // only update every other frame
if (switch_left)
player_rot <= player_rot - 1; // turn left
else if (switch_right)
player_rot <= player_rot + 1; // turn right
if (switch_up) begin
if (player_speed != 10) // max accel
player_speed <= player_speed + 1;
end else
player_speed <= 0; // stop
end
end
end
reg collision_detected;
always @(posedge clk)
if (vstart)
collision_detected <= 0; // textはここ間違ってる
else if (collision_gfx)
collision_detected <= 1;

More Related Content

What's hot

FPGAでゲーム機を作ろう! 第6回
FPGAでゲーム機を作ろう! 第6回FPGAでゲーム機を作ろう! 第6回
FPGAでゲーム機を作ろう! 第6回yoshimitsusudoh
 
130710 02
130710 02130710 02
130710 02openrtm
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenMITSUNARI Shigeo
 
cdev_write and_comwrite
cdev_write and_comwritecdev_write and_comwrite
cdev_write and_comwritekusabanachi
 
SEH on mingw32
SEH on mingw32SEH on mingw32
SEH on mingw32kikairoya
 
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)MITSUNARI Shigeo
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解MITSUNARI Shigeo
 
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくる
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくるデジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくる
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくるAtsushi Tadokoro
 
dofilewrite and vn_write
dofilewrite and vn_writedofilewrite and vn_write
dofilewrite and vn_writekusabanachi
 
64-bit SML# への壁
64-bit SML# への壁64-bit SML# への壁
64-bit SML# への壁chunjp
 
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習B
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習BopenFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習B
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習BAtsushi Tadokoro
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたMITSUNARI Shigeo
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチンyohhoy
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutinemelpon
 

What's hot (20)

ゆるバグ
ゆるバグゆるバグ
ゆるバグ
 
FPGAでゲーム機を作ろう! 第6回
FPGAでゲーム機を作ろう! 第6回FPGAでゲーム機を作ろう! 第6回
FPGAでゲーム機を作ろう! 第6回
 
Boost.SIMD
Boost.SIMDBoost.SIMD
Boost.SIMD
 
130710 02
130710 02130710 02
130710 02
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
 
cdev_write and_comwrite
cdev_write and_comwritecdev_write and_comwrite
cdev_write and_comwrite
 
SEH on mingw32
SEH on mingw32SEH on mingw32
SEH on mingw32
 
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解
 
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくる
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくるデジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくる
デジタルアートセミナー#2 openFrameworksで学ぶ、 クリエイティブ・コーディング Session 2: 構造をつくる
 
dofilewrite and vn_write
dofilewrite and vn_writedofilewrite and vn_write
dofilewrite and vn_write
 
64-bit SML# への壁
64-bit SML# への壁64-bit SML# への壁
64-bit SML# への壁
 
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習B
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習BopenFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習B
openFrameworks基礎 たくさんの図形を動かす 静的配列と動的配列 - 芸大グラフィックスプログラミング演習B
 
コルーチンの使い方
コルーチンの使い方コルーチンの使い方
コルーチンの使い方
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
 
From IA-32 to avx-512
From IA-32 to avx-512From IA-32 to avx-512
From IA-32 to avx-512
 
ttwrite
ttwritettwrite
ttwrite
 
コルーチンを使おう
コルーチンを使おうコルーチンを使おう
コルーチンを使おう
 

Similar to Designing video game hardware in verilog

Cvim saisentan 半精度浮動小数点数 half
Cvim saisentan 半精度浮動小数点数 halfCvim saisentan 半精度浮動小数点数 half
Cvim saisentan 半精度浮動小数点数 halftomoaki0705
 
PBL1-v1-011j.pptx
PBL1-v1-011j.pptxPBL1-v1-011j.pptx
PBL1-v1-011j.pptxNAIST
 
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョン
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョンenchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョン
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョンRyota Shiroguchi
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptxNAIST
 
Android OpenGL HandsOn
Android OpenGL HandsOnAndroid OpenGL HandsOn
Android OpenGL HandsOnIkuo Tansho
 
coma Study Room vol.2 Arduino Workshop
coma Study Room vol.2 Arduino Workshopcoma Study Room vol.2 Arduino Workshop
coma Study Room vol.2 Arduino WorkshopEto Haruhiko
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputingNoboru Irieda
 
VHDL-2008が好きなんです
VHDL-2008が好きなんですVHDL-2008が好きなんです
VHDL-2008が好きなんですwindy12806
 
VerilatorとSystemCでSoftware Driven Verification
VerilatorとSystemCでSoftware Driven VerificationVerilatorとSystemCでSoftware Driven Verification
VerilatorとSystemCでSoftware Driven VerificationMr. Vengineer
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングKiwamu Okabe
 
enchant.jsでゲーム制作をはじめてみよう
enchant.jsでゲーム制作をはじめてみようenchant.jsでゲーム制作をはじめてみよう
enchant.jsでゲーム制作をはじめてみようRyota Shiroguchi
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptxNAIST
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)Takeshi Yamamuro
 
Xbyakの紹介とその周辺
Xbyakの紹介とその周辺Xbyakの紹介とその周辺
Xbyakの紹介とその周辺MITSUNARI Shigeo
 
Androidプログラミング初心者のためのゲームアプリ開発入門
Androidプログラミング初心者のためのゲームアプリ開発入門Androidプログラミング初心者のためのゲームアプリ開発入門
Androidプログラミング初心者のためのゲームアプリ開発入門Masahiko Mizuta
 
コンピューティングとJava~なにわTECH道
コンピューティングとJava~なにわTECH道コンピューティングとJava~なにわTECH道
コンピューティングとJava~なにわTECH道なおき きしだ
 
StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件yaegashi
 
高位合成友の会@ドワンゴ,2015年12月8日
高位合成友の会@ドワンゴ,2015年12月8日高位合成友の会@ドワンゴ,2015年12月8日
高位合成友の会@ドワンゴ,2015年12月8日貴大 山下
 
メディア・アートII 第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーション
メディア・アートII  第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーションメディア・アートII  第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーション
メディア・アートII 第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーションAtsushi Tadokoro
 

Similar to Designing video game hardware in verilog (20)

Cvim saisentan 半精度浮動小数点数 half
Cvim saisentan 半精度浮動小数点数 halfCvim saisentan 半精度浮動小数点数 half
Cvim saisentan 半精度浮動小数点数 half
 
PBL1-v1-011j.pptx
PBL1-v1-011j.pptxPBL1-v1-011j.pptx
PBL1-v1-011j.pptx
 
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョン
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョンenchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョン
enchant.jsでゲーム制作をはじめてみよう 「パンダの会」バージョン
 
PBL1-v1-008j.pptx
PBL1-v1-008j.pptxPBL1-v1-008j.pptx
PBL1-v1-008j.pptx
 
Android OpenGL HandsOn
Android OpenGL HandsOnAndroid OpenGL HandsOn
Android OpenGL HandsOn
 
coma Study Room vol.2 Arduino Workshop
coma Study Room vol.2 Arduino Workshopcoma Study Room vol.2 Arduino Workshop
coma Study Room vol.2 Arduino Workshop
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputing
 
VHDL-2008が好きなんです
VHDL-2008が好きなんですVHDL-2008が好きなんです
VHDL-2008が好きなんです
 
VerilatorとSystemCでSoftware Driven Verification
VerilatorとSystemCでSoftware Driven VerificationVerilatorとSystemCでSoftware Driven Verification
VerilatorとSystemCでSoftware Driven Verification
 
HaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミングHaskellではじめるCortex-M3組込みプログラミング
HaskellではじめるCortex-M3組込みプログラミング
 
enchant.jsでゲーム制作をはじめてみよう
enchant.jsでゲーム制作をはじめてみようenchant.jsでゲーム制作をはじめてみよう
enchant.jsでゲーム制作をはじめてみよう
 
Minix smp
Minix smpMinix smp
Minix smp
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptx
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
 
Xbyakの紹介とその周辺
Xbyakの紹介とその周辺Xbyakの紹介とその周辺
Xbyakの紹介とその周辺
 
Androidプログラミング初心者のためのゲームアプリ開発入門
Androidプログラミング初心者のためのゲームアプリ開発入門Androidプログラミング初心者のためのゲームアプリ開発入門
Androidプログラミング初心者のためのゲームアプリ開発入門
 
コンピューティングとJava~なにわTECH道
コンピューティングとJava~なにわTECH道コンピューティングとJava~なにわTECH道
コンピューティングとJava~なにわTECH道
 
StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件
 
高位合成友の会@ドワンゴ,2015年12月8日
高位合成友の会@ドワンゴ,2015年12月8日高位合成友の会@ドワンゴ,2015年12月8日
高位合成友の会@ドワンゴ,2015年12月8日
 
メディア・アートII 第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーション
メディア・アートII  第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーションメディア・アートII  第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーション
メディア・アートII 第2回 openFrameworks基礎 配列、くりかえし、乱数 ベクトルを使用したアニメーション
 

Designing video game hardware in verilog

  • 1. Designing Video Game Hardware in Verilog chap 17 ~ 22
  • 2. 17 - Sprites • Sprite とは • スクリーン上を動くビットマップ • 左右対称 • この章でやること • Rece car sprite の描画 • スクリーン上の固定の位置に表示 • 16 x 16 ピクセル
  • 3. codemodule car_bitmap(yofs, bits); input [3:0] yofs; // 0 15 output [7:0] bits; // 左右対称なので16pixedの半分でよい reg [7:0] bitarray[0:15]; assign bits = bitarray[yofs]; initial begin // textに書いてあるbitmapとは若干違う bitarray[0] = 8 b00000000; // 車の下の方。0にしておく必要がある bitarray[1] = 8'b00001100; bitarray[2] = 8'b11001100; bitarray[3] = 8'b11111100; bitarray[4] = 8'b11101100; bitarray[5] = 8'b11100000; bitarray[6] = 8'b01100000; bitarray[7] = 8'b01110000; bitarray[8] = 8'b00110000; bitarray[9] = 8'b00110000; bitarray[10] = 8'b00110000; bitarray[11] = 8'b01101110; bitarray[12] = 8'b11101110; bitarray[13] = 8'b11111110; bitarray[14] = 8'b11101110; bitarray[15] = 8 b00101110; // 車の上の方 end endmodule
  • 4. code // 定義したROMと接続 reg [3:0] car_sprite_xofs; reg [3:0] car_sprite_yofs; wire [7:0] car_sprite_bits; car_bitmap car( .yofs(car_sprite_yofs), .bits(car_sprite_bits)); // spriteの位置を定義(固定) reg [8:0] player_x = 128; reg [8:0] player_y = 128; // scanline毎にcar_sprite_yofsを設定
 // car_sprite_yofsは基本的に0 always @(posedge hsync) if (vpos == player_y) car_sprite_yofs <= 15; else if (car_sprite_yofs != 0) car_sprite_yofs <= car_sprite_yofs - 1;
 // clock毎(pixel毎)にcar_sprite_xofsを設定 // car_sprite_xofsは基本的に0
 always @(posedge clk) if (hpos == player_x) car_sprite_xofs <= 15; else if (car_sprite_xofs != 0) car_sprite_xofs <= car_sprite_xofs - 1;
  • 5. code // car_gfxを定義していく // しかし car_sprite_bits は 8bit 幅なため以下の定義は不適切 // // wire car_gfx = car_sprite_bits[car_sprite_xofs] // car_sprite_xofs をミラーリングする素直な実装 wire [3:0] car_bit = car_sprite_xofs>=8 ? 15-car_sprite_xofs : car_sprite_xofs; // 以下の実装も等価 // ( 7 = b0111 であり、右 3bit が1であれば動くので15でもok ) // wire [3:0] car_bit = car_sprite_xofs[3] ? car_sprite_xofs ^ 7 : car_sprite_xofs; wire car_gfx = car_sprite_bits[car_bit[2:0]]
  • 6. play • player_x, player_y の値を変えてみる • bitmapの値を変えてみる(特に0列目と0行目) • car_bit の実装を変えてみる(xorを使ったバージョン、7を15に変えたバージョン) • vsync 毎に player_x, player_yの値を変えてみる
  • 7. 18 - Better Sprites • Finite State Machine • 人間にとって理解しやすい • 多くの合成ツールはFSMに対して最適化をかける • Verilog における State Machine は以下の3コンポーネントで構成される • State register : 現在の状態 • Next state logic : 状態遷移関数 • Output logic : 出力関数
  • 8. Example FSM always @(pasedge clk) begin case (state) // 状態管理レジスタ STATE_LABEL: begin register <= some_logic; // 出力関数 out <= some_output; // 出力関数 if (condition) state <= NEXT_STATE; // 状態遷移関数 if (other_condition) state <= YET_ANOTHER_STATE; // 状態遷移関数 end /// .. more case handlers endcase end
  • 9. Sprite renderer state machine WAIT_FOR_VSTART WAIT_FOR_LOAD LOAD1_SETUP LOAD1_FETCH WAIT_FOR_HSTART DRAW vstart == 1 load == 1 hstart == 1 xcount == 15 count == 15 && ycount == 15
  • 10. codemodule sprite_renderer( input clk, input vstart, // 上辺の描画開始を知らせるシグナル input load, // sprite のビットマップデータをロードできることを知らせるシグナル input hstart, // 左辺の描画開始を知らせるシグナル output [3:0] rom_addr, // ROM のアドレス input [7:0] rom_bits, // ROM からのinput output gfx, output in_progress // vstart を待っているときは 0, それ以外は 1 ); reg [2:0] state; // current state localparam WAIT_FOR_VSTART = 0; localparam WAIT_FOR_LOAD = 1; localparam LOAD1_SETUP = 2; localparam LOAD1_FETCH = 3; localparam WAIT_FOR_HSTART = 4; localparam DRAW = 5; assign in_progress = state != WAIT_FOR_VSTART; reg [3:0] ycount; // number of scanlines drawn so far reg [3:0] xcount; // number of horiz. pixels in this line reg [7:0] outbits; // register to store bits from ROM
  • 11. codealways @(posedge clk) begin case (state) WAIT_FOR_VSTART: begin ycount <= 0; // initialize vertical count gfx <= 0; // default pixel value (off) // wait for vstart, then next state if (vstart) state <= WAIT_FOR_LOAD; end WAIT_FOR_LOAD: begin xcount <= 0; // WAIT_FOR_HSTARTでよくない? gfx <= 0; // wait for load, then next state if (load) state <= LOAD1_SETUP; end LOAD1_SETUP: begin rom_addr <= ycount; // load ROM address state <= LOAD1_FETCH; end LOAD1_FETCH: begin // 1 clock cycle でROMからデータをとれる前提だけど
 // これって常にそうなん? outbits <= rom_bits; // latch bits from ROM state <= WAIT_FOR_HSTART; end WAIT_FOR_HSTART: begin // wait for hstart, then start drawing if (hstart) state <= DRAW; end DRAW: begin // get pixel, mirroring graphics left/right gfx <= outbits[xcount<8 ? xcount[2:0] : xcount[2:0]]; xcount <= xcount + 1; // finished drawing horizontal slice? if (xcount == 15) // pre-increment value ycount <= ycount + 1; // finished drawing sprite? if (xcount == 15 && ycount == 15) // pre-increment value state <= WAIT_FOR_VSTART; // done drawing sprite else if (xcount == 15) state <= WAIT_FOR_LOAD; // done drawing this scanline end // unknown state -- reset default: begin state <= WAIT_FOR_VSTART; end endcase end
  • 12. code// car bitmap ROM and associated wires wire [3:0] car_sprite_addr; wire [7:0] car_sprite_bits; car_bitmap car( .yofs(car_sprite_addr), .bits(car_sprite_bits)); // convert player X/Y to 9 bits and compare to CRT hpos/vpos wire vstart = {1'b0,player_y} == vpos; wire hstart = {1'b0,player_x} == hpos; wire car_gfx; // car sprite video signal wire in_progress; // 1 = rendering taking place on scanline // sprite renderer module sprite_renderer renderer( .clk(clk), .vstart(vstart), .load(hsync), // sprite データは画面外にいるときに取得される .hstart(hstart), .rom_addr(car_sprite_addr), .rom_bits(car_sprite_bits), .gfx(car_gfx), .in_progress(in_progress));
  • 13. 19 - Racing Game • 複数 sprite と、衝突検知のデモンストレーション • 2つの sprite rendering module を使用するが、
 使うROMは1つだけにしたい • 言語レベルでは問題ないが、合成時に問題になる • 実際の回路では同時アクセスに制限がある • hposが256 259の間はplayer spriteのROMアクセス、
 hposが260 以降は enemy spriteのROMアクセスにあてる
  • 14. code// ROMにアクセスする対象を決める wire player_load = (hpos >= 256) && (hpos < 260); wire enemy_load = (hpos >= 260); // wire up car sprite ROM // multiplex between player and enemy ROM address wire [3:0] player_sprite_yofs; wire [3:0] enemy_sprite_yofs; wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs; wire [7:0] car_sprite_bits; car_bitmap car( .yofs(car_sprite_yofs), .bits(car_sprite_bits)); // player の sprite を描画するためのシグナルを定義 wire player_vstart = {1'b0,player_y} == vpos; wire player_hstart = {1'b0,player_x} == hpos; wire player_gfx; wire player_is_drawing; // player sprite generator sprite_renderer player_renderer( .clk(clk), .vstart(player_vstart), .load(player_load), .hstart(player_hstart), .rom_addr(player_sprite_yofs), .rom_bits(car_sprite_bits), .gfx(player_gfx), .in_progress(player_is_drawing)); // signals for enemy sprite generator wire enemy_vstart = {1'b0,enemy_y} == vpos; wire enemy_hstart = {1'b0,enemy_x} == hpos; wire enemy_gfx; wire enemy_is_drawing; // enemy sprite generator sprite_renderer enemy_renderer( .clk(clk), .vstart(enemy_vstart), .load(enemy_load), .hstart(enemy_hstart), .rom_addr(enemy_sprite_yofs), .rom_bits(car_sprite_bits), .gfx(enemy_gfx), .in_progress(enemy_is_drawing));
  • 15. code// signals for enemy bouncing off left/right borders wire enemy_hit_left = (enemy_x == 64); // 左側 64pixel は無敵ゾーン wire enemy_hit_right = (enemy_x == 192); // 右側 48pixel は無敵ゾーン(spriteの幅だけ無敵ゾーン小さい) wire enemy_hit_edge = enemy_hit_left ¦¦ enemy_hit_right; // player, enemy, track のポジションをフレーム毎に更新する always @(posedge vsync) begin player_x <= paddle_x; player_y <= 180; track_pos <= track_pos + {11'b0,speed[7:4]}; enemy_y <= enemy_y + {3'b0, speed[7:4]}; if (enemy_hit_edge) enemy_dir <= !enemy_dir; if (enemy_dir ^ enemy_hit_edge) enemy_x <= enemy_x + 1; else enemy_x <= enemy_x - 1; // collision check? if (frame_collision) speed <= 16; else if (speed < paddle_y) speed <= speed + 1; else speed <= speed - 1; end // set to 1 when player collides with enemy or track reg frame_collision; always @(posedge clk) if (player_gfx && (enemy_gfx ¦¦ track_gfx)) frame_collision <= 1; else if (vsync) frame_collision <= 0; // track graphics signals wire track_offside = (hpos[7:5]==0) ¦¦ (hpos[7:5]==7); wire track_shoulder = (hpos[7:3]==3) ¦¦ (hpos[7:3]==28); wire track_gfx = (vpos[5:1]!=track_pos[5:1]) && track_offside;
  • 16. 20 - Sprite Rotation • 回転するspriteを実現したい • 各回転におけるbitmapを定義することで実現する • 上下左右にミラーリングすることで、(N/4) + 1個のbitmap でNステップの回転を表現できる
  • 17. code// 16個のrotation stepを5個のbitmapにマッピングするモジュール
 module rotation_selector( input [3:0] rotation, output [2:0] bitmap_num, output hmirror, vmirror ); always @(*) // なんで rotation ではなく * なの? case (rotation[3:2]) // 0 3, 4 7, 8 11, 12 15 で場合分け 0: begin // 0..3 -> 0..3 bitmap_num = {1'b0, rotation[1:0]}; hmirror = 0; vmirror = 0; end 1: begin // 4..7 -> 4..1 bitmap_num = -rotation[2:0]; hmirror = 0; vmirror = 1; end 2: begin // 8-11 -> 0..3 bitmap_num = {1'b0, rotation[1:0]}; hmirror = 1; vmirror = 1; end 3: begin // 12-15 -> 4..1 bitmap_num = -rotation[2:0]; hmirror = 1; vmirror = 0; end endcase endmodule
  • 18. 21 - Motion Vectors • tank動かすぞー • 衝突の検知
  • 19. Fixed-Point • 1フレーム毎に1ピクセル動かすと早すぎる • 固定小数点の導入 • 100101100101 → 10010110.0101 ( 1/16 ) reg [11:0] player_x_fixed; wire [7:0] player_x = player_x_fixed[11:4]; wire [3:0] player_x_frac = player_x_fixed[3:0];
  • 20. The sine function • サイン関数の実装 • 完全なサイン関数の再現は不可能 • 今回のケースではかなり粗い精度で十分 • 入力値として16パターンだけ受け付ける • 出力は -7 7 の4bit • case文による場合分けで実装 • コサイン関数は、入力に90度足してから
 サイン関数に渡すことで実現できる function signed [3:0] sin_16x4; input [3:0] in; // input angle 0..15 integer y; // 符号付にしたいからintegerにしてる case (in[1:0]) // 4 values per quadrant 0: y = 0; 1: y = 3; 2: y = 5; // sin(45) * 7 3: y = 6; // sin(67.5) * 7 endcase case (in[3:2]) // 4 quadrants 0: sin_16x4 = 4 (y); // 0 3 → 0 6 1: sin_16x4 = 4 (7-y); // 4 7 → 7 1 2: sin_16x4 = 4 (-y); // 8 11 → 0 -6 3: sin_16x4 = 4 (y-7); // 12 15 → -7 -1 endcase endfunction
  • 21. Move During the Hsync • サイン関数、コサイン関数の結果を、速度で乗じたい • verilogに掛け算の機能はない!(1970年代は) • 「X掛ける」を「X回たす」で実装する • sin_16x4 回路1つだけで実現できるように、
 x座標の計算とy座標の計算を別のタイミングで行う if (vpos < 9'(player_speed)) begin // ここで「player_speed回たす」を実現している if (vpos[0]) player_x_fixed <= player_x_fixed + 12 (sin_16x4(player_rot)); else player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+4)); end
  • 22. code module tank_bitmap( input [7:0] addr, output [7:0] bits ); // output は 8bit 幅だが bitarray は 16bit 幅 reg [15:0] bitarray[0:255]; // addrの最下位bitで、16bit の前半と後半どちらを返すのかを決定する assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0]; initial begin/*{w:16,h:16,bpw:16,count:5}*/ bitarray['h00] = 16'b11110000000; bitarray['h01] = 16 b11110000000; … … end endmodule
  • 23. codemodule sprite_renderer2( input clk, vstart, load, hstart, // FSMで使用するシグナル output [4:0] rom_addr, // rom_addr[4:1]はspriteの何行目か、rom_addr[0]はその行の前半後半どちらか、を表す input [7:0] rom_bits, // ROMから取得した8bit input hmirror, vmirror, // ROMから取得したデータをミラーリングして表示するか output gfx, // 現在のycount, xcount (内部レジスタ)で描画するもの output busy ); assign busy = state != WAIT_FOR_VSTART; reg [2:0] state; reg [3:0] ycount; reg [3:0] xcount; reg [15:0] outbits; // ROMからfetchした2byteのデータを格納するレジスタ localparam WAIT_FOR_VSTART = 0; localparam WAIT_FOR_LOAD = 1; localparam LOAD1_SETUP = 2; localparam LOAD1_FETCH = 3; localparam LOAD2_SETUP = 4; // 前回と違い、2byte取得する必要がある localparam LOAD2_FETCH = 5; localparam WAIT_FOR_HSTART = 6; localparam DRAW = 7; always @(posedge clk) begin case (state) … // 前回とほぼ同じ endcase end endmodule
  • 24. codealways @(posedge vsync or posedge reset) begin if (reset) begin player_rot <= initial_rot; player_speed <= 0; end else begin frame <= frame + 1; // increment frame counter if (frame[0]) begin // only update every other frame if (switch_left) player_rot <= player_rot - 1; // turn left else if (switch_right) player_rot <= player_rot + 1; // turn right if (switch_up) begin if (player_speed != 10) // max accel player_speed <= player_speed + 1; end else player_speed <= 0; // stop end end end reg collision_detected; always @(posedge clk) if (vstart) collision_detected <= 0; // textはここ間違ってる else if (collision_gfx) collision_detected <= 1;