Page 1 of 1

How to receive spi transmission inside a defined struct, order of MSB and LSB does not match

Posted: Tue Sep 12, 2023 10:57 pm
by lukilukeskywalker
I'm working on a project involving an ESP32 interfacing with an FPGA. I've run into an issue when transferring ADC data from the FPGA to the ESP32 using SPI. I have developed a c library that contains a few structs like this:

Code: Select all

typedef uint16_t adc_ch_sample_t;
typedef struct{
    adc_ch_sample_t INA1;
    adc_ch_sample_t INA2;
    adc_ch_sample_t INB1;
    adc_ch_sample_t INB2;
}ssfpga_adc_sample_t;
Problem:
FPGA sends data in this order via SPI: Sample0_MSB, Sample0_LSB, Sample1_MSB, Sample1_LSB, ... Lets say, that the fpga sends over spi the following array = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
When I store this data in my C struct (ssfpga_adc_sample_t), the order changes unexpectedly:
Sample1_MSB, Sample0_LSB, Sample2_MSB, Sample1_LSB, ... (So, what I see is 0x4523, 0x8967, 0xcdab, 0x01ef)

Questions:
Why does the order of MSB change when I store data in my struct?
How can I ensure that the data is correctly arranged within the struct?

Any insights or solutions would be greatly appreciated!
The code that receives the samples looks like:

Code: Select all

ssfpga_adc_sample_t *ssfpga_get_adc_sample(const ssfpga_t *ssfpga, const uint8_t adc){
    ssfpga_adc_sample_t *adc_sample = calloc(1, sizeof(ssfpga_adc_sample_t));
    ssfpga->spi_duplex_op(ssfpga->handle, SPI_READ_MULT_BYTES(ADC0_INA1_MSB)+adc*8, adc_sample, NULL, sizeof(ssfpga_adc_sample_t)/sizeof(uint8_t));
    return adc_sample;
}
And the code that performs the spi tranmisions looks like:

Code: Select all

esp_err_t fpga_spi_duplex_op(const void *handle, uint16_t reg, void *rx_data, void *tx_data, const size_t data_size){
    uint8_t free_mem = 0;
    if(tx_data == NULL){
        free_mem = 1;
        tx_data = calloc(data_size, sizeof(uint8_t));
    }
    spi_device_handle_t *spi_handle_internal = (spi_device_handle_t *)handle;
    spi_transaction_t t = {
        .addr = reg, 
        .length = 8 * data_size,
        .rx_buffer = rx_data,
        .tx_buffer = tx_data,
        .flags = 0
    };
    esp_err_t ret = spi_device_polling_transmit(*spi_handle_internal, &t);
    if(free_mem)
        free(tx_data);
    return ret;
}
(FPGA Addresses are accessed over macros and defined addresses)

Re: How to receive spi transmission inside a defined struct, order of MSB and LSB does not match

Posted: Wed Sep 13, 2023 3:30 pm
by MicroController
Sample1_MSB, Sample0_LSB, Sample2_MSB, Sample1_LSB, ... (So, what I see is 0x4523, 0x8967, 0xcdab, 0x01ef)
This first byte seems to be lost. Possibly because it is already sent by the FPGA while the ESP is in the "address" phase of the transaction. Do away with the "addr" of the SPI transaction and include the address in the TX data.
Questions:
Why does the order of MSB change when I store data in my struct?
Because the ESPs are "little-endian" machines, while the FPGA sends "big-endian" data.
How can I ensure that the data is correctly arranged within the struct?
After receiving, but before using, you have to reverse the byte order of every 16-bit value in software; either "manually" or by using e.g. __builtin_bswap16() or ntohs().

Re: How to receive spi transmission inside a defined struct, order of MSB and LSB does not match

Posted: Wed Sep 13, 2023 7:23 pm
by lukilukeskywalker
This first byte seems to be lost. Possibly because it is already sent by the FPGA while the ESP is in the "address" phase of the transaction. Do away with the "addr" of the SPI transaction and include the address in the TX data.
Hmm, that was indeed the reason...
The FPGA code is designed to be little endian, but in my library I set the address to be the MSB byte, so, the FPGA sends the MSB byte when asked... My mistake :oops:
Thanks for pointing me in the right drection