From 58c26a728c6b70d582f67a67f6c8d75e1661066b Mon Sep 17 00:00:00 2001 From: Chris Osborn Date: Mon, 16 Sep 2024 11:14:28 -0700 Subject: [PATCH 1/2] Get SPI receive to start faster. * Prepare linked list for SmartPort command packet and block packet in advance * Directly access SPI registers to start receive and avoid overhead/latency of ESPIDF SPI driver --- lib/bus/iwm/iwm_ll.cpp | 68 ++++++++--------- lib/bus/iwm/iwm_ll.h | 11 +-- lib/bus/iwm/spi_extensions.c | 138 +++++++++++++++++++++++++++++++++++ lib/bus/iwm/spi_extensions.h | 21 ++++++ 4 files changed, 193 insertions(+), 45 deletions(-) create mode 100644 lib/bus/iwm/spi_extensions.c create mode 100644 lib/bus/iwm/spi_extensions.h diff --git a/lib/bus/iwm/iwm_ll.cpp b/lib/bus/iwm/iwm_ll.cpp index b898c422e..84c28a138 100644 --- a/lib/bus/iwm/iwm_ll.cpp +++ b/lib/bus/iwm/iwm_ll.cpp @@ -4,8 +4,8 @@ #include -#include "esp_rom_gpio.h" -#include "soc/spi_periph.h" +#include +#include #include "iwm_ll.h" #include "iwm.h" @@ -22,6 +22,15 @@ #define IWM_BITLEN_FOR_BYTES(bytecount, freq, buffer) ({ \ (bytecount) * 8 * IWM_SAMPLES_PER_CELL(freq); \ }) +#define IWM_NUMBYTES_FOR_BITS(bitcount, buffer) ({ \ + size_t bufbits = sizeof(*(buffer)) * 8; \ + size_t blen = (bitcount) * \ + IWM_SAMPLES_PER_CELL(smartport.f_spirx); \ + blen = (blen + bufbits - 1) / bufbits; \ + blen; \ + }) + +#define IWM_CHUNK_SIZE 128 volatile uint8_t _phases = 0; volatile sp_cmd_state_t sp_command_mode = sp_cmd_state_t::standby; @@ -135,20 +144,6 @@ void IRAM_ATTR phi_isr_handler(void *arg) } } -inline void iwm_ll::iwm_extra_set() -{ -#ifdef EXTRA - IWM_BIT_SET(SP_EXTRA); -#endif -} - -inline void iwm_ll::iwm_extra_clr() -{ -#ifdef EXTRA - IWM_BIT_CLEAR(SP_EXTRA); -#endif -} - int IRAM_ATTR iwm_sp_ll::encode_spi_packet() { // clear out spi buffer @@ -180,7 +175,6 @@ int IRAM_ATTR iwm_sp_ll::encode_spi_packet() return j - 1; } - int IRAM_ATTR iwm_sp_ll::iwm_send_packet_spi() { //***************************************************************************** @@ -218,7 +212,9 @@ int IRAM_ATTR iwm_sp_ll::iwm_send_packet_spi() // send the data // TODO - enable / disable output using hasbuffer - no external tristate enable_output(); // enable the tri-state buffer + spi_end(); ret = spi_device_polling_transmit(spi, &trans); + spi_open_receive(spirx); disable_output(); // make rddata hi-z iwm_ack_clr(); assert(ret == ESP_OK); @@ -315,9 +311,6 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) fnTimer.reset(); - // signal the logic analyzer - iwm_extra_set(); - /* calculations for determining array sizes int numsamples = pulsewidth * (n + 2) * 8; command packet on DIY SP is 872 us long @@ -346,12 +339,6 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) // helps the IIgs get in sync to the bitstream quicker //memset(spi_buffer, 0xff, SPI_SP_LEN); - memset(&rxtrans, 0, sizeof(spi_transaction_t)); - rxtrans.rx_buffer = spi_buffer; - - // add 10% for overhead to accomodate YS command packet - rxtrans.rxlength = IWM_BITLEN_FOR_BYTES(packet_len * 11 / 10, f_spirx, spi_buffer); - // setup a timeout counter to wait for REQ response if (req_wait_for_rising_timeout(10000)) { // timeout! @@ -359,13 +346,14 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) // timeout Debug_print("t"); #endif - iwm_extra_clr(); return 1; // timeout waiting for REQ } - esp_err_t ret = spi_device_polling_start(spirx, &rxtrans, portMAX_DELAY); - assert(ret == ESP_OK); - iwm_extra_clr(); + lldesc_t *desc = sp_command_desc; + if (packet_len > COMMAND_PACKET_LEN) + desc = sp_block_desc; + spi_trigger_receive(spirx, desc, + IWM_BITLEN_FOR_BYTES(packet_len * 11 / 10, f_spirx, spi_buffer) - 1); // decode the packet here size_t spirx_bit_ctr = 0; // initialize the SPI buffer sampler @@ -387,8 +375,6 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) do // have_data { - iwm_extra_set(); // signal to LA we're in the nested loop - samples = spirx_bit_ctr - last_bit_pos; // difference since last time last_bit_pos = spirx_bit_ctr; @@ -476,7 +462,7 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) } } -void IRAM_ATTR iwm_sp_ll::spi_end() { spi_device_polling_end(spirx, portMAX_DELAY); }; +void IRAM_ATTR iwm_sp_ll::spi_end() { spi_close_receive(spirx); }; bool iwm_sp_ll::req_wait_for_falling_timeout(int t) { @@ -598,11 +584,22 @@ void iwm_sp_ll::setup_spi() esp_rom_gpio_connect_out_signal(PIN_SD_HOST_MOSI, spi_periph_signal[HSPI_HOST].spid_out, false, false); } - if (smartport.spiMutex == NULL) { - smartport.spiMutex = xSemaphoreCreateMutex(); + size_t length; + + + spi_open_receive(spirx); + + /* Setup linked lists for receiving the two different packet sizes */ + length = COMMAND_PACKET_LEN * 11 / 10; + length = IWM_NUMBYTES_FOR_BITS(length * 8, spi_buffer); + sp_command_desc = fspi_alloc_linked_list(spi_buffer, length, IWM_CHUNK_SIZE); + length = BLOCK_PACKET_LEN * 11 / 10; + length = IWM_NUMBYTES_FOR_BITS(length * 8, spi_buffer); + sp_block_desc = fspi_alloc_linked_list(spi_buffer, length, IWM_CHUNK_SIZE); } + return; } void iwm_ll::setup_gpio() @@ -649,7 +646,6 @@ void iwm_ll::setup_gpio() Debug_printf("\nEXTRA signaling line configured"); #endif - // attach the interrupt service routine gpio_isr_handler_add((gpio_num_t)SP_PHI0, phi_isr_handler, (void*) (gpio_num_t)SP_PHI0); gpio_isr_handler_add((gpio_num_t)SP_PHI1, phi_isr_handler, (void*) (gpio_num_t)SP_PHI1); diff --git a/lib/bus/iwm/iwm_ll.h b/lib/bus/iwm/iwm_ll.h index 27282fa25..12085ec8c 100644 --- a/lib/bus/iwm/iwm_ll.h +++ b/lib/bus/iwm/iwm_ll.h @@ -9,10 +9,9 @@ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include #endif -#include -#include #include "../../include/pinmap.h" +#include "spi_extensions.h" #include "fnRMTstream.h" // #define SPI_II_LEN 27000 // 200 ms at 1 mbps for disk ii + some extra @@ -187,8 +186,6 @@ class iwm_ll protected: // low level bit-banging i/o functions bool iwm_req_val() { return (IWM_BIT(SP_REQ)); }; - void iwm_extra_set(); - void iwm_extra_clr(); void disable_output(); void enable_output(); @@ -219,15 +216,11 @@ class iwm_sp_ll : public iwm_ll // const int f_spirx = APB_CLK_FREQ / 39; // 2051282 Hz or 2052kHz or 2.052 MHz - works for NTSC but ... const int f_spirx = APB_CLK_FREQ / 40; // 2 MHz - need slower rate for PAL - // SPI receiver data stream counters - int spirx_byte_ctr = 0; int spirx_bit_ctr = 0; - - //uint8_t packet_buffer[BLOCK_PACKET_LEN]; //smartport packet buffer uint16_t packet_len = 0; + lldesc_t *sp_command_desc, *sp_block_desc; public: - SemaphoreHandle_t spiMutex; // Phase lines and ACK handshaking void iwm_ack_set() { IWM_BIT_INPUT(SP_ACK); }; // disable the line so it goes hi-z void iwm_ack_clr() { IWM_BIT_OUTPUT(SP_ACK); }; // enable the line already set to low diff --git a/lib/bus/iwm/spi_extensions.c b/lib/bus/iwm/spi_extensions.c new file mode 100644 index 000000000..fa236a346 --- /dev/null +++ b/lib/bus/iwm/spi_extensions.c @@ -0,0 +1,138 @@ +#include "spi_extensions.h" + +#ifndef CONFIG_IDF_TARGET_ESP32S3 + +#include +#include +#include +#include + +// FIXME - structs that had to be copied from ESPIDF components/driver/spi_master.c +// these structs are needed in order to tear down a +// spi_device_handle_t to access the selected SPI peripheral +// registers + +// spi_device_t and spi_host_t reference each other +typedef struct spi_device_t spi_device_t; +//spi_device_handle_t is a pointer to spi_device_t + +typedef struct { + spi_transaction_t *trans; + const uint32_t *buffer_to_send; + uint32_t *buffer_to_rcv; +} spi_trans_priv_t; + +typedef struct { + int id; + spi_device_t *device[DEV_NUM_MAX]; + intr_handle_t intr; + spi_hal_context_t hal; + spi_trans_priv_t cur_trans_buf; + int cur_cs; + const spi_bus_attr_t *bus_attr; + spi_device_t *device_acquiring_lock; + + //debug information + bool polling; //in process of a polling, avoid of queue new transactions into ISR +} spi_host_t; + +struct spi_device_t { + int id; + QueueHandle_t trans_queue; + QueueHandle_t ret_queue; + spi_device_interface_config_t cfg; + spi_hal_dev_config_t hal_dev; + spi_host_t *host; + spi_bus_lock_dev_handle_t dev_lock; +}; + +// END spi_master.c structs + + + +/* Allocates a DMA linked list with each element pointing to a + successive section of buffer that is only chunk_size long. */ +lldesc_t *fspi_alloc_linked_list(uint8_t *buffer, size_t length, size_t chunk_size) +{ + uint32_t num_chunks, idx; + lldesc_t *llfirst, *llcur; + + + num_chunks = (length + chunk_size - 1) / chunk_size; + + llfirst = (lldesc_t *) heap_caps_malloc(sizeof(lldesc_t) * num_chunks, MALLOC_CAP_DMA); + if (!llfirst) { + ESP_LOGE("SPI_DMA", "Failed to allocate DMA descriptor"); + return 0; + } + + memset(llfirst, 0, sizeof(lldesc_t) * num_chunks); + for (idx = 0; idx < num_chunks; idx++) { + llcur = &llfirst[idx]; + llcur->size = chunk_size; + llcur->owner = 1; // Owned by DMA hardware + llcur->buf = &buffer[idx * chunk_size]; + llcur->qe.stqe_next = &llfirst[(idx + 1) % num_chunks]; + } + + /* Mark last element in linked list as last */ + llcur = &llfirst[num_chunks - 1]; + llcur->size = length - (num_chunks - 1) * chunk_size; + llcur->eof = 1; + llcur->qe.stqe_next = NULL; + + return llfirst; +} + +/* Gets SPI peripheral setup and ready to start capturing data with + spi_start_receive */ +void spi_open_receive(spi_device_handle_t handle) +{ + spi_transaction_t rxtrans; + + + /* Do a short poll to setup the SPI peripheral */ + memset(&rxtrans, 0, sizeof(spi_transaction_t)); + rxtrans.rxlength = 8; + rxtrans.rx_buffer = (uint8_t *) heap_caps_malloc(8, MALLOC_CAP_DMA); + ESP_ERROR_CHECK(spi_device_polling_start(handle, &rxtrans, portMAX_DELAY)); + spi_device_polling_end(handle, portMAX_DELAY); + heap_caps_free(rxtrans.rx_buffer); + + return; +} + +/* Waits for receive transaction to end and Puts SPI peripheral back + into a state that it will work with the ESPIDF drivers */ +void spi_close_receive(spi_device_handle_t handle) +{ + spi_host_t *host = handle->host; + spi_dev_t *hw = host->hal.hw; + + + hw->dma_conf.dma_rx_stop = 1; + + /* Wait for transaction to come to a full and complete stop */ + while (hw->ext2.st) + ; + + return; +} + +/* Starts SPI receive using the linked list `desc` */ +void spi_trigger_receive(spi_device_handle_t handle, lldesc_t *desc, size_t bit_length) +{ + spi_host_t *host = handle->host; + spi_dev_t *hw = host->hal.hw; + + + hw->dma_in_link.addr = (uint32_t) desc; + hw->dma_inlink_dscr = hw->dma_in_link.addr; + hw->miso_dlen.usr_miso_dbitlen = bit_length; + hw->dma_in_link.start = 1; + hw->cmd.usr = 1; + + return; +} + +#endif /* CONFIG_IDF_TARGET_ESP32S3 */ diff --git a/lib/bus/iwm/spi_extensions.h b/lib/bus/iwm/spi_extensions.h new file mode 100644 index 000000000..849639201 --- /dev/null +++ b/lib/bus/iwm/spi_extensions.h @@ -0,0 +1,21 @@ +#ifndef _SPI_BETTER_H + +#include "sdkconfig.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + lldesc_t *fspi_alloc_linked_list(uint8_t *buffer, size_t length, size_t chunk_size); + void spi_open_receive(spi_device_handle_t handle); + void spi_close_receive(spi_device_handle_t handle); + void spi_trigger_receive(spi_device_handle_t handle, lldesc_t *desc, size_t bit_length); + +#ifdef __cplusplus +} +#endif + +#endif /* _SPI_BETTER_H */ From 6c42627512c6e094bef0cb6d3b14aafc9e3d56d5 Mon Sep 17 00:00:00 2001 From: Chris Osborn Date: Wed, 25 Sep 2024 09:09:10 -0700 Subject: [PATCH 2/2] Increase iwm decode timeout from 19 to 23. --- lib/bus/iwm/iwm_ll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bus/iwm/iwm_ll.cpp b/lib/bus/iwm/iwm_ll.cpp index 84c28a138..b354c6635 100644 --- a/lib/bus/iwm/iwm_ll.cpp +++ b/lib/bus/iwm/iwm_ll.cpp @@ -431,7 +431,7 @@ int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(uint8_t* buffer, int packet_len) fnTimer.alarm_snooze( (samples * 10 * 1000 * 1000) / f_spirx); // samples * 10 /2 ); // snooze the timer based on the previous number of samples } - rxbyte = iwm_decode_byte(spi_buffer, SPI_SP_LEN, f_spirx, 19, &spirx_bit_ctr, &more_data); + rxbyte = iwm_decode_byte(spi_buffer, SPI_SP_LEN, f_spirx, 23, &spirx_bit_ctr, &more_data); if ((rxbyte == 0xc3) && (!synced)) {