diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9c25c928d..d38142c79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -417,6 +417,16 @@ set(header_eez_modules_bp3c
list (APPEND header_files ${header_eez_modules_bp3c})
source_group("eez\\modules\\bp3c" FILES ${src_eez_modules_bp3c} ${header_eez_modules_bp3c})
+set(src_eez_modules_fpga
+ src/eez/modules/fpga/prog.cpp
+)
+list (APPEND src_files ${src_eez_modules_fpga})
+set(header_eez_modules_fpga
+ src/eez/modules/fpga/prog.h
+)
+list (APPEND header_files ${header_eez_modules_fpga})
+source_group("eez\\modules\\fpga" FILES ${src_eez_modules_fpga} ${header_eez_modules_fpga})
+
set(src_eez_modules_mcu
src/eez/modules/mcu/battery.cpp
src/eez/modules/mcu/display.cpp
diff --git a/src/eez/gui/app_context.cpp b/src/eez/gui/app_context.cpp
index e359ab70c..b4ba793c1 100644
--- a/src/eez/gui/app_context.cpp
+++ b/src/eez/gui/app_context.cpp
@@ -104,10 +104,10 @@ void AppContext::onPageChanged(int previousPageId, int activePageId) {
}
void AppContext::doShowPage(int pageId, Page *page, int previousPageId) {
-#if CONF_OPTION_FPGA
- pageId = PAGE_ID_WELCOME_800X480;
- page = nullptr;
-#endif
+//#if CONF_OPTION_FPGA
+// pageId = PAGE_ID_WELCOME_800X480;
+// page = nullptr;
+//#endif
page = page ? page : getPageFromIdHook(pageId);
diff --git a/src/eez/modules/fpga/prog.cpp b/src/eez/modules/fpga/prog.cpp
new file mode 100644
index 000000000..f765b27ce
--- /dev/null
+++ b/src/eez/modules/fpga/prog.cpp
@@ -0,0 +1,467 @@
+/*
+ * EEZ Modular Firmware
+ * Copyright (C) 2020-present, Envox d.o.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if EEZ_PLATFORM_STM32
+#include
+#include
+#endif
+
+#include
+#include
+#include
+
+#include
+
+#include
+
+namespace eez {
+namespace fpga {
+
+#if defined(EEZ_PLATFORM_SIMULATOR)
+
+#define DOUT2_GPIO_Port 0
+#define DOUT2_Pin 0
+#define GPIOB 0
+#define GPIO_PIN_14 0
+#define GPIOI 0
+#define GPIO_PIN_3 0
+#define GPIO_PIN_1 0
+
+typedef uint32_t GPIO_TypeDef;
+
+enum GPIO_PinState {
+ GPIO_PIN_RESET = 0,
+ GPIO_PIN_SET
+};
+
+void HAL_GPIO_WritePin(GPIO_TypeDef *port, uint32_t pin, GPIO_PinState state) {
+}
+
+GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *port, uint32_t pin) {
+ return GPIO_PIN_SET;
+}
+
+#endif
+
+struct Pin {
+ Pin(GPIO_TypeDef *port, uint32_t pin) : m_port(port), m_pin(pin) {
+ }
+
+ void on() {
+ HAL_GPIO_WritePin(m_port, m_pin, GPIO_PIN_SET);
+ }
+
+ void off() {
+ HAL_GPIO_WritePin(m_port, m_pin, GPIO_PIN_RESET);
+ }
+
+ GPIO_PinState value() {
+ return HAL_GPIO_ReadPin(m_port, m_pin);
+ }
+
+ GPIO_TypeDef *m_port;
+ uint32_t m_pin;
+};
+
+Pin tms(DOUT2_GPIO_Port, DOUT2_Pin);
+Pin tdi(GPIOB, GPIO_PIN_14);
+Pin tdo(GPIOI, GPIO_PIN_3);
+Pin tck(GPIOI, GPIO_PIN_1);
+
+void bitbang_jtag_on() {
+ // led=Pin(gpio_led,Pin.OUT)
+ // tms=Pin(gpio_tms,Pin.OUT)
+ // tck=Pin(gpio_tck,Pin.OUT)
+ // tdi=Pin(gpio_tdi,Pin.OUT)
+ // tdo=Pin(gpio_tdo,Pin.IN)
+}
+
+void bitbang_jtag_off() {
+ // led=Pin(gpio_led,Pin.IN)
+ // tms=Pin(gpio_tms,Pin.IN)
+ // tck=Pin(gpio_tck,Pin.IN)
+ // tdi=Pin(gpio_tdi,Pin.IN)
+ // tdo=Pin(gpio_tdo,Pin.IN)
+ // a = led.value()
+ // a = tms.value()
+ // a = tck.value()
+ // a = tdo.value()
+ // a = tdi.value()
+ // del led
+ // del tms
+ // del tck
+ // del tdi
+ // del tdo
+}
+
+void spi_jtag_on() {
+ // global hwspi,swspi
+ // hwspi=SPI(spi_channel, baudrate=spi_freq, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, sck=Pin(gpio_tck), mosi=Pin(gpio_tdi), miso=Pin(gpio_tdo))
+ // swspi=SPI(-1, baudrate=spi_freq, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, sck=Pin(gpio_tck), mosi=Pin(gpio_tdi), miso=Pin(gpio_tdo))
+}
+
+void spi_jtag_off() {
+ // hwspi.deinit()
+ // del hwspi
+
+ // swspi.deinit()
+ // del swspi
+}
+
+void send_tms(int val) {
+ if (val) {
+ tms.on();
+ } else {
+ tms.off();
+ }
+ tck.off();
+ tck.on();
+}
+
+void send_tms0111() {
+ send_tms(0); // # -> pause DR
+ send_tms(1); // # -> exit 2 DR
+ send_tms(1); // # -> update DR
+ send_tms(1); // # -> select DR scan
+}
+
+void send_read_buf_lsb1st(const uint8_t *buf, int bufLen, int last, uint8_t *w) {
+ const uint8_t *p = buf;
+ int l = bufLen;
+
+ int val = 0;
+ tms.off();
+
+ for (int i = 0; i < l - 1; i++) {
+ uint8_t byte = 0;
+ uint8_t val = p[i];
+
+ for (int nf = 0; nf < 8; nf++) {
+ if ((val >> nf) & 1) {
+ tdi.on();
+ } else {
+ tdi.off();
+ }
+ tck.off();
+ tck.on();
+ if (tdo.value()) {
+ byte |= 1 << nf;
+ }
+ }
+
+ if (w) {
+ w[i] = byte; // # write byte
+ }
+ }
+
+ uint8_t byte = 0;
+ val = p[l - 1]; // # read last byte
+ for (int nf = 0; nf < 7; nf++) { // # first 7 bits
+ if ((val >> nf) & 1) {
+ tdi.on();
+ } else {
+ tdi.off();
+ }
+ tck.off();
+ tck.on();
+ if (tdo.value()) {
+ byte |= 1 << nf;
+ }
+ }
+
+ // # last bit
+ if (last) {
+ tms.on();
+ }
+ if ((val >> 7) & 1) {
+ tdi.on();
+ } else {
+ tdi.off();
+ }
+ tck.off();
+ tck.on();
+ if (tdo.value()) {
+ byte |= 1 << 7;
+ }
+ if (w) {
+ w[l - 1] = byte; //# write last byte
+ }
+}
+
+void reset_tap() {
+ for (int n = 0; n < 6; n++) {
+ send_tms(1); // # -> Test Logic Reset
+ }
+}
+
+void runtest_idle(int count, uint32_t duration_ms) {
+ uint32_t leave = millis() + duration_ms;
+
+ for (int n = 0; n < count; n++) {
+ send_tms(0); // # -> idle
+ }
+
+ while (leave >= millis()) {
+ send_tms(0); // # -> idle
+ }
+
+ send_tms(1); // # -> select DR scan
+}
+
+void sir(const uint8_t *buf, int bufLen) {
+ send_tms(1); // # -> select IR scan
+ send_tms(0); // # -> capture IR
+ send_tms(0); // # -> shift IR
+ send_read_buf_lsb1st(buf, bufLen, 1, 0); //# -> exit 1 IR
+ send_tms0111(); // # -> select DR scan
+}
+
+void sir(uint8_t byte) {
+ sir(&byte, 1);
+}
+
+void sir_idle(const uint8_t *buf, int bufLen, int n, uint32_t ms) {
+ send_tms(1); // # -> select IR scan
+ send_tms(0); // # -> capture IR
+ send_tms(0); // # -> shift IR
+ send_read_buf_lsb1st(buf, bufLen, 1, 0); // # -> exit 1 IR
+ send_tms(0); // # -> pause IR
+ send_tms(1); // # -> exit 2 IR
+ send_tms(1); // # -> update IR
+ runtest_idle(n + 1, ms); // # -> select DR scan
+}
+
+void sir_idle(uint8_t byte, int n, uint32_t ms) {
+ sir_idle(&byte, 1, n, ms);
+}
+
+void sdr(const uint8_t *buf, int bufLen) {
+ send_tms(0); // # ->capture DR
+ send_tms(0); // # ->shift DR
+ send_read_buf_lsb1st(buf, bufLen, 1, 0);
+ send_tms0111(); // # -> select DR scan
+}
+
+void sdr(uint8_t byte) {
+ sdr(&byte, 1);
+}
+
+void sdr_idle(const uint8_t *buf, int bufLen, int n, uint32_t ms) {
+ send_tms(0); // # -> capture DR
+ send_tms(0); // # -> shift DR
+ send_read_buf_lsb1st(buf, bufLen, 1, 0);
+ send_tms(0); // # -> pause DR
+ send_tms(1); // # -> exit 2 DR
+ send_tms(1); // # -> update DR
+ runtest_idle(n + 1, ms); // # -> select DR scan
+}
+
+void sdr_idle(uint8_t byte, int n, uint32_t ms) {
+ sdr_idle(&byte, 1, n, ms);
+}
+
+void sdr_response(uint8_t *buf, int bufLen) {
+ send_tms(0); // # -> capture DR
+ send_tms(0); // # -> shift DR
+ send_read_buf_lsb1st(buf, bufLen, 1, buf);
+ send_tms0111(); // # -> select DR scan
+}
+
+void check_response(uint32_t response, uint32_t expected, uint32_t mask = 0xFFFFFFFF, const char *message = "") {
+ if ((response & mask) != expected) {
+ DebugTrace("0x%08X & 0x%08X != 0x%08X %s\n", response, mask, expected, message);
+ }
+}
+
+void common_open() {
+ spi_jtag_on();
+
+ // hwspi.init(sck=Pin(gpio_tcknc)) # avoid TCK-glitch
+
+ bitbang_jtag_on();
+
+ reset_tap();
+ runtest_idle(1, 0);
+
+ //#sir(b"\xE0") # read IDCODE
+ //#sdr(pack("capture DR
+ send_tms(0); // # ->shift DR
+ // # switch from bitbanging to SPI mode
+
+ // hwspi.init(sck = Pin(gpio_tck)) // # 1 TCK - glitch ? TDI = 0
+
+ // # we are lucky that format of the bitstream tolerates
+ // # any leading and trailing junk bits. If it weren't so,
+ // # HW SPI JTAG acceleration wouldn't work.
+ // # to upload the bitstream:
+ // # FAST SPI mode
+ // #hwspi.write(block)
+ // # SLOW bitbanging mode
+ // #for byte in block:
+ // # send_int_msb1st(byte,0)
+
+}
+
+void prog_stream_done() {
+ // # switch from hardware SPI to bitbanging done after prog_stream()
+ // hwspi.init(sck=Pin(gpio_tcknc)) # avoid TCK-glitch
+ spi_jtag_off();
+}
+
+// # call this after uploading all of the bitstream blocks,
+// # this will exit FPGA programming mode and start the bitstream
+// # returns status True - OK False - Fail
+bool prog_close() {
+ bitbang_jtag_on();
+
+ send_tms(1); // # ->exit 1 DR
+ send_tms(0); // # ->pause DR
+ send_tms(1); // # ->exit 2 DR
+ send_tms(1); // # ->update DR
+ // #send_tms(0) # ->idle, disabled here as runtest_idle does the same
+ runtest_idle(100, 10);
+ // # ---------- bitstream end -----------
+ sir_idle(0xC0, 2, 1); //# read usercode
+
+ uint32_t usercode;
+ sdr_response((uint8_t *)&usercode, 4);
+ check_response(usercode, 0, 0xFFFFFFFF, "FAIL usercode");
+
+ sir_idle(0x26, 2, 200); // # ISC DISABLE
+ sir_idle(0xFF, 2, 1); // # BYPASS
+
+ sir(0x3C); // # LSC_READ_STATUS
+
+ uint32_t status;
+ sdr_response((uint8_t *)&status, 4);
+ check_response(status, 0x100, 0x2100, "FAIL status");
+
+ bool done = true;
+ if ((status & 0x2100) != 0x100) {
+ done = false;
+ }
+
+ reset_tap();
+
+ bitbang_jtag_off();
+
+ return done;
+}
+
+void write_block(uint8_t *block, uint32_t blockLen) {
+#if defined(EEZ_PLATFORM_STM32)
+ spi::select(0, spi::CHIP_FPGA);
+ spi::transmit(0, block, blockLen);
+ spi::deselect(0);
+#endif
+}
+
+scpi_result_t prog(const char *filePath) {
+#if OPTION_DISPLAY
+ size_t total;
+ size_t transfered;
+#endif
+
+ File file;
+
+ if (!file.open(filePath, FILE_OPEN_EXISTING | FILE_READ)) {
+ goto Exit;
+ }
+
+#if OPTION_DISPLAY
+ psu::gui::showProgressPageWithoutAbort("Programming FPGA...");
+ psu::gui::updateProgressPage(0, 0);
+ total = file.size();
+ transfered = 0;
+#endif
+
+ prog_open();
+
+ while (true) {
+ uint8_t block[1024];
+ size_t bytes = file.read(block, sizeof(block));
+ if (bytes == 0) {
+ break;
+ }
+
+ write_block(block, bytes);
+
+#if OPTION_DISPLAY
+ transfered += bytes;
+ psu::gui::updateProgressPage(transfered, total);
+#endif
+
+ if (bytes < sizeof(block)) {
+ break;
+ }
+ }
+
+ prog_stream_done();
+
+ prog_close();
+
+ file.close();
+
+#if OPTION_DISPLAY
+ psu::gui::hideProgressPage();
+#endif
+
+Exit:
+
+ return SCPI_RES_OK;
+}
+
+} // namespace fpga
+} // namespace eez
diff --git a/src/eez/modules/fpga/prog.h b/src/eez/modules/fpga/prog.h
new file mode 100644
index 000000000..1af31a0c6
--- /dev/null
+++ b/src/eez/modules/fpga/prog.h
@@ -0,0 +1,27 @@
+/*
+ * EEZ Modular Firmware
+ * Copyright (C) 2020-present, Envox d.o.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+namespace eez {
+namespace fpga {
+
+scpi_result_t prog(const char *filePath);
+
+} // namespace fpga
+} // namespace eez
diff --git a/src/eez/modules/psu/gui/file_manager.cpp b/src/eez/modules/psu/gui/file_manager.cpp
index 9d4fb8533..026898e89 100644
--- a/src/eez/modules/psu/gui/file_manager.cpp
+++ b/src/eez/modules/psu/gui/file_manager.cpp
@@ -51,6 +51,7 @@
#include
#include
+#include
using namespace eez::psu::gui;
@@ -654,6 +655,12 @@ bool isOpenFileEnabled() {
return mp::isIdle();
}
+ if (fileItem->type == FILE_TYPE_OTHER) {
+ if (endsWithNoCase(fileItem->name, ".bit")) {
+ return true;
+ }
+ }
+
return false;
}
@@ -690,6 +697,10 @@ void openFile() {
sendMessageToLowPriorityThread(THREAD_MESSAGE_FILE_MANAGER_OPEN_IMAGE_FILE);
} else if (fileItem->type == FILE_TYPE_MICROPYTHON) {
mp::startScript(filePath);
+ } else if (fileItem->type == FILE_TYPE_OTHER) {
+ if (endsWithNoCase(filePath, ".bit")) {
+ sendMessageToLowPriorityThread(THREAD_MESSAGE_FILE_MANAGER_OPEN_BIT_FILE);
+ }
}
}
@@ -712,6 +723,22 @@ void openImageFile() {
}
}
+void openBitFile() {
+ auto fileItem = getFileItem(g_selectedFileIndex);
+ if (fileItem) {
+ if (strlen(g_currentDirectory) + 1 + strlen(fileItem->name) > MAX_PATH_LENGTH) {
+ return;
+ }
+
+ char filePath[MAX_PATH_LENGTH + 1];
+ strcpy(filePath, g_currentDirectory);
+ strcat(filePath, "/");
+ strcat(filePath, fileItem->name);
+
+ fpga::prog(filePath);
+ }
+}
+
bool isUploadFileEnabled() {
#if !defined(EEZ_PLATFORM_SIMULATOR)
if (psu::serial::isConnected()) {
diff --git a/src/eez/modules/psu/gui/file_manager.h b/src/eez/modules/psu/gui/file_manager.h
index 5f1f3529a..a6ef962d3 100644
--- a/src/eez/modules/psu/gui/file_manager.h
+++ b/src/eez/modules/psu/gui/file_manager.h
@@ -72,6 +72,7 @@ bool isDeleteFileEnabled();
void deleteFile();
void openImageFile();
+void openBitFile();
void onEncoder(int couter);
diff --git a/src/eez/modules/psu/gui/keypad.cpp b/src/eez/modules/psu/gui/keypad.cpp
index a3dba3d33..3d78ebc9c 100644
--- a/src/eez/modules/psu/gui/keypad.cpp
+++ b/src/eez/modules/psu/gui/keypad.cpp
@@ -97,7 +97,7 @@ void onKeypadTextTouch(const WidgetCursor &widgetCursor, Event &touchEvent) {
}
Keypad *keypad = getActiveKeypad();
- if (keypad && keypad != getActiveNumericKeypad()) {
+ if (keypad) {
keypad->setCursorPosition(DISPLAY_DATA_getCharIndexAtPosition(touchEvent.x, widgetCursor));
}
}
@@ -434,6 +434,7 @@ void NumericKeypad::init(
if (value.getType() == VALUE_TYPE_IP_ADDRESS) {
ipAddressToString(value.getUInt32(), m_keypadText);
+ m_cursorPosition = strlen(m_keypadText);
m_state = BEFORE_DOT;
}
}
@@ -725,6 +726,8 @@ int NumericKeypad::getCursorPostion() {
double NumericKeypad::getValue() {
const char *p = m_keypadText;
+ bool empty = true;
+
int a = 0;
double b = 0;
int sign = 1;
@@ -739,6 +742,7 @@ double NumericKeypad::getValue() {
while (*p && *p != getDotSign()) {
a = a * 10 + (*p - '0');
++p;
+ empty = false;
}
if (*p) {
@@ -746,9 +750,14 @@ double NumericKeypad::getValue() {
while (q != p) {
b = (b + (*q - '0')) / 10;
--q;
+ empty = false;
}
}
+ if (empty) {
+ return NAN;
+ }
+
double value = sign * (a + b);
if (isPico()) {
@@ -780,7 +789,13 @@ void NumericKeypad::digit(int d) {
insertChar('+');
}
}
+ } else {
+ if (m_keypadText[m_cursorPosition] == '-' || m_keypadText[m_cursorPosition] == '+') {
+ sound::playBeep();
+ return;
+ }
}
+
insertChar(d + '0');
if (!checkNumSignificantDecimalDigits()) {
@@ -803,7 +818,7 @@ void NumericKeypad::dot() {
return;
}
- if (m_state == EMPTY) {
+ if (m_state == START || m_state == EMPTY) {
if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
if (strlen(m_keypadText) == 0) {
insertChar('+');
@@ -813,12 +828,7 @@ void NumericKeypad::dot() {
m_state = BEFORE_DOT;
}
- if (m_state == START || m_state == EMPTY) {
- insertChar('0');
- m_state = BEFORE_DOT;
- }
-
- if (m_state == BEFORE_DOT) {
+ if (m_state == BEFORE_DOT && m_keypadText[m_cursorPosition] != '-' && m_keypadText[m_cursorPosition] != '+') {
insertChar(getDotSign());
m_state = AFTER_DOT;
} else {
@@ -886,6 +896,8 @@ void NumericKeypad::sign() {
if (m_keypadText[0] == 0) {
m_keypadText[0] = '-';
m_keypadText[1] = 0;
+ m_cursorPosition = 1;
+ m_state = BEFORE_DOT;
} else if (m_keypadText[0] == '-') {
m_keypadText[0] = '+';
} else {
@@ -894,11 +906,17 @@ void NumericKeypad::sign() {
} else {
if (m_keypadText[0] == '-') {
strcpy(m_keypadText, m_keypadText + 1);
+ if (m_cursorPosition > 0) {
+ m_cursorPosition--;
+ }
} else if (m_keypadText[0] == '+') {
m_keypadText[0] = '-';
} else {
- memmove(m_keypadText + 1, m_keypadText, strlen(m_keypadText));
+ auto n = strlen(m_keypadText);
+ memmove(m_keypadText + 1, m_keypadText, n);
+ m_keypadText[n + 1] = 0;
m_keypadText[0] = '-';
+ m_cursorPosition++;
}
if (m_state == START || m_state == EMPTY) {
@@ -981,6 +999,10 @@ void NumericKeypad::ok() {
return;
} else {
double value = getValue();
+ if (isNaN(value)) {
+ sound::playBeep();
+ return;
+ }
if (!isNaN(m_options.min) && value < m_options.min && !(value == 0 && m_options.allowZero)) {
psuErrorMessage(0, MakeLessThenMinMessageValue(m_options.min, m_startValue));
diff --git a/src/eez/modules/psu/scpi/debug.cpp b/src/eez/modules/psu/scpi/debug.cpp
index bd8cab8c5..458fc064f 100644
--- a/src/eez/modules/psu/scpi/debug.cpp
+++ b/src/eez/modules/psu/scpi/debug.cpp
@@ -48,6 +48,8 @@
#include
+#include
+
namespace eez {
namespace psu {
@@ -117,7 +119,13 @@ scpi_result_t scpi_cmd_debug(scpi_t *context) {
taskENTER_CRITICAL();
#endif
while(1);
- } {
+ } else if (cmd == 31) {
+ char filePath[MAX_PATH_LENGTH + 1];
+ if (!getFilePath(context, filePath, true)) {
+ return SCPI_RES_ERR;
+ }
+ return fpga::prog(filePath);
+ } else {
SCPI_ErrorPush(context, SCPI_ERROR_HARDWARE_MISSING);
return SCPI_RES_ERR;
}
diff --git a/src/eez/platform/stm32/spi.cpp b/src/eez/platform/stm32/spi.cpp
index 2eb8dc29a..3961d2e5a 100644
--- a/src/eez/platform/stm32/spi.cpp
+++ b/src/eez/platform/stm32/spi.cpp
@@ -57,6 +57,9 @@ void select(uint8_t slotIndex, int chip) {
} else if (chip == CHIP_IOEXP || chip == CHIP_TEMP_SENSOR) {
WRITE_REG(handle[slotIndex]->Instance->CR1, SPI_MODE_MASTER | SPI_DIRECTION_2LINES | SPI_POLARITY_LOW | SPI_PHASE_1EDGE | (SPI_NSS_SOFT & SPI_CR1_SSM) | SPI_BAUDRATEPRESCALER_16 | SPI_FIRSTBIT_MSB | SPI_CRCCALCULATION_DISABLE);
handle[slotIndex]->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ } else if (chip == CHIP_FPGA) {
+ WRITE_REG(handle[slotIndex]->Instance->CR1, SPI_MODE_MASTER | SPI_DIRECTION_1LINE | SPI_POLARITY_HIGH | SPI_PHASE_1EDGE | (SPI_NSS_SOFT & SPI_CR1_SSM) | SPI_BAUDRATEPRESCALER_16 | SPI_FIRSTBIT_MSB | SPI_CRCCALCULATION_DISABLE);
+ handle[slotIndex]->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
} else {
WRITE_REG(handle[slotIndex]->Instance->CR1, SPI_MODE_MASTER | SPI_DIRECTION_2LINES | SPI_POLARITY_LOW | SPI_PHASE_2EDGE | (SPI_NSS_SOFT & SPI_CR1_SSM) | SPI_BAUDRATEPRESCALER_16 | SPI_FIRSTBIT_MSB | SPI_CRCCALCULATION_DISABLE);
handle[slotIndex]->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
diff --git a/src/eez/platform/stm32/spi.h b/src/eez/platform/stm32/spi.h
index 758ad0001..dd290f002 100644
--- a/src/eez/platform/stm32/spi.h
+++ b/src/eez/platform/stm32/spi.h
@@ -35,6 +35,7 @@ static const int CHIP_IOEXP = 2;
static const int CHIP_TEMP_SENSOR = 3;
static const int CHIP_SLAVE_MCU = 4;
static const int CHIP_SLAVE_MCU_NO_CRC = 5;
+static const int CHIP_FPGA = 6;
void init(uint8_t slotIndex, int chip);
diff --git a/src/eez/tasks.cpp b/src/eez/tasks.cpp
index 192d269a8..d486438c1 100644
--- a/src/eez/tasks.cpp
+++ b/src/eez/tasks.cpp
@@ -365,6 +365,8 @@ void lowPriorityThreadOneIter() {
file_manager::uploadFile();
} else if (type == THREAD_MESSAGE_FILE_MANAGER_OPEN_IMAGE_FILE) {
file_manager::openImageFile();
+ } else if (type == THREAD_MESSAGE_FILE_MANAGER_OPEN_BIT_FILE) {
+ file_manager::openBitFile();
} else if (type == THREAD_MESSAGE_FILE_MANAGER_DELETE_FILE) {
file_manager::deleteFile();
} else if (type == THREAD_MESSAGE_FILE_MANAGER_RENAME_FILE) {
diff --git a/src/eez/tasks.h b/src/eez/tasks.h
index c67096566..105b41dba 100644
--- a/src/eez/tasks.h
+++ b/src/eez/tasks.h
@@ -90,6 +90,7 @@ enum LowPriorityThreadMessage {
THREAD_MESSAGE_FILE_MANAGER_LOAD_DIRECTORY,
THREAD_MESSAGE_FILE_MANAGER_UPLOAD_FILE,
THREAD_MESSAGE_FILE_MANAGER_OPEN_IMAGE_FILE,
+ THREAD_MESSAGE_FILE_MANAGER_OPEN_BIT_FILE,
THREAD_MESSAGE_FILE_MANAGER_DELETE_FILE,
THREAD_MESSAGE_FILE_MANAGER_RENAME_FILE,
THREAD_MESSAGE_DLOG_UPLOAD_FILE,
diff --git a/src/eez/util.cpp b/src/eez/util.cpp
index 6f52f3c92..0612911a9 100644
--- a/src/eez/util.cpp
+++ b/src/eez/util.cpp
@@ -214,6 +214,10 @@ bool isNaN(float x) {
return x != x;
}
+bool isNaN(double x) {
+ return x != x;
+}
+
bool isDigit(char ch) {
return ch >= '0' && ch <= '9';
}
diff --git a/src/eez/util.h b/src/eez/util.h
index b027e66eb..763911f6e 100644
--- a/src/eez/util.h
+++ b/src/eez/util.h
@@ -76,6 +76,7 @@ float floorPrec(float a, float prec);
float ceilPrec(float a, float prec);
bool isNaN(float x);
+bool isNaN(double x);
bool isDigit(char ch);
bool isHexDigit(char ch);