Starting from: spi_new_trans
// The function is called to send a new transaction, in ISR or in the task.
// Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used)
static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf)
{
host->cur_cs = dev->id;
//Reconfigure according to device settings, the function only has effect when the dev_id is changed.
spi_setup_device(dev);
spi_setup_device
spi_setup_device (wish I could link from the summary)
// Setup the device-specified configuration registers. Called every time a new
// transaction is to be sent, but only apply new configurations when the device
// changes.
static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev) {
spi_bus_lock_dev_handle_t dev_lock = dev->dev_lock;
spi_hal_context_t *hal = &dev->host->hal;
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
if (spi_bus_lock_touch(dev_lock)) {
/* Configuration has not been applied yet. */
spi_hal_setup_device(hal, hal_dev);
}
}
Looks like it's a lazy initialization situation that stores back to the dev handle that I'd hold on to and pass back in when I want to use the SPI.
https://github.com/espressif/esp-idf/blob/df9310ada26123d8d478bcfa203f874d8c21d654/components/hal/spi_hal_iram.c#L31C1-L51
void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev)
{
//Configure clock settings
spi_dev_t *hw = hal->hw;
#if SOC_SPI_AS_CS_SUPPORTED
spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs);
#endif
spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs);
spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg);
spi_ll_set_clk_source(hw, dev->timing_conf.clock_source);
//Configure bit order
spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst);
spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst);
spi_ll_master_set_mode(hw, dev->mode);
//Configure misc stuff
spi_ll_set_half_duplex(hw, dev->half_duplex);
spi_ll_set_sio_mode(hw, dev->sio);
//Configure CS pin and timing
spi_ll_master_set_cs_setup(hw, dev->cs_setup);
spi_ll_master_set_cs_hold(hw, dev->cs_hold);
spi_ll_master_select_cs(hw, dev->cs_pin_id);
}
— https://github.com/espressif/esp-idf/blob/df9310ada26123d8d478bcfa203f874d8c21d654/components/hal/spi_hal_iram.c#L31-L52
maybe this is interesting?
static inline void spi_ll_set_half_duplex(spi_dev_t *hw, bool half_duplex)
{
hw->user.doutdin = !half_duplex;
}
...
* SIO is a mode which MOSI and MISO share a line. The device MUST work in half-duplexmode.
...
static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
{
hw->user.sio = sio_mode;
}
https://github.com/espressif/esp-idf/blob/df9310ada26123d8d478bcfa203f874d8c21d654/components/hal/esp32c3/include/hal/spi_ll.h#L546-L568
//set the transaction specific configuration each time before a transaction setup
spi_hal_trans_config_t hal_trans = {};
...
hal_trans setup
//set the transaction specific configuration each time before a transaction setup
spi_hal_trans_config_t hal_trans = {};
hal_trans.tx_bitlen = trans->length;
hal_trans.rx_bitlen = trans->rxlength;
hal_trans.rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv;
hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send;
hal_trans.cmd = trans->cmd;
hal_trans.addr = trans->addr;
hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0;
//Set up OIO/QIO/DIO if needed
hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1;
#if SOC_SPI_SUPPORT_OCT
if (trans->flags & SPI_TRANS_MODE_OCT) {
hal_trans.line_mode.data_lines = 8;
}
#endif
hal_trans.line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans.line_mode.data_lines : 1;
hal_trans.line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans.line_mode.data_lines : 1;
if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
} else {
hal_trans.cmd_bits = dev->cfg.command_bits;
}
if (trans->flags & SPI_TRANS_VARIABLE_ADDR) {
hal_trans.addr_bits = ((spi_transaction_ext_t *)trans)->address_bits;
} else {
hal_trans.addr_bits = dev->cfg.address_bits;
}
if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) {
hal_trans.dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits;
} else {
hal_trans.dummy_bits = dev->cfg.dummy_bits;
}
— https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/driver/spi/gpspi/spi_master.c#L598-L633
spi_hal_setup_trans(hal, hal_dev, &hal_trans);
spi_hal_prepare_data(hal, hal_dev, &hal_trans);
//Call pre-transmission callback, if any
if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
//Kick off transfer
spi_hal_user_start(hal);
Flattening:
// spi_hal_setup_trans(hal, hal_dev, &hal_trans);
{
hw->dma_int_raw.trans_done = 0; // spi_ll_clear_int_stat
((void)(hw->cmd.val == 0)); // via HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0)
// spi_ll_master_set_line_mode
{
hw->ctrl.val &= ~(SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_QUAD | SPI_FADDR_DUAL);
hw->user.val &= ~(SPI_FWRITE_QUAD | SPI_FWRITE_DUAL);
hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->user.fwrite_quad = (line_mode.data_lines == 4);
} // — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L576C20-L588
// extra_dummy applies to rx, not tx, ignoring...
// spi_ll_set_dummy(hw, (extra_dummy = 0) + trans->dummy_bits);
{
hw->user.usr_dummy = dummy_n ? 1 : 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user1, usr_dummy_cyclelen, dummy_n - 1);
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L950
// uint32_t miso_delay_num = 0;
// uint32_t miso_delay_mode = 0;
// ...
// spi_ll_set_miso_delay(...)
// skip this whole section, since the esp32c3 doesn't do anything with it
{
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L775-L778
// spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen);
{
if (bitlen > 0) {
hw->ms_dlen.ms_data_bitlen = bitlen - 1;
}
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L816
// if (dev->half_duplex) { .. } else { .. }
// both branches are
// spi_ll_set_miso_bitlen(...)
{
if (bitlen > 0) {
hw->ms_dlen.ms_data_bitlen = bitlen - 1;
}
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L830-L834
// kind of skipping these, since we're not using them
// but they still do write to registers, so they take time
// ...
// spi_ll_set_addr_bitlen(..)
// spi_ll_set_command_bitlen(..)
hw->user1.usr_addr_bitlen = bitlen - 1;
hw->user.usr_addr = bitlen ? 1 : 0;
hw->user2.usr_command_bitlen = bitlen - 1;
hw->user.usr_command = bitlen ? 1 : 0;
// spi_ll_set_command(..)
// spi_ll_set_address(..)
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user2, usr_command_value, /* cmd */);
hw->addr = /* addr */;
// spi_ll_master_keep_cs
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
//Save the transaction attributes for internal usage.
memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t));
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/spi_hal_iram.c#L54
// spi_hal_prepare_data(hal, hal_dev, &hal_trans);
{
// skipping the rcv_buffer thing and CONFIG_IDF_TARGET_ESP32
// if (trans->rcv_buffer) { ... }
// if (trans->send_buffer) { if (!hal->dma_enabled) { ... } else { ... } }
// assuming (hal->dma_enabled) ...
// lldesc_setup_link(hal->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false);
// -> lldesc_setup_link_constrained(out_desc_array, buffer, size, LLDESC_MAX_NUM_PER_DESC, isrx)
{ /* populates hal->dmadesc_tx */ }
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/soc/lldesc.c#L3
// spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan);
// -> gdma_ll_tx_reset_channel(&GDMA, chan);
{
dev->channel[channel].out.out_conf0.out_rst = 1;
dev->channel[channel].out.out_conf0.out_rst = 0;
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/gdma_ll.h#L351-L354
// spi_ll_dma_tx_fifo_reset(hal->hw);
{
hw->dma_conf.dma_afifo_rst = 1;
hw->dma_conf.dma_afifo_rst = 0;
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L277-L280
// spi_ll_outfifo_empty_clr(hal->hw);
{
hw->dma_int_clr.outfifo_empty_err = 1;
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L309-L311C2
// spi_ll_dma_tx_enable(hal->hw, 1);
{
hw->dma_conf.dma_tx_ena = 1;
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#LL334C1-L337C1
// spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, hal->dmadesc_tx);
// -> gdma_ll_tx_set_desc_addr(&GDMA, chan, (uint32_t)addr);
// gdma_ll_tx_start(&GDMA, chan);
{
dev->channel[channel].out.out_link.addr = addr; // gdma_ll_tx_set_desc_addr
dev->channel[channel].out.out_link.start = 1; // gdma_ll_tx_start
}
// if (.. || trans->send_buffer) { .. } else { ... }
hw->user.usr_mosi = 1; // spi_ll_enable_mosi(hw, 1);
hw->user.usr_miso = 0; // spi_ll_enable_miso(hw, 0);
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/spi_hal_iram.c#L170-L174
// dev->cfg.pre_cb is user-supplied and not mandatory; ignoring it
// if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
// spi_hal_user_start(hal)
// -> spi_ll_master_user_start(hal->hw)
{
hw->cmd.update = 1;
while (hw->cmd.update);
hw->cmd.usr = 1;
}
// — https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/esp32c3/include/hal/spi_ll.h#L205C20-L210
HAL_ASSERT: https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/platform_port/include/hal/assert.h#L35-L41
looks like it always evaluates the expression, just throws it away instead of checking when asserts are off
HAL_FORCE_MODIFY_U32_REG_FIELD: https://github.com/espressif/esp-idf/blob/56123c52aaa08f1b53350c7af30c91320b352ef4/components/hal/platform_port/include/hal/misc.h#L30
Macro to force a 32-bit read, modify, then write on a peripheral register (b/c of gcc compiler bug, looks like)
dma -> gdma mapping: looks like the esp32 lacked GDMA so it's got SPI-specific DMA stuff going on