#include "gameoflife.h" #include 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); // } // } }