|
@@ -16,7 +16,7 @@
|
|
|
|
|
|
#define FPGA_SPI_HOST FSPI /* SPI2 */
|
|
#define FPGA_SPI_HOST FSPI /* SPI2 */
|
|
|
|
|
|
-#define FPGA_PRIORITY 3
|
|
|
|
|
|
+#define FPGA_PRIORITY 10
|
|
#define FPGA_SVC_STACK 4096
|
|
#define FPGA_SVC_STACK 4096
|
|
|
|
|
|
static spi_bus_config_t spi_bus_config = {
|
|
static spi_bus_config_t spi_bus_config = {
|
|
@@ -53,14 +53,17 @@ static const spi_device_interface_config_t spi_device_interface_config = {
|
|
|
|
|
|
static spi_device_handle_t spi_handle;
|
|
static spi_device_handle_t spi_handle;
|
|
static TaskHandle_t fpga_task;
|
|
static TaskHandle_t fpga_task;
|
|
|
|
+static TimerHandle_t fpga_timesync_timer;
|
|
static SemaphoreHandle_t spi_mutex;
|
|
static SemaphoreHandle_t spi_mutex;
|
|
static EventGroupHandle_t spi_done_evgroup;
|
|
static EventGroupHandle_t spi_done_evgroup;
|
|
static volatile bool spi_abort_all;
|
|
static volatile bool spi_abort_all;
|
|
|
|
+static const volatile struct esplink_timesync *tsync_addr;
|
|
|
|
|
|
#define NOTIFY_INDEX 0
|
|
#define NOTIFY_INDEX 0
|
|
#define NOTIFY_FPGA (1 << 0)
|
|
#define NOTIFY_FPGA (1 << 0)
|
|
#define NOTIFY_ENABLE (1 << 1)
|
|
#define NOTIFY_ENABLE (1 << 1)
|
|
#define NOTIFY_DISABLE (1 << 2)
|
|
#define NOTIFY_DISABLE (1 << 2)
|
|
|
|
+#define NOTIFY_TIMESYNC (1 << 3)
|
|
#if 0
|
|
#if 0
|
|
#define NOTIFY_SPI (1 << 3)
|
|
#define NOTIFY_SPI (1 << 3)
|
|
#define NOTIFY_RINGBUF (1 << 4)
|
|
#define NOTIFY_RINGBUF (1 << 4)
|
|
@@ -89,7 +92,7 @@ static uint32_t notify_wait_for(uint32_t flags)
|
|
static void ARDUINO_ISR_ATTR fpga_notify_from_isr(uint32_t flags)
|
|
static void ARDUINO_ISR_ATTR fpga_notify_from_isr(uint32_t flags)
|
|
{
|
|
{
|
|
BaseType_t wakeup = pdFALSE;
|
|
BaseType_t wakeup = pdFALSE;
|
|
-
|
|
|
|
|
|
+
|
|
if (xTaskNotifyIndexedFromISR(fpga_task, NOTIFY_INDEX, flags, eSetBits,
|
|
if (xTaskNotifyIndexedFromISR(fpga_task, NOTIFY_INDEX, flags, eSetBits,
|
|
&wakeup) != pdFAIL)
|
|
&wakeup) != pdFAIL)
|
|
portYIELD_FROM_ISR(wakeup);
|
|
portYIELD_FROM_ISR(wakeup);
|
|
@@ -105,6 +108,12 @@ static void ARDUINO_ISR_ATTR fpga_interrupt(void)
|
|
fpga_notify_from_isr(NOTIFY_FPGA);
|
|
fpga_notify_from_isr(NOTIFY_FPGA);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void fpga_timesync_trigger(TimerHandle_t t)
|
|
|
|
+{
|
|
|
|
+ (void)t;
|
|
|
|
+ fpga_notify_from_task(NOTIFY_TIMESYNC);
|
|
|
|
+}
|
|
|
|
+
|
|
static void ARDUINO_ISR_ATTR spi_callback(spi_transaction_t *t)
|
|
static void ARDUINO_ISR_ATTR spi_callback(spi_transaction_t *t)
|
|
{
|
|
{
|
|
size_t flags = (size_t)t->user;
|
|
size_t flags = (size_t)t->user;
|
|
@@ -145,6 +154,10 @@ esp_err_t fpga_service_init(void)
|
|
FPGA_PRIORITY, &fpga_task) != pdPASS)
|
|
FPGA_PRIORITY, &fpga_task) != pdPASS)
|
|
return ESP_FAIL;
|
|
return ESP_FAIL;
|
|
|
|
|
|
|
|
+ fpga_timesync_timer =
|
|
|
|
+ null_check(xTimerCreate("rtc_sync", 17*configTICK_RATE_HZ,
|
|
|
|
+ pdTRUE, NULL, fpga_timesync_trigger));
|
|
|
|
+
|
|
esplink_init();
|
|
esplink_init();
|
|
|
|
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
@@ -154,7 +167,7 @@ esp_err_t fpga_service_init(void)
|
|
static bool fpga_link_enable(void)
|
|
static bool fpga_link_enable(void)
|
|
{
|
|
{
|
|
esp_err_t err;
|
|
esp_err_t err;
|
|
-
|
|
|
|
|
|
+
|
|
if (spi_handle)
|
|
if (spi_handle)
|
|
return true; /* Already started */
|
|
return true; /* Already started */
|
|
|
|
|
|
@@ -175,7 +188,7 @@ static bool fpga_link_enable(void)
|
|
goto release_bus_fail;
|
|
goto release_bus_fail;
|
|
|
|
|
|
xEventGroupClearBits(spi_done_evgroup, EVENT_ALL_BITS);
|
|
xEventGroupClearBits(spi_done_evgroup, EVENT_ALL_BITS);
|
|
-
|
|
|
|
|
|
+
|
|
pinMode(PIN_FPGA_INT, INPUT);
|
|
pinMode(PIN_FPGA_INT, INPUT);
|
|
attachInterrupt(PIN_FPGA_INT, fpga_interrupt, FALLING);
|
|
attachInterrupt(PIN_FPGA_INT, fpga_interrupt, FALLING);
|
|
|
|
|
|
@@ -193,7 +206,7 @@ free_bus_fail:
|
|
|
|
|
|
init_fail:
|
|
init_fail:
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
-
|
|
|
|
|
|
+
|
|
done:
|
|
done:
|
|
return !err;
|
|
return !err;
|
|
}
|
|
}
|
|
@@ -204,9 +217,9 @@ static void fpga_link_disable(void)
|
|
return; /* Already stopped */
|
|
return; /* Already stopped */
|
|
|
|
|
|
xEventGroupClearBits(fpga_service_evgroup, NOTIFY_ENABLE);
|
|
xEventGroupClearBits(fpga_service_evgroup, NOTIFY_ENABLE);
|
|
-
|
|
|
|
|
|
+
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
-
|
|
|
|
|
|
+
|
|
detachInterrupt(PIN_FPGA_INT);
|
|
detachInterrupt(PIN_FPGA_INT);
|
|
|
|
|
|
spi_device_release_bus(spi_handle);
|
|
spi_device_release_bus(spi_handle);
|
|
@@ -217,6 +230,8 @@ static void fpga_link_disable(void)
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void fpga_poll_set_time(void);
|
|
|
|
+
|
|
static bool fpga_online(void)
|
|
static bool fpga_online(void)
|
|
{
|
|
{
|
|
struct esplink_head head;
|
|
struct esplink_head head;
|
|
@@ -239,26 +254,26 @@ static bool fpga_online(void)
|
|
head.board.major, head.board.minor,
|
|
head.board.major, head.board.minor,
|
|
head.board.fixes, head.board.fpga);
|
|
head.board.fixes, head.board.fpga);
|
|
|
|
|
|
- if (((size_t)head.signature_len - 1) >= 127)
|
|
|
|
- return false;
|
|
|
|
-
|
|
|
|
- char signature_string[head.signature_len+1];
|
|
|
|
- fpga_io_read(0, head.signature,
|
|
|
|
- signature_string, head.signature_len);
|
|
|
|
- signature_string[head.signature_len] = '\0';
|
|
|
|
- fpga_io_write(0, (char *)head.signature + 9, "GUBBAR", 6);
|
|
|
|
|
|
+ tsync_addr = head.tsync;
|
|
|
|
|
|
- printf("[FPGA] online, signature \"%s\"\n", signature_string);
|
|
|
|
|
|
+ printf("[FPGA] online, signature \"%.*s\"\n",
|
|
|
|
+ (int)(sizeof head.signature - 1), head.signature);
|
|
esplink_start(&head);
|
|
esplink_start(&head);
|
|
|
|
|
|
setenv_bool("status.max80.fpga", true);
|
|
setenv_bool("status.max80.fpga", true);
|
|
xSemaphoreGiveRecursive(spi_mutex);
|
|
xSemaphoreGiveRecursive(spi_mutex);
|
|
|
|
+
|
|
|
|
+ xTimerStart(fpga_timesync_timer, portMAX_DELAY);
|
|
|
|
+ fpga_poll_set_time();
|
|
|
|
+
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
static void fpga_offline(void)
|
|
static void fpga_offline(void)
|
|
{
|
|
{
|
|
|
|
+ tsync_addr = NULL;
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
|
|
+ xTimerStop(fpga_timesync_timer, portMAX_DELAY);
|
|
setenv_bool("status.max80.fpga", false);
|
|
setenv_bool("status.max80.fpga", false);
|
|
esplink_start(NULL); /* Stop esplink */
|
|
esplink_start(NULL); /* Stop esplink */
|
|
}
|
|
}
|
|
@@ -273,13 +288,13 @@ esp_err_t fpga_iov(const struct fpga_iov *iov, size_t niov)
|
|
|
|
|
|
for (size_t i = 0; i < niov; i++) {
|
|
for (size_t i = 0; i < niov; i++) {
|
|
const struct fpga_iov *iv = &iov[i];
|
|
const struct fpga_iov *iv = &iov[i];
|
|
-
|
|
|
|
|
|
+
|
|
if (!iv->len && !(iv->cmd & FPGA_CMD_NULL))
|
|
if (!iv->len && !(iv->cmd & FPGA_CMD_NULL))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
spi_transaction_ext_t *t = &trans[ntrans];
|
|
spi_transaction_ext_t *t = &trans[ntrans];
|
|
memset(t, 0, sizeof *t);
|
|
memset(t, 0, sizeof *t);
|
|
-
|
|
|
|
|
|
+
|
|
t->base.flags =
|
|
t->base.flags =
|
|
SPI_TRANS_MODE_DIO |
|
|
SPI_TRANS_MODE_DIO |
|
|
SPI_TRANS_VARIABLE_DUMMY |
|
|
SPI_TRANS_VARIABLE_DUMMY |
|
|
@@ -310,7 +325,7 @@ esp_err_t fpga_iov(const struct fpga_iov *iov, size_t niov)
|
|
|
|
|
|
if (!ntrans)
|
|
if (!ntrans)
|
|
return ESP_OK;
|
|
return ESP_OK;
|
|
-
|
|
|
|
|
|
+
|
|
esp_err_t err = ESP_OK;
|
|
esp_err_t err = ESP_OK;
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
if (!spi_handle) {
|
|
if (!spi_handle) {
|
|
@@ -346,7 +361,7 @@ fail:
|
|
xSemaphoreGiveRecursive(spi_mutex);
|
|
xSemaphoreGiveRecursive(spi_mutex);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
esp_err_t fpga_io_write(unsigned int cmd, const void *addr,
|
|
esp_err_t fpga_io_write(unsigned int cmd, const void *addr,
|
|
const void *data, size_t len)
|
|
const void *data, size_t len)
|
|
{
|
|
{
|
|
@@ -373,6 +388,123 @@ esp_err_t fpga_io_read(unsigned int cmd, const void *addr,
|
|
return fpga_iov(&iov, 1);
|
|
return fpga_iov(&iov, 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * This should be executed after getting an EL_UIRQ_TIME notification;
|
|
|
|
+ * do this in polling mode for best latency.
|
|
|
|
+ */
|
|
|
|
+static void fpga_get_time(void)
|
|
|
|
+{
|
|
|
|
+ esp_err_t err;
|
|
|
|
+ struct tm tm;
|
|
|
|
+ struct timeval tv;
|
|
|
|
+ struct tsbuf {
|
|
|
|
+ /* These two words are the status word normally considered "dummy" */
|
|
|
|
+ uint16_t status;
|
|
|
|
+ uint16_t tick;
|
|
|
|
+
|
|
|
|
+ struct esplink_timesync_buf get;
|
|
|
|
+ } tsbuf;
|
|
|
|
+
|
|
|
|
+ if (!tsync_addr) {
|
|
|
|
+ fpga_io_status(FPGA_CMD_ACK(EL_UIRQ_TIME));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spi_transaction_ext_t trans;
|
|
|
|
+ memset(&trans, 0, sizeof trans);
|
|
|
|
+
|
|
|
|
+ trans.base.flags =
|
|
|
|
+ SPI_TRANS_MODE_DIO |
|
|
|
|
+ SPI_TRANS_VARIABLE_DUMMY |
|
|
|
|
+ SPI_TRANS_MULTILINE_CMD |
|
|
|
|
+ SPI_TRANS_MULTILINE_ADDR;
|
|
|
|
+ trans.base.rxlength = sizeof tsbuf << 3;
|
|
|
|
+ trans.base.rx_buffer = &tsbuf;
|
|
|
|
+
|
|
|
|
+ trans.base.addr = (size_t)&tsync_addr->get;
|
|
|
|
+ trans.base.cmd = FPGA_CMD_RD | FPGA_CMD_ACK(EL_UIRQ_TIME);
|
|
|
|
+
|
|
|
|
+ xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
|
|
+ err = spi_device_polling_transmit(spi_handle, &trans.base);
|
|
|
|
+ xSemaphoreGiveRecursive(spi_mutex);
|
|
|
|
+ if (err)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (time_net_sync_status)
|
|
|
|
+ return; /* Ignore time from RTC if SNTP active now */
|
|
|
|
+
|
|
|
|
+ tm.tm_sec = tsbuf.get.tm.sec2 << 1;
|
|
|
|
+ tm.tm_min = tsbuf.get.tm.min;
|
|
|
|
+ tm.tm_hour = tsbuf.get.tm.hour;
|
|
|
|
+ tm.tm_mday = tsbuf.get.tm.mday;
|
|
|
|
+ tm.tm_mon = tsbuf.get.tm.mon - 1;
|
|
|
|
+ tm.tm_year = tsbuf.get.tm.year + 80;
|
|
|
|
+ tm.tm_isdst = -1; /* Unknown */
|
|
|
|
+
|
|
|
|
+ /* The third term handles wraparounds due to delay in transit */
|
|
|
|
+ tv.tv_sec = mktime(&tm) + (tsbuf.tick >> 15) +
|
|
|
|
+ ((tsbuf.get.tick >= tsbuf.tick) << 1);
|
|
|
|
+ tv.tv_usec = (((uint32_t)tsbuf.tick << 17) * UINT64_C(1000000)) >> 32;
|
|
|
|
+
|
|
|
|
+ settimeofday(&tv, NULL);
|
|
|
|
+
|
|
|
|
+ print_time("[FPGA] Time set from RTC: ", &tv);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void fpga_poll_set_time(void)
|
|
|
|
+{
|
|
|
|
+ if (!tsync_addr)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (!time_net_sync_status) {
|
|
|
|
+ /* Poll for current time; will call fpga_get_time() later */
|
|
|
|
+ fpga_io_status(FPGA_CMD_IRQ(EL_DIRQ_TIME));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Otherwise transmit time to set the RTC */
|
|
|
|
+ esp_err_t err;
|
|
|
|
+ struct timeval tv;
|
|
|
|
+ struct esplink_timesync_buf tset;
|
|
|
|
+
|
|
|
|
+ spi_transaction_t trans;
|
|
|
|
+ memset(&trans, 0, sizeof trans);
|
|
|
|
+
|
|
|
|
+ tset.update = 1;
|
|
|
|
+
|
|
|
|
+ xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);
|
|
|
|
+
|
|
|
|
+ gettimeofday(&tv, NULL);
|
|
|
|
+ const struct tm *tm = localtime(&tv.tv_sec);
|
|
|
|
+
|
|
|
|
+ tset.tick = ((tv.tv_usec * ((1ULL << (32+15))/1000000+1)) >> 32)
|
|
|
|
+ + ((tv.tv_sec & 1) << 15);
|
|
|
|
+ tset.tm.sec2 = tm->tm_sec >> 1;
|
|
|
|
+ tset.tm.min = tm->tm_min;
|
|
|
|
+ tset.tm.hour = tm->tm_hour;
|
|
|
|
+ tset.tm.mday = tm->tm_mday;
|
|
|
|
+ tset.tm.mon = tm->tm_mon + 1;
|
|
|
|
+ tset.tm.year = tm->tm_year - 80;
|
|
|
|
+
|
|
|
|
+ trans.flags =
|
|
|
|
+ SPI_TRANS_MODE_DIO |
|
|
|
|
+ SPI_TRANS_MULTILINE_CMD |
|
|
|
|
+ SPI_TRANS_MULTILINE_ADDR;
|
|
|
|
+ trans.length = sizeof tset << 3;
|
|
|
|
+ trans.tx_buffer = &tset;
|
|
|
|
+
|
|
|
|
+ trans.addr = (size_t)&tsync_addr->set;
|
|
|
|
+ trans.cmd = FPGA_CMD_WR | FPGA_CMD_IRQ(EL_DIRQ_TIME) |
|
|
|
|
+ FPGA_CMD_ACK(EL_UIRQ_TIME);
|
|
|
|
+
|
|
|
|
+ err = spi_device_polling_transmit(spi_handle, &trans);
|
|
|
|
+ xSemaphoreGiveRecursive(spi_mutex);
|
|
|
|
+ if (err)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ print_time("[FPGA] RTC update: ", &tv);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Get status in polling mode (small transaction, < 256 CPU cycles).
|
|
* Get status in polling mode (small transaction, < 256 CPU cycles).
|
|
* cmd typically would be IRQ/ACK bits.
|
|
* cmd typically would be IRQ/ACK bits.
|
|
@@ -412,7 +544,7 @@ static void fpga_service_task(void *dummy)
|
|
} fpga_state = FPGA_DISABLED;
|
|
} fpga_state = FPGA_DISABLED;
|
|
|
|
|
|
fputs("[FPGA] Starting FPGA services task\n", stdout);
|
|
fputs("[FPGA] Starting FPGA services task\n", stdout);
|
|
-
|
|
|
|
|
|
+
|
|
while (1) {
|
|
while (1) {
|
|
uint32_t notifiers, status;
|
|
uint32_t notifiers, status;
|
|
|
|
|
|
@@ -431,16 +563,23 @@ static void fpga_service_task(void *dummy)
|
|
FPGA_CMD_IRQ(EL_DIRQ_HELLO));
|
|
FPGA_CMD_IRQ(EL_DIRQ_HELLO));
|
|
printf("[FPGA] FPGA status flags = 0x%08x\n", status);
|
|
printf("[FPGA] FPGA status flags = 0x%08x\n", status);
|
|
|
|
|
|
- if ((status & 0xf031) == 0x9030) {
|
|
|
|
- if (fpga_online())
|
|
|
|
- fpga_state = FPGA_ONLINE;
|
|
|
|
- } else if (digitalRead(PIN_FPGA_INT)) {
|
|
|
|
|
|
+ if (!digitalRead(PIN_FPGA_INT)) {
|
|
|
|
+ if (status & 0x40)
|
|
|
|
+ fpga_io_status(FPGA_CMD_ACK(2));
|
|
|
|
+ if (status & 0x80)
|
|
|
|
+ fpga_io_status(FPGA_CMD_ACK(3));
|
|
|
|
+ if ((status & 0xf031) == 0x9030) {
|
|
|
|
+ if (fpga_online())
|
|
|
|
+ fpga_state = FPGA_ONLINE;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE);
|
|
notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
|
|
|
|
case FPGA_ONLINE:
|
|
case FPGA_ONLINE:
|
|
- notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE);
|
|
|
|
|
|
+ notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE|
|
|
|
|
+ NOTIFY_TIMESYNC);
|
|
|
|
|
|
if (notifiers & NOTIFY_DISABLE) {
|
|
if (notifiers & NOTIFY_DISABLE) {
|
|
fpga_offline();
|
|
fpga_offline();
|
|
@@ -454,17 +593,20 @@ static void fpga_service_task(void *dummy)
|
|
fpga_offline();
|
|
fpga_offline();
|
|
printf("[FPGA] FPGA offline, status = 0x%08x\n", status);
|
|
printf("[FPGA] FPGA offline, status = 0x%08x\n", status);
|
|
fpga_state = FPGA_OFFLINE;
|
|
fpga_state = FPGA_OFFLINE;
|
|
|
|
+ notifiers = 0;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (status & 0x80)
|
|
|
|
+ fpga_get_time();
|
|
|
|
+
|
|
if (status & 0x40)
|
|
if (status & 0x40)
|
|
esplink_poll();
|
|
esplink_poll();
|
|
-
|
|
|
|
- if (status & 0x80) {
|
|
|
|
- fputs("[FPGA] invalid upstream interrupt 3\n", stdout);
|
|
|
|
- fpga_io_status(FPGA_CMD_ACK(3));
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (notifiers & NOTIFY_TIMESYNC)
|
|
|
|
+ fpga_poll_set_time();
|
|
|
|
+
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|