Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve reliability of SmartPort packet receive #794

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 33 additions & 37 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 @@ -445,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))
{
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
138 changes: 138 additions & 0 deletions lib/bus/iwm/spi_extensions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include "spi_extensions.h"

#ifndef CONFIG_IDF_TARGET_ESP32S3

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