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
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));
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;