Skip to content

Commit

Permalink
Get SPI receive to start faster.
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
FozzTexx committed Sep 17, 2024
1 parent 4502472 commit 248d56d
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 45 deletions.
68 changes: 32 additions & 36 deletions lib/bus/iwm/iwm_ll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#include <string.h>

#include "esp_rom_gpio.h"
#include "soc/spi_periph.h"
#include <esp_rom_gpio.h>
#include <soc/spi_periph.h>

#include "iwm_ll.h"
#include "iwm.h"
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
{
//*****************************************************************************
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -346,26 +339,21 @@ 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!
#ifdef VERBOSE_IWM
// 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
Expand All @@ -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;

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 2 additions & 9 deletions lib/bus/iwm/iwm_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#include <hal/gpio_ll.h>
#endif
#include <driver/spi_master.h>
#include <freertos/semphr.h>

#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
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand Down
130 changes: 130 additions & 0 deletions lib/bus/iwm/spi_extensions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include "spi_extensions.h"

#include <esp_log.h>
#include <esp_private/spi_common_internal.h>
#include <hal/spi_hal.h>
#include <freertos/queue.h>

// 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)
{
SPI3.dma_conf.dma_rx_stop = 1;

/* Wait for transaction to come to a full and complete stop */
while (SPI3.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;
}
21 changes: 21 additions & 0 deletions lib/bus/iwm/spi_extensions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef _SPI_BETTER_H

#include "sdkconfig.h"

#include <driver/spi_master.h>
#include <soc/lldesc.h>

#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 */

0 comments on commit 248d56d

Please sign in to comment.