This is an FPGA based 24x24 NeoPixel LED panel control design project to implement the snake game using Sundance VCS-3 FPGA board.
Firmware design
Create a new RTL project (Vivado 2024.2), do not specify sources at this time, target the Sundance VCS-3 board
Once created, in the sources window, click ‘+’ to add design sources. Browse to your IP download path and select pixdrive.vhd (Credit to Adam Taylor for the VHDL code, GitHub linked).
Click ‘Create Block Design’ in the Flow Navigator, once created, right click the new design source in the sources window and select ‘Add module to block design’
Add Zynq UltraScale+ MPSoC and run block automation
Double click the ZYNQ IP block, click ‘Clock Configuration’, ‘Output Clocks’ ‘Low Power Domain Clocks’, ‘PL Fabric Clocks’ and make the following changes: deselect PL2, change PL1 to IOPLL source and frequency to 20MHz. This will drive the neopixel IP block.
Now, add the Block Memory Generator and AXI BRAM Controller using the ‘+’ button in the block diagram toolbar.
Double click the Block Memory Generator and select ‘True Dual Port RAM’, and do the same for the AXI BRAM Controlled except change number of BRAM interfaces from 2 to 1, since we are only controlling BRAM port A with AXI control, port B will read and write to the neopixel IP.
Select the ‘Board’ panel in the sources window and double click ‘aux gpio on connector j8’, click OK to add the AXI GPIO block to the design
Add an ILA IP block, double click and select ‘native’ and change the number of probes to 7, with this configuration:
Now select ‘Run Connection Automation’
Right click the ‘clk’ port on the neo_pixel_0 IP and ‘disconnect pin’ -> we want to connect pl_clk1 (20MHz) to this port, so click and drag from pl_clk1 to enter connection mode, releasing the mouse over the ‘clk’ port of the neo_pixel_0 block. Do the same for BRAM_PORTB ‘clkb’.
Connect the corresponding ports from BRAM_PORTB and the neo_pixel_0 block, right click ‘dout’ and ‘make external’.
Connect the probes of the ILA to dout, rstb, enb, web, addrb, dinb and doutb signals. (It is important to make sure that the ILA is clocked at 100MHz – the design fails if not, which is peculiar).
Regenerate layout and your block design should look like this:
Add a new constraints file by clicking the ‘+’ in the sources window, ‘add or create constraints’, ‘create file’, create a file called constraints and add this to it (pin B10 corresponds to pin 2 on the J8 output port):
Press F6 to validate the design, then right click the block design in the sources window and select ‘create HDL wrapper’, ‘let Vivado manage and auto update’.
Once this is done, we can run Synthesis and Implementation (Clicking ‘Generate Bitstream’ does everything we need).
Once the bitstream is generated (this may take up to 30 minutes), export the hardware from File > Export > Export Hardware, make sure to ‘Include Bitstream’ in the export. This will provide us with an.xsa file to build our Vitis platform on.
Software design
In Vitis 2024.2, Set Workspace to a new folder within your project folder (but a different folder to your ‘Firmware’ folder where your Vivado project is).
Create a new platform component, choose your.xsa file we just exported from Vivado as your Hardware Design for Implementation, next, next and finish to create your platform.
Now create an application component, targeting the platform you just created, next until finish.
Once created, in the Vitis Explorer window, click app_component > Settings > UserConfig.cmake > Directories and ‘Add Item’ to include bsp files, browsing to./platform/psu_cortexa53_0/standalone_psu_cortexa53_0/bsp/include.
Create a new source code file under Sources > src called snakegame.c
Rules:
- The snake moves at constant speed in one direction; input interrupts can change direction (max 90 degrees in one turn).
- The objective is to eat as many food pixels as possible; each food causes the snake to grow one pixel onto the end of its tail.
- The game ends if the snake collides with the edge walls or its own body.
Implementation
- I have written a build_led_map function that creates a 2x2 lookup table, knowing how the LEDs are wired.
- Snake moves in 4 cardinal directions depending on which of 4 input buttons is pressed – the rest of the body follows the head turn.
- Collision detection must occur every game tick.
- Food collision needs to be checked every tick, if food detected at head, grow and spawn another food.
- Boundary limits set by panel size, 24x24
- In a single game tick, snake moves forward one pixel in the current direction – frame by frame motion animation.
You need to build the platform and application so that the required bsp files are included in the directory we added, once built, delete a character in the first line that shows error and replace it and it should sort itself out.
Comments