Add local HLS dev setup: clangd, pragma highlighting + linting, local C-sim
This commit is contained in:
118
Practicals/practical-3b/game_of_life_practical3b/gameoflife.cpp
Normal file
118
Practicals/practical-3b/game_of_life_practical3b/gameoflife.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "gameoflife.h"
|
||||
|
||||
#include <ap_int.h>
|
||||
|
||||
void game_of_life_step(const uint32 in_words[4], uint32 out_words[4]) {
|
||||
#pragma HLS ARRAY_PARTITION variable=in_words dim=1 type=complete
|
||||
#pragma HLS ARRAY_PARTITION variable=out_words dim=1 type=complete
|
||||
|
||||
#pragma HLS INTERFACE s_axilite port = in_words bundle = AXILiteS
|
||||
#pragma HLS INTERFACE s_axilite port = out_words bundle = AXILiteS
|
||||
#pragma HLS INTERFACE s_axilite port = return bundle = AXILiteS
|
||||
|
||||
// Represent the grid as 100 1 bit values in the fabric.
|
||||
ap_uint<1> current_grid[10][10];
|
||||
#pragma HLS ARRAY_PARTITION variable=current_grid dim=0 type=complete
|
||||
|
||||
// Create another grid of the same size to allow the values to be read and written without interfering.
|
||||
ap_uint<1> next_grid[10][10];
|
||||
#pragma HLS ARRAY_PARTITION variable=next_grid dim=0 type=complete
|
||||
|
||||
// We get our values in 4 uint32 integers (4 * 32 = 128) which nicely pack the 100 required values into 4.
|
||||
// This allows quick movement from the CPU to the FPGA through the AXI bus.
|
||||
// However, since we now have 4 uint32 values we need to convert or transform these values into something usable in our case.
|
||||
// For us, we want them in our current_grid as singular 1 bit values.
|
||||
// Hence, we need to "unpack" the uint32s into our grid.
|
||||
unpack_loop:
|
||||
for (int i = 0; i < 100; i++) {
|
||||
#pragma HLS UNROLL
|
||||
int word_index = i / 32;
|
||||
int bit_index = i % 32;
|
||||
|
||||
int row = i / 10;
|
||||
int col = i % 10;
|
||||
current_grid[row][col] = (in_words[word_index] >> bit_index) & 1;
|
||||
}
|
||||
|
||||
// Now that we have our grid stored as 100 different singular bits in our FPGA fabric we can do our comparison
|
||||
row_loop:
|
||||
for (int row = 0; row < 10; row++) {
|
||||
#pragma HLS UNROLL
|
||||
|
||||
column_loop:
|
||||
for (int col = 0; col < 10; col++) {
|
||||
#pragma HLS UNROLL
|
||||
|
||||
// Sum our 8 neighbours (log_2(8) = 3 so we could almost use a 3 bit value, but then we couldn't have 0 neighbours)
|
||||
ap_uint<4> alive_neighbours = 0;
|
||||
|
||||
delta_row_loop:
|
||||
for (int delta_row = -1; delta_row <= 1; delta_row++) {
|
||||
#pragma HLS UNROLL
|
||||
|
||||
delta_col_loop:
|
||||
for (int delta_col = -1; delta_col <= 1; delta_col++) {
|
||||
#pragma HLS UNROLL
|
||||
|
||||
if ((delta_row == 0) && (delta_col == 0)) {
|
||||
continue; // There is no need to check the centre cell, we only care about neighbours
|
||||
}
|
||||
|
||||
// We add +10 here before we % by 10. This is to prevent negative values from coming in.
|
||||
// Example: row = 0, delta_row = -1, then we would get 0 + -1 = -1, -1 % 10 = -1.
|
||||
// Example: this value does not exist. However, if we add 10 on, then we get,
|
||||
// Example: -1 + 10 = 9, 9 % 10 = 9. Which is valid.
|
||||
// Adding +10 allows us to get wrapping around the grid.
|
||||
int neighbour_row = (row + delta_row + 10) % 10;
|
||||
int neighbour_col = (col + delta_col + 10) % 10;
|
||||
|
||||
alive_neighbours += current_grid[neighbour_row][neighbour_col];
|
||||
}
|
||||
}
|
||||
|
||||
bool is_alive = current_grid[row][col];
|
||||
bool is_alive_after_step = (alive_neighbours == 3) || (is_alive && alive_neighbours == 2); // Apply Conway's Game Of Life Rules
|
||||
next_grid[row][col] = is_alive_after_step;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have computed the entire step for all cells, we need to "repack" our bits into our 4 uint32 values.
|
||||
ap_uint<32> packed_words[4];
|
||||
#pragma HLS ARRAY_PARTITION variable=packed_words dim=1 type=complete
|
||||
|
||||
// Put our out words to all 0s.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
#pragma HLS UNROLL
|
||||
packed_words[i] = 0;
|
||||
}
|
||||
|
||||
pack_direct:
|
||||
for (int i = 0; i < 100; i++) {
|
||||
#pragma HLS UNROLL
|
||||
int word_index = i / 32;
|
||||
int bit_index = i % 32;
|
||||
int row = i / 10;
|
||||
int column = i % 10;
|
||||
|
||||
packed_words[word_index][bit_index] = next_grid[row][column];
|
||||
}
|
||||
|
||||
for (int word = 0; word < 4; word++) {
|
||||
#pragma HLS UNROLL
|
||||
out_words[word] = packed_words[word];
|
||||
}
|
||||
|
||||
// I did have this, but it took 11 ish cycles. Slow because each OR operation depended on the previous one being complete.
|
||||
// pack_loop:
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
// #pragma HLS UNROLL
|
||||
// int word_index = i / 32;
|
||||
// int bit_index = i % 32;
|
||||
|
||||
// int row = i / 10;
|
||||
// int col = i % 10;
|
||||
// if (next_grid[row][col]) {
|
||||
// out_words[word_index] |= (1U << bit_index);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user