aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2022-05-31 16:13:38 +0200
committerRenze Nicolai <renze@rnplus.nl>2022-05-31 22:27:22 +0200
commit68da02427cfec13692578414ce616182f1afccfc (patch)
treedf805c7df5b8f29d097069e59b8e6b83f8cf5ecb /main
parenta1ae09acdd92f2d1a519bb7b4f6335ebed1bea12 (diff)
downloadmch2022-68da02427cfec13692578414ce616182f1afccfc.tar.gz
mch2022-68da02427cfec13692578414ce616182f1afccfc.tar.bz2
mch2022-68da02427cfec13692578414ce616182f1afccfc.zip
New FPGA self test routine
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Diffstat (limited to 'main')
-rw-r--r--main/fpga_test.c681
1 files changed, 515 insertions, 166 deletions
diff --git a/main/fpga_test.c b/main/fpga_test.c
index 7d1cbce..767ffa4 100644
--- a/main/fpga_test.c
+++ b/main/fpga_test.c
@@ -1,17 +1,20 @@
-#include "fpga_test.h"
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
+#include <driver/gpio.h>
+#include "hardware.h"
#include "ili9341.h"
#include "ice40.h"
#include "rp2040.h"
-#include "hardware.h"
+#include "fpga_test.h"
extern const uint8_t fpga_selftest_bin_start[] asm("_binary_fpga_selftest_bin_start");
extern const uint8_t fpga_selftest_bin_end[] asm("_binary_fpga_selftest_bin_end");
+
static const char *TAG = "fpga_test";
esp_err_t load_file_into_psram(ICE40* ice40, FILE* fd) {
@@ -22,207 +25,553 @@ esp_err_t load_file_into_psram(ICE40* ice40, FILE* fd) {
uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE);
if (tx_buffer == NULL) return ESP_FAIL;
- while(1) {
- tx_buffer[0] = write_cmd;
- tx_buffer[1] = (position >> 16);
- tx_buffer[2] = (position >> 8) & 0xFF;
- tx_buffer[3] = position & 0xFF;
- amount_read = fread(&tx_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd);
- if (amount_read < 1) break;
- ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, amount_read);
- esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, NULL, 0);
- if (res != ESP_OK) {
- ESP_LOGE(TAG, "Write transaction failed @ %u", position);
- free(tx_buffer);
- return res;
- }
- position += amount_read;
- };
- free(tx_buffer);
- return ESP_OK;
-}
+/* SPI commands */
+#define SPI_CMD_NOP1 0x00
+#define SPI_CMD_SOC_MSG 0x10
+#define SPI_CMD_REG_ACCESS 0xf0
+#define SPI_CMD_LOOPBACK 0xf1
+#define SPI_CMD_LCD_PASSTHROUGH 0xf2
+#define SPI_CMD_BUTTON_REPORT 0xf4
+#define SPI_CMD_IRQ_ACK 0xfd
+#define SPI_CMD_RESP_ACK 0xfe
+#define SPI_CMD_NOP2 0xff
-esp_err_t load_buffer_into_psram(ICE40* ice40, uint8_t* buffer, uint32_t buffer_length) {
- const uint8_t write_cmd = 0x02;
- uint32_t position = 0;
- uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE);
- if (tx_buffer == NULL) return ESP_FAIL;
- while(1) {
- tx_buffer[0] = write_cmd;
- tx_buffer[1] = (position >> 16);
- tx_buffer[2] = (position >> 8) & 0xFF;
- tx_buffer[3] = position & 0xFF;
- uint32_t length = buffer_length - position;
- if (length > SPI_MAX_TRANSFER_SIZE - 4) length = SPI_MAX_TRANSFER_SIZE - 4;
- memcpy(&tx_buffer[4], &buffer[position], length);
- if (length == 0) break;
- ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, length);
- esp_err_t res = ice40_transaction(ice40, tx_buffer, length + 4, NULL, 0);
+/* Messages to self-test SoC */
+#define SOC_CMD_PING 0x00
+#define SOC_CMD_PING_PARAM 0xc0ffee
+#define SOC_CMD_PING_RESP 0xcafebabe
+
+#define SOC_CMD_RGB_STATE_SET 0x10
+#define SOC_CMD_IRQN_SET 0x11
+#define SOC_CMD_LCD_RGB_CYCLE_SET 0x12
+#define SOC_CMD_PMOD_CYCLE_SET 0x13
+#define SOC_CMD_LCD_PASSTHROUGH_SET 0x14
+
+#define SOC_CMD_PSRAM_TEST 0x20
+#define SOC_CMD_UART_LOOPBACK_TEST 0x21
+#define SOC_CMD_PMOD_OPEN_TEST 0x22
+#define SOC_CMD_PMOD_PLUG_TEST 0x23
+#define SOC_CMD_LCD_INIT_TEST 0x24
+
+#define SOC_CMD_LCD_CHECK_MODE 0x30
+
+#define SOC_RESP_OK 0x00000000
+
+
+/* SoC commands */
+
+static bool soc_message(ICE40* ice40, uint8_t cmd, uint32_t param, uint32_t *resp, TickType_t ticks_to_wait) {
+ esp_err_t res;
+ uint8_t data_tx[6];
+ uint8_t data_rx[6];
+
+ /* Default delay */
+ ticks_to_wait /= 10; /* We do 10 retries */
+ if (!ticks_to_wait)
+ ticks_to_wait = pdMS_TO_TICKS(50);
+
+ /* Prepare message */
+ data_tx[0] = SPI_CMD_SOC_MSG;
+ data_tx[1] = cmd;
+ data_tx[2] = (param >> 16) & 0xff;
+ data_tx[3] = (param >> 8) & 0xff;
+ data_tx[4] = (param ) & 0xff;
+
+ /* Send message to PicoRV */
+ res = ice40_send_turbo(ice40, data_tx, 5);
+ if (res != ESP_OK) {
+ ESP_LOGE(TAG, "SoC message TX failed");
+ return false;
+ }
+
+ /* Poll until we get a response */
+ data_tx[0] = SPI_CMD_RESP_ACK;
+
+ for (int i=0; i<10; i++) {
+ /* Poll */
+ res = ice40_transaction(ice40, data_tx, 6, data_rx, 6);
if (res != ESP_OK) {
- ESP_LOGE(TAG, "Write transaction failed @ %u", position);
- free(tx_buffer);
- return res;
+ ESP_LOGE(TAG, "SoC response RX failed");
+ return false;
}
- position += length;
- };
- free(tx_buffer);
- return ESP_OK;
+
+ /* Was response valid ? */
+ if (data_rx[1] & 0x80)
+ break;
+
+ /* Wait before retry */
+ vTaskDelay(ticks_to_wait);
+ }
+
+ if (!(data_rx[1] & 0x80)) {
+ ESP_LOGE(TAG, "SoC response RX timeout");
+ return false;
+ }
+
+ /* Report response */
+ if (resp) {
+ *resp = 0;
+ for (int i=0; i<4; i++)
+ *resp = (*resp << 8) | data_rx[2+i];
+ }
+
+ return true;
}
-esp_err_t verify_file_in_psram(ICE40* ice40, FILE* fd) {
- fseek(fd, 0, SEEK_SET);
- const uint8_t read_cmd = 0x03;
- uint32_t amount_read;
- uint32_t position = 0;
- uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE);
- if (tx_buffer == NULL) return ESP_FAIL;
- memset(tx_buffer, 0, SPI_MAX_TRANSFER_SIZE);
- uint8_t* verify_buffer = malloc(SPI_MAX_TRANSFER_SIZE);
- if (verify_buffer == NULL) return ESP_FAIL;
- uint8_t* rx_buffer = malloc(SPI_MAX_TRANSFER_SIZE);
- if (rx_buffer == NULL) return ESP_FAIL;
-
- while(1) {
- tx_buffer[0] = read_cmd;
- tx_buffer[1] = (position >> 16);
- tx_buffer[2] = (position >> 8) & 0xFF;
- tx_buffer[3] = position & 0xFF;
- amount_read = fread(&verify_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd);
- if (amount_read < 1) break;
- ESP_LOGI(TAG, "Reading PSRAM @ %u (%u bytes)", position, amount_read);
- esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, rx_buffer, amount_read + 4);
- if (res != ESP_OK) {
- ESP_LOGE(TAG, "Read transaction failed @ %u", position);
- free(tx_buffer);
- return res;
- }
- position += amount_read;
- ESP_LOGI(TAG, "Verifying PSRAM @ %u (%u bytes)", position, amount_read);
- for (uint32_t i = 4; i < amount_read; i++) {
- if (rx_buffer[i] != verify_buffer[i]) {
- ESP_LOGE(TAG, "Verifying PSRAM @ %u failed: %02X != %02X", position + i, rx_buffer[i], verify_buffer[i]);
- free(tx_buffer);
- free(rx_buffer);
- free(verify_buffer);
- return ESP_FAIL;
- }
- }
- };
- free(tx_buffer);
- free(rx_buffer);
- free(verify_buffer);
- ESP_LOGI(TAG, "PSRAM contents verified!");
- return ESP_OK;
+
+/* Test routines */
+
+static bool test_bitstream_load(ICE40* ice40, uint32_t *rc) {
+ esp_err_t res;
+
+ res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start);
+ if (res != ESP_OK) {
+ *rc = res;
+ return false;
+ }
+
+ *rc = 0;
+ return true;
}
-bool test_spi(ICE40* ice40) {
+static bool test_spi_loopback_one(ICE40* ice40) {
esp_err_t res;
- uint8_t data_tx[256];
- uint8_t data_rx[128];
+ uint8_t data_tx[257];
+ uint8_t data_rx[258];
- // Generate pseudo random sequence
- data_tx[0] = 1;
- for (int i = 1; i < 256; i++)
+ /* Generate pseudo random sequence */
+ data_tx[1] = 1;
+ for (int i = 2; i < 257; i++)
data_tx[i] = (data_tx[i-1] << 1) ^ ((data_tx[i-1] & 0x80) ? 0x1d : 0x00);
- // Send first 128 byte at high speed
- res = ice40_send_turbo(ice40, &data_tx[0], 128);
+ /* Send 256 bytes at high speed with echo command */
+ data_tx[0] = SPI_CMD_LOOPBACK;
+
+ res = ice40_send_turbo(ice40, data_tx, 257);
if (res != ESP_OK) {
- ESP_LOGE(TAG, "Transaction 1 failed (Turbo TX)");
+ ESP_LOGE(TAG, "SPI loopback transaction 1 failed (Turbo TX)");
return false;
}
- // Execute full duplex transaction with next 128 bytes
- res = ice40_transaction(ice40, &data_tx[128], 128, data_rx, 128);
+ /* Execute full duplex transaction with next 128 bytes */
+ res = ice40_transaction(ice40, data_tx, 257, data_rx, 257);
if (res != ESP_OK) {
- ESP_LOGE(TAG, "Transaction 2 failed (Full Duplex)");
+ ESP_LOGE(TAG, "SPI loopback transaction 2 failed (Full Duplex)");
+ return false;
+ }
+
+ /* Validate response present */
+ if ((data_rx[1] & 0x80) == 0) {
+ ESP_LOGE(TAG, "SPI loopback transaction 2 reports no response available\n");
return false;
}
- // Validate RX data
- if (memcmp(&data_rx[1], &data_tx[0], 127)) {
- printf("Transaction 1->2 integrity fail:\n");
- for (int i = 0; i < 128; i++)
+ /* Validate RX data (only 254 byte got read) */
+ if (memcmp(&data_rx[2], &data_tx[1], 254)) {
+ ESP_LOGE(TAG, "SPI loopback transaction 1->2 integrity fail:\n");
+ for (int i = 0; i < 254; i++)
printf("%02X%c", data_rx[i], ((i&0xf)==0xf) ? '\n' : ' ');
printf("\n");
return false;
}
- // Receive half duplex
- res = ice40_receive(ice40, data_rx, 128);
+ /* Read two responses and ack them */
+ for (int t = 0; t < 2; t++) {
+ /* Receive half duplex */
+ res = ice40_receive(ice40, data_rx, 258);
+ if (res != ESP_OK) {
+ ESP_LOGE(TAG, "SPI loopback transaction 3.%d failed (Half Duplex RX)", t);
+ return false;
+ }
+
+ /* Short acknowledge command */
+ data_tx[0] = SPI_CMD_RESP_ACK;
+
+ res = ice40_send_turbo(ice40, data_tx, 1);
+ if (res != ESP_OK) {
+ ESP_LOGE(TAG, "SPI loopback transaction 4.%d failed (Turbo ACK)", t);
+ return false;
+ }
+
+ /* Validate response present */
+ if ((data_rx[1] & 0x80) == 0) {
+ ESP_LOGE(TAG, "SPI loopback transaction 3.%d reports no response available\n", t);
+ return false;
+ }
+
+ /* Validate RX data (only 254 byte got read) */
+ if (memcmp(&data_rx[2], &data_tx[1], 254)) {
+ ESP_LOGE(TAG, "SPI loopback transaction %d->3.%d integrity fail:\n", 1+t, t);
+ for (int i = 0; i < 254; i++)
+ printf("%02X%c", data_rx[i], ((i&0xf)==0xf) ? '\n' : ' ');
+ printf("\n");
+ return false;
+ }
+ }
+
+ /* Check there is no more responses pending */
+ data_tx[0] = SPI_CMD_NOP2;
+
+ res = ice40_transaction(ice40, data_tx, 2, data_rx, 2);
if (res != ESP_OK) {
- ESP_LOGE(TAG, "Transaction 3 failed (Half Duplex RX)");
+ ESP_LOGE(TAG, "SPI loopback transaction 5 failed (Full Duplex)");
return false;
}
- // Validate RX data
- if (memcmp(&data_rx[1], &data_tx[128], 127)) {
- printf("Transaction 2->3 integrity fail:\n");
- for (int i = 0; i < 128; i++)
- printf("%02X%c", data_rx[i], ((i&0xf)==0xf) ? '\n' : ' ');
- printf("\n");
+ if ((data_rx[1] & 0x80) != 0) {
+ ESP_LOGE(TAG, "SPI loopback transaction 5 reports response available\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_spi_loopback(ICE40* ice40, uint32_t *rc) {
+ int i;
+
+ /* Run test 256 times */
+ for (i=0; i<256; i++) {
+ if (!test_spi_loopback_one(ice40))
+ break;
+ }
+
+ /* Failure ? */
+ if (i != 256) {
+ *rc = i + 1;
return false;
}
+ /* OK ! */
+ *rc = 0;
+ return true;
+}
+
+static bool test_soc_loopback(ICE40 *ice40, uint32_t *rc) {
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_PING, SOC_CMD_PING_PARAM, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Check response */
+ if (*rc != SOC_CMD_PING_RESP)
+ return false;
+
+ /* Success */
+ *rc = 0;
return true;
}
-void fpga_test(ILI9341* ili9341, ICE40* ice40, xQueueHandle buttonQueue) {
+static bool test_uart_loopback(ICE40* ice40, uint32_t *rc) {
+ /* Enable loopback mode of RP2040 */
+ rp2040_set_fpga_loopback(get_rp2040(), true, true);
+ vTaskDelay(pdMS_TO_TICKS(10));
+
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_UART_LOOPBACK_TEST, 0, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Disable loopback mode of RP2040 */
+ rp2040_set_fpga_loopback(get_rp2040(), true, false);
+
+ /* Check response */
+ return *rc == SOC_RESP_OK;
+}
+
+static bool test_psram(ICE40* ice40, uint32_t *rc) {
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_PSRAM_TEST, 0, rc, pdMS_TO_TICKS(1000))) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Check response */
+ return *rc == SOC_RESP_OK;
+}
+
+static bool test_irq_n(ICE40* ice40, uint32_t *rc) {
esp_err_t res;
- bool reload_fpga = false;
- do {
- printf("Start FPGA test...\n");
- reload_fpga = false;
- printf("LCD deinit...\n");
- ili9341_deinit(ili9341);
-
- printf("FPGA load...\n");
- res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start);
- if (res != ESP_OK) {
- printf("Failed to load app bitstream into FPGA (%d)\n", res);
- ice40_disable(ice40);
- ili9341_init(ili9341);
- return;
- } else {
- printf("Bitstream loaded succesfully!\n");
- }
- int i;
- for (i = 0; i < 256; i++)
- if (!test_spi(ice40))
- break;
- if (i == 256)
- printf("SPI test success\n");
- else
- printf("SPI test failure at iteration %d\n", i);
-
- bool waitForChoice = true;
- while (waitForChoice) {
- rp2040_input_message_t buttonMessage = {0};
- printf("Waiting for button press...\n");
- if (xQueueReceive(buttonQueue, &buttonMessage, portMAX_DELAY) == pdTRUE) {
- printf("Button: %u, %u\n", buttonMessage.input, buttonMessage.state);
- if (buttonMessage.state) {
- switch(buttonMessage.input) {
- case RP2040_INPUT_BUTTON_HOME:
- case RP2040_INPUT_BUTTON_MENU:
- waitForChoice = false;
- break;
- case RP2040_INPUT_BUTTON_BACK:
- reload_fpga = true;
- waitForChoice = false;
- break;
- case RP2040_INPUT_BUTTON_ACCEPT:
- reload_fpga = true;
- waitForChoice = false;
- break;
- default:
- break;
- }
+ /* Set pin as input */
+ res = gpio_set_direction(GPIO_INT_FPGA, GPIO_MODE_INPUT);
+ if (res != ESP_OK) {
+ *rc = 32;
+ return false;
+ }
+
+ /* Assert interrupt line */
+ if (!soc_message(ice40, SOC_CMD_IRQN_SET, 1, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ if (*rc != SOC_RESP_OK)
+ return false;
+
+ /* Check level is 0 */
+ if (gpio_get_level(GPIO_INT_FPGA) != 0) {
+ *rc = 16;
+ return false;
+ }
+
+ /* Release interrupt line */
+ if (!soc_message(ice40, SOC_CMD_IRQN_SET, 0, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ if (*rc != SOC_RESP_OK)
+ return false;
+
+ /* Check level is 1 */
+ if (gpio_get_level(GPIO_INT_FPGA) != 1) {
+ *rc = 16;
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_lcd_mode(ICE40* ice40, uint32_t *rc) {
+ esp_err_t res;
+ bool ok;
+
+ /* Defaults */
+ ok = true;
+ *rc = 0;
+
+ /* Check state is 0 */
+ if (!soc_message(ice40, SOC_CMD_LCD_CHECK_MODE, 0, rc, 0)) {
+ *rc = 16;
+ return false;
+ }
+
+ if (*rc != SOC_RESP_OK)
+ return false;
+
+ /* Set LCD mode to 1 */
+ res = gpio_set_level(GPIO_LCD_MODE, 1);
+ if (res != ESP_OK) {
+ *rc = 32;
+ return false;
+ }
+
+ /* Check state is 1 */
+ if (!soc_message(ice40, SOC_CMD_LCD_CHECK_MODE, 1, rc, 0)) {
+ *rc = 17;
+ ok = false;
+ }
+
+ if (*rc != SOC_RESP_OK)
+ ok = false;
+
+ /* Set LCD mode back to 0 */
+ res = gpio_set_level(GPIO_LCD_MODE, 0);
+ if (res != ESP_OK) {
+ *rc = 33;
+ return false;
+ }
+
+ /* All good */
+ return ok;
+}
+
+static bool test_pmod_open(ICE40* ice40, uint32_t *rc) {
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_PMOD_OPEN_TEST, 0, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Check response */
+ return *rc == SOC_RESP_OK;
+}
+
+static bool test_pmod_plug(ICE40* ice40, uint32_t *rc) {
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_PMOD_PLUG_TEST, 0, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Check response */
+ return *rc == SOC_RESP_OK;
+}
+
+static bool test_lcd_init(ICE40* ice40, uint32_t *rc) {
+ /* Execute command */
+ if (!soc_message(ice40, SOC_CMD_LCD_INIT_TEST, 0, rc, 0)) {
+ *rc = -1;
+ return false;
+ }
+
+ /* Check response */
+ return *rc == SOC_RESP_OK;
+}
+
+
+typedef bool (*test_fn)(ICE40 *ice40, uint32_t *rc);
+
+
+static bool wait_button(xQueueHandle buttonQueue) {
+ rp2040_input_message_t buttonMessage = {0};
+
+ while (1) {
+ if (xQueueReceive(buttonQueue, &buttonMessage, 0) == pdTRUE) {
+ if (buttonMessage.state) {
+ switch(buttonMessage.input) {
+ case RP2040_INPUT_BUTTON_HOME:
+ case RP2040_INPUT_BUTTON_MENU:
+ case RP2040_INPUT_BUTTON_BACK:
+ return false;
+ case RP2040_INPUT_BUTTON_ACCEPT:
+ return true;
+ default:
+ break;
}
}
+ } else {
+ vTaskDelay(pdMS_TO_TICKS(10));
}
- ice40_disable(ice40);
- ili9341_init(ili9341);
- } while (reload_fpga);
+ }
+}
+
+static bool run_test(ICE40* ice40, pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line,
+ const char *test_name, test_fn fn) {
+ bool rv;
+ uint32_t rc;
+
+ /* Test name */
+ pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name);
+ if (ili9341)
+ ili9341_write(ili9341, pax_buffer->buf);
+
+ /* Run the test */
+ rv = fn(ice40, &rc);
+
+ /* Display result */
+ if (!rv) {
+ /* Error */
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%08x", rc);
+ pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf);
+ } else {
+ /* OK ! */
+ pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK");
+ }
+
+ if (ili9341)
+ ili9341_write(ili9341, pax_buffer->buf);
+
+ /* Pass through the 'OK' status */
+ return rv;
+}
+
+#define RUN_TEST(name, fn) do {\
+ ok &= run_test(ice40, pax_buffer, font, ili9341, line++, name, fn); \
+} while (0)
+
+#define RUN_TEST_MANDATORY(name, fn) do {\
+ if (!run_test(ice40, pax_buffer, font, ili9341, line++, name, fn)) { \
+ pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \
+ ili9341_write(ili9341, pax_buffer->buf); \
+ ok = false; \
+ goto error; \
+ } \
+} while (0)
+
+#define RUN_TEST_BLIND(name, fn) do {\
+ ok &= run_test(ice40, pax_buffer, font, NULL, line++, name, fn); \
+} while (0)
+
+
+static void
+run_all_tests(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341)
+{
+ const pax_font_t *font;
+ int line = 0;
+ bool ok = true;
+
+ /* Screen init */
+ font = pax_get_font("sky mono");
+
+ pax_noclip(pax_buffer);
+ pax_background(pax_buffer, 0x8060f0);
+ ili9341_write(ili9341, pax_buffer->buf);
+
+ /* Run mandatory tests */
+ RUN_TEST_MANDATORY("Bitstream load", test_bitstream_load);
+ RUN_TEST_MANDATORY("SPI loopback", test_spi_loopback);
+ RUN_TEST_MANDATORY("SoC loopback", test_soc_loopback);
+
+ /* Set indicator to "in-progress" */
+ soc_message(ice40, SOC_CMD_RGB_STATE_SET, 1, NULL, 0);
+
+ /* Run non-interactive tests */
+ RUN_TEST("UART loopback", test_uart_loopback);
+ RUN_TEST("PSRAM", test_psram);
+ RUN_TEST("IRQ_n signal", test_irq_n);
+ RUN_TEST("LCD_MODE signal", test_lcd_mode);
+ RUN_TEST("PMOD open", test_pmod_open);
+
+ /* Show instructions for interactive test */
+ pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+ 0, "Insert PMOD plug");
+ pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+10, "Then press button for interactive test");
+ pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+20, " - Check LCD color bars");
+ pax_draw_text(pax_buffer, 0xffc0c0c0, font, 9, 25, 20*line+30, " - Then LCD & RGB led color cycling");
+ ili9341_write(ili9341, pax_buffer->buf);
+
+ /* Wait for button */
+ wait_button(buttonQueue);
+
+ /* Clear the instructions from buffer */
+ pax_draw_rect(pax_buffer, 0xff8060f0, 0, 20*line, 320, 240-20*line);
+
+ /* Handover LCD to FPGA */
+ ili9341_deinit(ili9341);
+
+ /* Run interactive tests */
+ RUN_TEST("PMOD plug", test_pmod_plug);
+ RUN_TEST("LCD init", test_lcd_init);
+
+ /* Wait a second (for user to see color bars) */
+ vTaskDelay(pdMS_TO_TICKS(1000));
+
+ /* Start LCD / RGB cycling */
+ soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 1, NULL, 0);
+
+ /* Wait for button */
+ wait_button(buttonQueue);
+
+ /* Stop LCD / RGB cycling */
+ soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 0, NULL, 0);
+
+ /* Take control of the LCD back and refresh screen */
+ ili9341_init(ili9341);
+
+error:
+ /* Update indicator */
+ soc_message(ice40, SOC_CMD_RGB_STATE_SET, ok ? 2 : 3, NULL, 0);
+
+ /* Pass / Fail result on screen */
+ if (ok)
+ pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 100, 20*line, "PASS");
+ else
+ pax_draw_text(pax_buffer, 0xffff0000, font, 36, 100, 20*line, "FAIL");
+
+ ili9341_write(ili9341, pax_buffer->buf);
+
+ /* Done, just wait for button */
+ wait_button(buttonQueue);
+
+ /* Cleanup */
+ ice40_disable(ice40);
+
+ return;
+}
+
+void fpga_test(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) {
+ run_all_tests(buttonQueue, ice40, pax_buffer, ili9341);
}