30
ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan Section ABG: Wednesday 12:00-2:50pm TAs: Ying Chen, Yi Liang

Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

  • Upload
    buitruc

  • View
    218

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

ECE 385

Spring 2015

Final Project

Feeding Frenzy using SoC and

SystemVerilog

Pichamon Meteveravong, Perut Boribalburephan

Section ABG: Wednesday 12:00-2:50pm

TAs: Ying Chen, Yi Liang

Page 2: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

1

Contents

1 Introduction…………………………………………………………………………………... 3

2 Feeding Frenzy: Survival mode……………………………………………………………... 4

3 Description of Circuit………………………………………………………………………… 5

4 Operation of Circuit………………………………………………………………………….. 5

4.1 The Big Picture…………………..…………………..…………………..……………….. 5

4.2 Hardware Entities…………………..…………………..…………………..…………….. 6

● FeedingFrenzy…………………..…………………..…………………..……………… 6

● GameState…………………..…………………..…………………..………………….. 6

● stopwatch…………………..…………………..…………………..…………………… 6

● VGA_controller…………………..…………………..…………………..…………….. 6

● Color_Mapper…………………..…………………..…………………..……………… 6

● HexDriver…………………..…………………..…………………..…………………. 7

● usb_system (Qsys-generated) ………………..…………………..…………………..… 7

4.3 Software Entities…………………..………………..…………………..………………… 7

● main.c…………………..…………………..………………..…………………………. 7

● usb.c / usb.h…………………..…………………..…………………..………………… 7

● game.c / game.h…………………..…………………..…………………..…………… 7

5 Game Logic (software) …………………..…………………..…………………..………….. 8

5.1 Sprite Attributes……………………………………..…………………..………………. 8

● World position, Instantaneous speed, and Target speed…………………..…………… 8

● Sprite number, Animation number, and Animation time………….……..…………….. 8

● Action and Cooldown…………………..…………………………………..………….. 9

● Growth, Size, Big, Present, and Alive…………………..…………………………….. 10

● Relative anchor, Child node, and Relative positioning………………..……………….. 10

5.2 Mechanics…………………..…………………………………..………………………… 11

● World, Screen, Camera and Game Status…………………..…………………………. 11

● Movement physics…………………..…………………………………..…………….. 11

● Animation sequencing…………………..…………………………………..…………. 11

Page 3: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

2

● List of actions and Action sequencing…………………..…………………………….. 12

● Collision detection (eating/eaten/spawn/kill) …………………..……………………… 13

5.3 AI behavior…………………..…………………………………..………………………. 13

● Event generation and Game difficulty…………………..…………………………….. 13

● Random movement…………………..…………………………………..……………. 14

● Predator mode movement…………………..…………………………………..……… 14

● Prey mode movement…………………..………………………………..……………. 14

● Heuristics for special characters…………………..…………………………………… 14

5.4 Communication With Hardware…………………..…………………………………..…. 15

● Sending the game state…………………..…………………………………..…………. 15

● Polling gamepad input………………..…………………………………..…………… 16

6 Game Rendering (Hardware) …………………..…………………………………..………… 17

6.1 Camera and Background…………………..………………………………..…………… 17

6.2 Retrieving Sprite Pixels…………………..…………………………………..……………. 17

● Preprocessing with python and OpenCV…………………..……………………………. 17

● Depth sorting and calculating effective sprite address…………………..……………... 20

6.3 Scoreboard and Conditional Prompt…………………..………………………………….. 21

6.4 True Output Color…………………..…………………………………..…………………. 22

7 Running the Program………………………………………………………………………… 22

8 Simulations……………………………………………………………………………………. 22

9 Borrowed Work……………………………………………………………………………… 24

10 Timing Report………………………………………………………………………………… 25

11 Block Diagrams……………………………………………………………………………… 26

12 State Machines……………………………………………………………………………… 28

13 Conclusion…………………………………………………………………………………… 29

Page 4: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

3

1 Introduction

As our final project for digital systems laboratory class, we agreed to work on Feeding Frenzy. We both

felt that this aquarium-like game was considered quite challenging, but manageable with our abilities.

After discussing the software and hardware aspects of the game, we figured that we could tackle this

project down nicely, with a well-planned implementation progress timeline. This game also satisfies our

desire to implement a fun, fast-paced, and colorful-looking game with a lot of functionalities that can be

applied from our past knowledge of SystemVerilog and Nios II, learned in ECE 385. Most important of

all, Feeding Frenzy was coincidentally one of our favorite games back when we were in middle school, so

we easily decided to work on it.

The original Feeding Frenzy game

2 Feeding Frenzy: Survival Mode

Feeding Frenzy was a popular underwater action game ten years back, written by Sprout Games and

published by Popcap Games. The objective of this game is quite simple: you are a small fry (we named

our main character Andy, like the original game) in an ocean full of big fish, and the only way to survive

is to eat the fish that are smaller than you in order to grow bigger and be able to challenge the bigger

predators such as sharks. The original Feeding Frenzy game has 40 levels, but we chose to work on a

survival mode instead, with increasing game difficulty as you grow bigger. The objective is to survive as

longest as possible without being eaten or killed by special characters.

Page 5: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

4

Doraemon Son Goku, from Dragon Ball

In our version of Feeding Frenzy, we decided to add two other special characters, Son Goku, the main

character from a very famous Japanese anime, Dragon Ball, and Doraemon, the blue earless cat with

countless magic items that every Asians probably know of. In this game, they will be the biggest enemies

that enter the game just to kill you, and you have to avoid these characters.

3 Description of Circuit Our circuit is composed of many separate files, called entities. These files contain the codes that define

how the wires are connected to each other via inputs or outputs. Each entity has a very different, specific

function. Hence, we will have a separate section on our report just to explain the functionality of each

entity. After building all the entities, they must be brought together to create the working game. This shall

be explained in the game logic section of the report.

As for the circuit we built, the game features the main fish character, which we call Andy, swimming

around a rectangular tank which is our computer screen. When Andy is present on the screen, he always

flickers his tail when he swims in any direction. Andy is also able to open his mouth to eat a character that

collides with his mouth.

The numbers of different characters are randomized, so are their movements. The game’s world screen is

set to be 800x800, so characters that swim outside this bound are considered dead (does not exist). The

movement of characters in this game is all animated. A total of 16x8 sprites are used in this game. Each

sprite has its own purpose. The sprite pixels are generated via the use of Python to convert PNG file into a

Hex File.

Additionally, the score in this game is measured by the time you survive in the game. As soon as the

game starts, the time (in decimal) is output to the HexDriver to be displayed on the FPGA. When you are

killed, the game resets and the time restarts with zero again. We also implemented a score bar on the

game screen via hardware code.

Page 6: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

5

4 Operation of Circuit

4.1 The Big Picture

State Diagram of the Connections

The game state and state transitions due to gamepad input will be computed from the software sidep and

sent to the hardware side for rendering. The information about where the characters are, the position of

the world camera, the zoom scale of the sprites, and the z-depth ordering of the sprites will be managed in

software. We update these states through multiple parallel IO channels, and once ready for rendering the

software asserts “render_flag” signal.

The hardware, on the other hand, parses the game state and renders sprites (stored as ROM) in correct

scale, direction, and order, and renders texts as necessary (the game state has a flag for this). Render here

simply means updating the game state buffer. To save resource, create a game state buffer, which has

much smaller memory footprint and don’t store the actual screen. The renderer can use that information to

figure out what to draw to each pixel. The VGA controller will put the colors onto the screen by writing

the corresponding pixel provided by the renderer every pixel clock. In the diagram above, some trivial

hardware pins are omitted for clarity.

Page 7: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

6

4.2 Hardware Entities

Entity: FeedingFrenzy.sv

This entity is the top-level entity that connects together the hardware components, which are GameState,

stopwatch, VGA_controller, Color_Mapper, HexDriver, and usb_system.

Entity: GameState.sv

This entity provides a (2^addr_size)x32-bit RAM buffer “mem” to write to by giving it in_addr and

in_data. The data is then copied over to an identical size RAM called “cache” whenever the signal

“render” is raised. The data is always written to “mem” and read from “cache”, so that data are only

available when they are ready for rendering. Handshaking the same way as done in Lab 9 is also used to

ensure data correctness.

Entity: stopwatch.sv

This entity provides a simple, naive approach to timing. Since we know the rough frequency of the

system clock to be 50 MHz, we can calculate the estimated time past by looking at the counter the counts

every rising edge of the clock. The stopwatch outputs the time count since last stopwatch reset, in

milliseconds.

Entity: VGA_controller.sv

This entity, borrowed from Lab 8, contains a state machine that controls VGA clock input and sync

pulses, and also outputs DrawX and DrawY for use in hardware rendering. This entity produces pixel

clock which is half the frequency of the system clock, then other sync pulse and DrawX, DrawY are

generated based on that pixel clock.

Entity: ColorMapper.sv

This entity contains the code for sprites/texts and outputs the color to draw on screen given the game

state, camera position, and draw position (DrawX, DrawY).

To create sprites in hardware, we can track the top left corner of each of the sprite we want to draw. We

can use statements like the color_mapper in lab 8, so for sprites:

"if the current pixel to draw is within text bounds, the draw text, else if it is within sprite_top_left

+ sprite_size_X/Y, then draw the sprite by using the retrieved sprite arrays; else draw the background".

● There are multiple sprites, so there are orders in which sprites are drawn. If many sprites are on at

the same time, then the circuit chooses the first in-queue.

● Create a sprite array that contains a list of sprites, then to use each sprite, we can select the

appropriate index.

● To select an appropriate index, we have to retrieve the color of the sprite to draw within one pixel

clock.

● Using full 32-bit color, the size of the sprite ROM will be very, very large. Instead, we used

python code, OpenCV library, and some knowledge of image processing to help reduce the

number of total colors used in each sprite. Then, we number each color. The ROM will store

these numbers instead i.e. we used histogram methods to create a palette of colors. An extra ROM

Page 8: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

7

called palette ROM will give the true color of the pixel, given the sprite number and palette

number as ROM address. (more on this in “Game Rendering” section)

Entity: HexDriver.sv

This entity converts a 4-bit number to a bit vector to be mapped on the hexadecimal display of FPGA. We

have been using this code since it was given for lab 6. In our implementation of Feeding Frenzy, the

HexDriver displays your score, based on the time survived in the game.

Entity: usb_system (Qsys-generated)

This entity is an extended version of Lab 8’s usb_system. The added ports are: .in_addr_export(in_addr) //input to GameState .in_data_export(in_data) //input to GameState

.hw_sig_export(hw_sig) //input to GameState

.sw_sig_export(sw_sig) //output from GameState .render_flag_export(render_flag) //input to GameState .timer_flag_export(timer_flag) //input to stopwatch .timer_port_export(timer_port), //output from stopwatch

.game_camera_export({CameraFlags,CameraY,CameraX}) //input to Color_Mapper

These ports can be used by the Nios II software program to control or inspect the corresponding entities

they are connected to.

4.3 Software Entities

main.c

Contains the modified Lab 8 software code for polling USB data. The polling loop is modified so

that the game gets updated in a non-blocking manner i.e. not affected by how long the program waits for

keyboard input. Particularly, game_update(ctrl) gets called repeatedly without game controller input to

keep evolving the game.

usb.c and usb.h

Contains enumeration methods for the USB interface (taken from Lab 8).

game.c and game.h

Contains game logic and constants. The main methods are game_init() and game_update(ctrl),

where ctrl is an 8-byte data packet retrieved from polling USB data (more on this in the communication

section).

Page 9: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

8

5 Game Logic (Software)

5.1 Sprite Attributes

World position, Instantaneous speed, and Target speed

Each sprite contains an information about its own world coordinate position (more on the coordinates in

the mechanics section), instantaneous speed, and target speed, as floating point numbers. The

instantaneous speed of the fish approaches the target speed at semi-exponential decay rate (see mechanics

section).

Sprite number, Animation number, and Animation time

For each of the sprites obtained from various sites, we first had to make sure they were all in a PNG

format, so that it is compatible with our Python code that translates the format of sprite pixels into a Hex

file to be used in hardware.

Page 10: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

9

All sprite pictures we edited in 128x128 format are shown ranging from Sprite Number 0 to 7:

Sprite #0: Main

character, Andy

Sprite #1: Small fish,

Pike

Sprite #2: Small fish,

Salmon

Sprite #3: Small fish,

Bass

Sprite #4: Jellyfish

Sprite #5: Shark &

Warning sign

Sprite #6: Doraemon

Sprite #7: Goku &

Spirut Ball

Each frame of an animation has its own associated time period, and the next animation to make transition

to. Whenever the animation time for the current animation exceeds the limit, the animation of the

character is changed to the next one (see “Animation Sequencing”).

Action and Cooldown

We defined 9 actions, each described in the following list. The action attributes are set to one of these

constants at any given time. The behavior of the characters will differ depending on these constraints.

1. ACTION_DEFAULT (0): does nothing. This is a default state where AIs may perform random

walk.

2. ACTION_ALERT (1): this action is activated whenever

3. ACTION_ENTRANCE (2): is activated every time a character enters or leaves the world’s screen.

When a character leaves the world’s screen, it is terminated and counts as a dead character.

4. ACTION_STUN (3): is set whenever the player is stunned by a stimuli, either by Doraemon’s

attack or by getting in contact with a jellyfish. When the player is stunned, Andy will be

paralyzed and will not be able to move as it wants for a few seconds.

5. ACTION_GOKU (4): is set whenever Goku enters the screen and starts disappearing and

reappearing. This was done by randomizing the various positions that Goku can appear on the

screen.

6. ACTION_GOKU_THROW (5): when set, Goku is positioned at the top center area and throws a

spirit bomb at the player.

7. ACTION_DORA (6): when set, Doraemon will enter the screen and starts animating.

Page 11: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

10

8. ACTION_DORA_SHOOT (7): when set, Doraemon is positioned on the same y-axis value as the

player and shoots at the player to miniaturize him/her.

9. ACTION_SHARK_WARNING (8): is set whenever a shark is about to enter the screen. The

warning sign will blink at the position that the shark will enter.

Cooldown is defined as the time it takes for an action’s effects to slowly disappear. The cooldown is

assigned as soon as a character gains a new action. Only when the cooldown expires that the character

will be able to perform a new action (which could be the same action as previously performing).

Growth, Size, Big, Present, and Alive

The growth and size attributes keep track of the player’s progress on eating smaller fish. Eating bigger

fish gives you more growth points, and if growth exceeds a certain threshold or fall down under some

threshold, size and big are adjusted accordingly. Intuitively, the player will be able to eat fish that have

smaller or up to the same size. Conversely, contact with bigger size fish results in being eaten. size is set

to -1 for inedible characters, that cannot eat us as well.

growth player’s size big

0-4 1 0

5-24 3 0

25+ 5 1

when a character is not present the hardware rendered does not render the sprite. This is different from

alive. If a character is not alive, it will stop interacting with any other characters, and when the event

generator generates a new character, the memory space used for that character can be reused.

Relative anchor, Child node, and Relative positioning

Sometimes a spritesheet for a character cannot be contained in a single 32x32 frame. For example, the

shark sprite’s shape is not square, requiring three frames each. When relative anchor is set, the sprite

piece ignores all mechanics and physics of the game and gets anchored to the parent sprite. In this case,

the body part and the tail part is always anchored to the head, with offsets offX, offY relative to the

parent. If the parent is flipped, these offsets are also reversed by multiplying -1. Also, since the frames for

a non-32x32 sprite are in three consecutive 32x32 frames (see shark), the animation number of these

anchored sprites are offset from the animation number of its parent. Whenever a parent sprite is removed

from the screen, all the anchored children are also removed in the same way.

Page 12: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

11

5.2 Mechanics

World, Screen, Camera and Game status

For this project, we have two reference frames: world coordinate and screen coordinate. As shown below,

our game world is 800x800 px in size, and the screen is 640x480 px wide. The top left corner (0,0) of the

screen correspond to (cameraX,cameraY) in world coordinate. We denote world coordinates using

underlines and screen coordinate using italics.

Movement physics

For every time dt that passes between each game update, the next world position is generally calculated

using

where ACCEL is the easing factor, currently 3.0, denotes the rate at which the instantaneous speed vx

approaches target speed VX. This cause the characters to move smoothly and simulates

acceleration/deceleration under water resistance. Special characters or characters under certain

actions may bypass this rule and use its own physics.

As for controlling the main character, use either the analog stick or the directional buttons of the

gamepad to move around, press START button to pause and resume, button 1 to restart, and

button 2 to use booster. The main character’s target speed will be adjusted according to these

controls, unless it is in ACTION_STUN.

Animation sequencing

Each 32x32 frame in a character’s spritesheet are numbered 0-15. Given sprite number and animation

number, the hardware renderer can find out what to draw for that character. That said, the software keeps

track of each sprite’s animation time and defines the expected time spent on each animation. animTime is

increased by 1 per 1 ms. Once the animation time exceeds the allowed time on the current animation

Page 13: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

12

number anim, it changes anim to the next animation number and decreases animTime by the allowed

time.

int anim_transition[16][16][2] = {

{//MAIN

{1,192},{2,192},{3,192},{4,192},

{5,192},{6,192},{7,192},{8,192},

{9,192},{0,192},{11,32},{12,32},

{13,32},{0,32},{14,192} ,{14,192}

}, … } … spr->animTime += (int) (dt*1000); … while(spr->animTime >= anim_transition[spr->number][spr->anim][1]){

spr->animTime -= anim_transition[spr->number][spr->anim][1];

spr->anim = anim_transition[spr->number][spr->anim][0];

}

A portion of related code reproduced above illustrates how this is done. The transition mapping is in the

form of {next_anim_number, frame_duration}.The main fish would swim as expected, spending

approximately 192 ms per frame looping through 0-9. When we want to change the sequence so that the

fish eats, for example, it is done so by setting anim to frame 10, which the fish will go through the eat

animation and jumps back to the frames 0-9 once it is done with frame 13.

List of actions and action sequencing

1. ACTION_DEFAULT : while in this action, the target speed is randomly set. By default, other

actions change to this one when it expires.

2. ACTION_ALERT : while in this action, the target speed is set so that the character moves

towards/away from the player.

3. ACTION_ENTRANCE : while in this action, any movement occurred will not be bounded within

the world boundary. Instead, the character is removed from the game once it leaves the world

boundary.

4. ACTION_STUN : while in this action, physics is not bypassed but the control over the character

is. The character will have its target velocity swing in small sinusoidal amplitude around 0. Once

ACTION_STUN expires, the player regains control of the speed through the gamepad.

5. ACTION_GOKU : while in this action, the target speed is set to 0 on both directions, and, in

intervals, the position of the character will be randomly set relative to the screen coordinate. This

makes Goku blink here and there on the screen. When ACTION_GOKU expires,

ACTION_GOKU_THROW takes place

6. ACTION_GOKU_THROW :while in this action, the physics is bypassed and Goku appears in the

middle top region of the screen (not the world). The animation sequence is also set to where

Goku is throwing his spirit ball. When ACTION_GOKU_THROW expires, the animation is set

to start from that spirit ball frame, the speed and target speed is then set to point directly to the

player at maximum speed, then ACTION_ENTRANCE with a very long cooldown takes place

until it leaves the world boundary.

Page 14: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

13

7. ACTION_DORA : while in this action, Doraemon stays on either left or right side of the screen

and moves vertically, attempting to align his horizontal position with the player. When this

expires, ACTION_DORA_SHOOT takes place.

8. ACTION_DORA_SHOOT : while in this action, the animation is set to the portion which

Doraemon shoots. When this expires, the animation is set to be the scissor bullet, the speed is set

to twice the maximum speed, headed towards the player, then ACTION_ENTRANCE with a

very long cooldown takes place until it leaves the world boundary.

9. ACTION_SHARK_WARNING : while in this action, the sprite changes to the warning sign and

sticks to the side of the screen which the shark appears. When this expires, the animation is

changed back to the regular shark and the position of the shark is set to the border of the world

with max speed entering the world. Then ACTION_ENTRANCE with a very long cooldown

occurs.

Collision detection (eating/eaten/spawn/kill)

When a collision occurs, different behaviors are assigned to both the player and the character collided:

● Eating

○ when a player eats a character, the sprite animation is reset, starting over from the sprite

animation where the eating action (open mouth/close mouth) takes place. This collision

● Eaten

○ the player will no more be alive, and it is killed.

● Spawn

○ this occurs periodically through the event generator. If there are enough space for

introducing new characters into the world, an available sprite slot is allocated for the new

sprite. If the created sprite needs child/anchored sprites, more slots are allocated. If the

spawning algorithm cannot allocate enough slots, then it frees all the in-progress slots and

abort spawning. The spawning algorithm also assigns an appropriate action for the

generated character.

● Kill

○ when the player is killed, the game is over and all the movements stop.

○ when a character is killed by the player, it disappears (player->present=0).

5.3 AI behavior

Event generation and game difficulty

game_add_event function in our game.c controls the event generation.

There are four sets of fish population distributions corresponding to the game difficulty: easy, normal,

hard, and extremely hard. We created four different hash arrays filled with unequal amounts of sprite

numbers. Upon determining which sprite to spawn, a randomly generated number is used as an index to

this hash, which the content specifies the character to spawn. The logic of this is as the difficulty of the

game increases (measured by the number of fish consumed by the player), the chance of encountering

tough enemies is higher. For example, rand_hash_easy would create more pikes on the screen so that it is

easier for the player to survive and grow bigger, while rand_hash_extreme would generate a lot of sharks,

Doraemons, and Gokus.

Page 15: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

14

Moreover, the size threshold is defined in game.h to set the number of characters to be eaten by Andy in

order for Andy to grow a size bigger. MEDIUM_SIZE_THRESHOLD is set to be 5, while

BIG_SIZE_THRESHOLD is 25, meaning that Andy would have to eat 5 characters in order to grow into

a medium size and 25 to grow into a big size.

To relate to the difficulty level, a specific threshold for each difficulty level is also defined in game.h. For

example, extreme level is defined to have three times as big as the hard level’s threshold, which was set

as BIG_SIZE_THRESHOLD.

Random movement

Random movement is assigned to a character when its size is equal to Andy or when it does not detect

Andy nearby. In that case, neither Andy nor the character is eaten. We then programmed the character to

perform a random swim by assigning thing->VX and thing->VY to random speed and direction, bounded

by the height and width of the world’s screen. We also set the character’s action to be

ACTION_ENTRANCE afterwards so that the speed retains for a while, and allows the character to exit

the screen. Every time ACTION_ENTRANCE expires, random movement assignment is retriggered

again unless Predator mode or Prey mode takes priority.

Predator mode movement

This happens during ACTION_ALERT. Movement of predators (which are the any characters bigger than

Andy that are able to eat him) is programmed to move closer to the player whenever they are close by or

in the circular area that detects the other character (the action cooldown of the character while in

ACTION_ENTRANCE must already expire in order to trigger this, which overrides random movement).

This is done by first comparing the size of the player and the character Andy is moving close by. If the

size of the character is bigger than Andy, the character is detected as a predator. We then normalize the

distance from the predator to the player, and program the predator to move towards the player in that

direction by setting its x and y velocities (thing->vx and thing->vy).

Prey mode movement

This happens during ACTION_ALERT. Movement of preys are the reverse of predators, that is it moves

away from Andy when Andy is close by. The velocities from the predator mode are simply multiplied by

-1 to move the character away.

Heuristics for special characters

As for shark, Goku, and Doraemon, their behaviors are simply a scripted event. One of the more “AI”

aspects of the special characters are that of aiming hazards towards the player. The velocities and

directions of the objects used to attack the player are set to be directed to the player.

Page 16: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

15

5.4 Communication With Hardware

Sending the game state

The game state is simply a 32-bit addressable memory with 32 memory locations accessed using a 5-bit

address. To set the data at address stored in in_addr to the data stored in in_data, follow the below

handshaking procedure from gsset(attr,val):

*in_addr = attr;

*in_data = gamestate[attr] = val;

*hw_sig = 1;

while(*sw_sig!=1);

*hw_sig = 0;

while(*sw_sig!=0);

where attr is the address of the memory, or so we call an attribute number, because each address of the

game state stores an attribute of the game. In this case, the position and the status of the sprites. For

example, attr is 0 for the 0-th sprite. The format of the data val to store is as shown below:

val[9:0] the x position of the sprite, converted to integer

val[19:10] the y position of the sprite, converted to integer

val[23:20] the animation number anim of the sprite

val[27:24] the number of the sprite

val[28] big flag

val[29] flipY flag

val[30] flipX flag

val[31] present flag

Also, there are PIOs to the stopwatch and the game_camera. No handshaking is necessary for this one.

The format is as shown below:

*timer_flag set to 1 when restarting the timer

*timer_port read from this to access time in milliseconds since last reset of stopwatch

game_camera[9:0] cameraX of the camera

game_camera[19:10] cameraY of the camera

game_camera[20] set to 1 to indicate game over

Page 17: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

16

Polling gamepad input

Donop USB wired Dual Shock

Using pyusb library first on laptop, we were able to find out the response packet format of the gamepad

we are using. The gamepad has an 8-byte response packet which reflects the state of the gamepad

byte data

0 x-axis data. when analog off, 0 means left, 127 means center, 255 means right. Changes from both

directional buttons and left analog stick. when analog on, the directional buttons no longer affect this byte, and the value for center

becomes 128.

1 y-axis data. when analog off, 0 means up, 127 means center, 255 means down. Changes from both

directional buttons and left analog stick. when analog on, the directional buttons no longer affect this byte, and the value for center

becomes 128.

2 z-axis data with the same format as byte 0, but meant for the right analog stick and right

side buttons

3 w-axis data with the same format as byte 1, but meant for the right analog stick and right

side buttons

4 not used

5 bit 7-4 contains the active-high flags for buttons 4, 3, 2, and 1, respectively. bit 3-0 contains the directional press on directional buttons (unaffected by analog stick). UP

is 0, and going clockwise 45 degrees each time, UPLEFT is 7.

6 stores active high flags of the other buttons, respectively from bit 7 to 0: |JOYR | JOYL | START | SELECT | R2 | L2 | R1 | L1 | (JOYR/JOYL are when you press down the analog stick)

7 0xC0 when analog control is off, 0x40 when analog control is on

Page 18: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

17

6 Game Rendering (Hardware)

6.1 Camera and Background

The function set_camera(x,y) in game.c assigns two variables, cameraX and cameraY, which sets the

bounds of the camera screen so that the minimum is 64 and the maximum cannot exceed the world’s

screen subtract screen’s width/height. If the position of the camera’s screen is larger than those values,

they must be limited to x or y values. The reason why we assign the camera’s screen to be 64 is because

when random characters are generated, they usually enter the world’s screen from the edge. Since we set

the camera’s attributes to be inside the world’s screen, the player will not be able to witness the creation

of random characters.

Background color is assigned in Color_Mapper.sv function, following how the background color was set

in Lab 8. We displayed the background color to be ocean blue, and the deeper you swim into, the darker

the water color gets. This was done by adjusting the background color relative to DrawY and CameraY

components.

6.2 Retrieving Sprite Pixels

Preprocessing with Python and OpenCV

Python is used to convert image from PNG/JPG format into c/sv/hex array using cv2 library.

Example of Python code: # given an BGRA image "img" and color limit "n", outputs # the tolerance-filtered histogram of the n most occurring color tones # i.e. reduces to number of colors in an image to n # return value is a list of tuples in the form if ((b,g,r,a),frequency). def color_histogram(img,n): hist = {} for row in img: for col in row: b,g,r,a = tuple(col) b,g,r = (b>>4)<<4, (g>>4)<<4,(r>>4)<<4 pix = b,g,r,a if a <= 127: pix = 0,0,0,0 if pix in hist.keys(): hist[pix] = hist[pix] + 1 else: hist[pix] = 1 # sort the histogram of colors in descending order of frequency hOrd = list(reversed(sorted(hist.items(),key=lambda a: a[1]))) #prepare return value res = [] num_count = 0 while len(hOrd) > 0 and num_count < n: # pop out the color with highest frequency cur, curamt = hOrd[0] cur = np.array(cur,dtype=int)

Page 19: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

18

hOrd.remove(hOrd[0]) close_color = False # check if there is any other color that is "close" to current color for color in res: color = np.array(color,dtype=int) # color tolerance. Close colors count as the same color if npla.norm(color[:3]-cur[:3]) < 0x20: close_color = True if close_color: break # if current color is not close to any of the previously added colors, # add it to the result if not close_color: res.append(cur) num_count = num_count + 1 return res

# extended version of histogram(). After finding reduced colors using the fn above, # it replaces each pixel of the original image with the closest color in the palette def reduce_palette(img,n): h,w = img.shape[:2] tHist = color_histogram(img,n) for i in xrange(h): for j in xrange(w): b,g,r,a = img[i,j] nearestPix = (0,0,0,0) if a > 127: nearestPix = min(tHist,key=lambda x: npla.norm(x[:3]-img[i,j][:3])) img[i,j,:]= np.array(nearestPix) return img

# same as above, but the return value replaces BGRA value with a 4-bit palette index # the second return value are the indexed colors the palette index stored # in the first return value is an index into the second return value. # The 32-bit GBRA color can be found using that index and the histogram. def reduce_palette_indexed(img,n): h,w = img.shape[:2] tHist = color_histogram(img,n) res = np.zeros((h,w)); for i in xrange(h): for j in xrange(w): b,g,r,a = img[i,j] nearestPalette = 0 if a > 127: nearestPalette = min(range(n),key=lambda k: npla.norm(tHist[k][:3]-

img[i,j][:3])) res[i,j]= nearestPalette return res,tHist

# Reduces and partitions every image into frameWidthxframeHeight chunks with 16-color limit. # writes .hex color file to outputFile. # outputs .txt (SystemVerilog syntax) array for the palette to histFile

Page 20: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

19

# outputs .txt mask file for the sprites in maskFile. def hex_sprite_packAll(filenames,frameWidth,frameHeight,outputFile,histFile,maskFile): addr = 0 hists = [] files_masks = [] with open(outputFile,'w') as f: for filename in filenames: masks = [] img = cv2.imread(filename,flags=-1) img,hist = reduce_palette_indexed(img,16) hists.append(hist) h,w = img.shape hsteps = h/frameHeight wsteps = w/frameWidth for i in xrange(hsteps): for j in xrange(wsteps): for distY in xrange(frameHeight): mask = 0 for distXSteps in xrange(frameWidth/8): adrval = 0 for distXOffset in xrange(8): pix = img[i*frameHeight + distY,j*frameWidth+distXSteps*8+

distXOffset] adrval = (adrval << 4) + int(pix) if int(hist[int(pix)][3])==0: mask = mask*2 else: mask = mask*2 + 1 hexsum = 4 + (addr%256) + ((addr/256)%256) + (adrval%256) +

((adrval/256)%256) +\ ((adrval/(256**2))%256) + ((adrval/(256**3))%256) hexsum = hexsum % 256 checksum = (256-hexsum)%256 f.write(':04%04X00%08X%02X\n'%(addr,adrval,checksum)) addr = addr + 1 masks.append(mask) files_masks.append(masks) f.write(':00000001FF\n') with open(histFile,'w') as f: addr = 0 f.write('{') first = True for hist in hists: for b,g,r,a in hist: if first: f.write("32'h%02X%02X%02X%02X"%(r,g,b,a)) first=False else: f.write(",32'h%02X%02X%02X%02X"%(r,g,b,a)) addr = addr+1 while addr % 16 !=0: f.write(",32'h00000000") addr = addr+1

Page 21: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

20

f.write('};') with open(maskFile,'w') as f: for masks in files_masks: f.write('{') first = True for m in masks: if first: f.write("32'h%08X"%m) first = False else: f.write(",32'h%08X"%m) f.write('};\n') print 'done' hex_sprite_packAll(['sprites/mainfish_frames.png'],32,32, 'sprites/fishpack.hex','sprites/fishpalette.txt' ,'sprites/fishmask.txt')

In summary, the code above reduces the number of colors in each sprite to 16 colors and assign

numbers to them. Then, each 32-bit color in each image is replaced with a 4-bit number

representing the palette number for that color. Then, it outputs .hex file for the sprites, plus a

SystemVerilog format arrays of bit mask and mappings of pallete number to the original 32-bit

color.

Depth sorting and calculating effective sprite address

As the PNG to hex converter above shows, we format effective sprite address this way:

effectiveIndex = {1'b0,tSpriteNumber,tAnimNumber,tEffY,tEffX};

where tSpriteNumber, tAnimNumber, tEffY, tEffX are the sprite number, animation number, and

effective vertical and horizontal position of the pixel to be drawn, relative to the target sprite frame’s top

left corner. The target sprite is calculated by using a mask array for lookahead purposes. Among the

sprites that are present, the target sprite is the first-in-index sprite whose boundary covers the point to be

drawn, and the mask for its corresponding pixel is not zero. This visually makes one sprite to appear on

top of the other when their boundaries conflict. If all masks for all sprites end up being zero, a

background color is chosen. Note, also, that the scoreboard and game over text described in the next

section takes priority over these. effectiveIndex[18:3] is used as an address to the 32-bit addressable sprite

ROM, and effectiveIndex[2:0] is used to select a 4-bit paletteNumber output from the 32-bit ROM output.

Then finally, fishpaletteROM[tSpriteNumber[2:0]][paletteNumber] will be the expression that selects the

32-bit RGBA value of the color to be drawn. Note that if the sprite is big, then it’s boundary will extend

by two folds, and if it is flipX’ed or flipY’ed, the effective offsets effX and effY are inverted accordingly.

Page 22: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

21

6.3 Scoreboard and Conditional Prompt

Since the scoreboard does not need much optimization space-wise, no palette is needed and the

scoreboard is implemented in a very simple way, using 10x5x5 digit fonts. The score is made to appear in

the top left corner when playing and at the center of the screen when the game is over. There is also a

game over text which only appears at the center of the screen when the condition is met.

After generating the scoreboard and the game over prompt, we make this take priority over the sprite

color output, so the characters will be drawn behind the text of their boundaries are in conflict with the

text.

Score is displayed on both the hex display and the screen.

Page 23: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

22

6.4 True Output Color

In our context, the meaning of ‘true color’ is the color that is actually shown on the screen. In order to

display a color on the screen, we have to go through several conditional checks:

● Check whether there is a text at that position

○ If there is, true color is white, because the text is white, else check the next condition

● Check whether there is a character displayed at that position

○ If there is, display the first character that swims into that position, so that the first

character at that position will be displayed on top of everyone else

● If there are no characters generated at the position, draw background color, which is a gradient

with respect to DrawY and CameraY.

7 Running the Program

In order to run Feeding Frenzy, here are the steps we take to setup the game:

1. Open our project with Quartus II and compile the latest version of the code.

2. Load the FPGA with the project’s .sof file. If you connect FPGA to VGA monitor now you

should see a blue-toned gradient screen with 0000 on the top left corner. The game cannot start

until the software project is run.

3. Open the software project through Eclipse. If include error occur, clean all projects (also uncheck

“automatically rebuild”), then right click on bsp project > indexing > search for unresolved

includes, then followed by the rebuild option on both software and bsp projects.

4. Generate BSP, enter BSP editor, press generate, then generate BSP again.

5. Build all projects

6. Go to run configurations, add new configuration for the project, make sure the .elf file is found,

then press Run.

7. Connect the FPGA to a USB gamepad and a VGA monitor, if haven’t done so, in order to be able

to play. The preferred gamepad is the one specified in an earlier section, as different gamepads

may have different protocols and our parser may not recognize button presses of other gamepad

models.

8 Simulations

To test the GameState Module and stopwatch, we created a separate Quartus project for the purpose of

testing small units without having to recompile sprite related files. Using the conventions described

earlier, the testbench attempts to set entry 0 of game state to 0xf000000f, 1 to 920000ff, and 2 to

7678000, and then sends hw_sig to the component, then asserts render flag after each of them completes.

At the end, the testbench checks the values of all outputs by addressing 0, 1, and 2 respectively, which

appears to be correct. The simulation is then run for a longer time to examine whether the counter has

incremented after 1000 milliseconds.

Page 24: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

23

Simulation of the hardware/software handshaking protocol

Page 25: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

24

Simulation of stopwatch

9 Borrowed Work

● Keyboard handler code from ECE 385 Lab 8

● Original sprites:

○ Andy sprite from Insaniquarium game:

http://imageshack.com/f/11/carnivoresheetfm5.png

○ Fish sprite by PixelJoint: http://www.pixeljoint.com/files/icons/full/fishes.png

○ Shark sprite: http://3.bp.blogspot.com/-

upqnBP1OFco/Ur3CJhMm67I/AAAAAAAABO0/5bRTaP4IFfQ/s1600/shark2.png

○ Goku fanmade sprite from Deviantart Website:

http://fc01.deviantart.net/fs70/i/2012/190/3/b/son_goku_extra_sprites_by_dragonsoul200

4-d56kz1q.png

○ Doraemon sprite from Spriters-resource: http://www.spriters-

resource.com/genesis_32x_scd/doraemonyd7nng/sheet/36197

○ Jellyfish sprite, also from Spriters-resource: http://www.spriters-

resource.com/game_boy_advance/spongebobsquarepantssupersponge/sheet/26008/

○ Warning Sign sprite from clker.com cliparts:

http://www.clker.com/cliparts/5/6/f/3/11971252291061148562zeimusu_Warning_notific

ation.svg.med.png

● Game idea: original game written by Sprout Games and published by Popcap Games

Page 26: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

25

10 Timing Report

Our project was completed with the following statistics according to Quartus II.

Fitter Resource Usage Summary

Page 27: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

26

PowerPlay Power Analyzer

TimeQuest Timing Analyzer Summary

11 Block Diagrams

The block diagram for the project appears to be very large that it cannot be shown effectively in

this report. However, a block diagram for the overall system, as seen in “The Big Picture”

section, adequately describes the interactions between each component. Here the blocks are

identical to the previous figure, but more signal names are added. It is impossible to include all

the relevant connection names here, as they are already defined in earlier sections, and where

they are being used should be straightforward.

Overall System (top-level entity + communication + software)

Page 28: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

27

block diagram for usb_system generated by Qsys

Page 29: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

28

12 State Machines

State Machine for GameState Entity. Not shown here is the response to the render signal, which all the

data stored in game state memory are copied over to game state cache, which is asynchronous with

respect to the state machine, but synchronous with the system clock.

State Machine for stopwatch

Mock-Up State Machine for the top level software code

Mock-Up State Machine for Sprite Actions in Software

Page 30: Feeding Frenzy using SoC and SystemVerilog · ECE 385 Spring 2015 Final Project Feeding Frenzy using SoC and SystemVerilog Pichamon Meteveravong, Perut Boribalburephan …

29

13 Conclusions

Our project turned out really well. We were able to implement all of the fundamental features that we

wanted to include in our version of Feeding Frenzy. Starting from the ball entity we had in Lab 8, we

turned the functions of the ball into that of a fish and replaced it with a sprite. We continued adding more

and more features, starting from simple things like increasing the number of sprites available to harder

tasks such as implementing AI for the enemies such as the shark, Goku, and Doraemon. We are satisfied

with our design choice, where the game logic and state machine models are on software side, separate

from the rendering logic. At the cost of not being able to process as many entities at once compared to

hardware, we significantly sped up the development cycle.

There were a couple things we wanted to include but did not have enough time, or even space. One thing

we would like to point out was the background for the game that has pictures of coral and aquatic plants.

We did not include the background because we did not have enough memory in the ROM, and also, the

process of adding the background is the same as adding sprites anyway so we are not missing out an extra

functionality we could have added on. As a note, we could have used the SDRAM for the game assets so

that there is no problem with memory limitations, but the control for routing the right color to the screen

would be even more complex due to SRAM being shared with the software program. Another example

was adding audio with sound driver for game theme music. We did not have enough time to implement

this feature.

Overall, we both are satisfied with our project. Other than fundamental features that we planned to do, we

also did extra features that we were not planning to do (animations, score bar, variety of AI, Goku’s

special ability to disappear/reappear as he tries to attack, etc). We learned a great deal about the design

flow and difficulties of working with a large SystemVerilog project, especially dealing with the long

compile times (we optimized the 1. 5+ hour compilation down to about 24 minutes!) After putting a lot of

effort into our final project, we have prepared ourselves very well for future FPGA design usage.