diff --git a/CMakeLists.txt b/CMakeLists.txt index 59d0626..b527639 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ if(FAMILY STREQUAL "rp2040") else() #pico_enable_stdio_uart(${PROJECT} 1) endif() + # TODO: separate flag for disabling this one? pico_enable_stdio_uart(${PROJECT} 1) pico_enable_stdio_usb(${PROJECT} 0) else() @@ -96,7 +97,8 @@ target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_jscan/jscan_hw.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_sump/sump_hw.c - ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/base.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_hw.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_proto.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/uart.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/mpsse.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/asyncbb.c diff --git a/bsp/rp2040/bsp-info.h b/bsp/rp2040/bsp-info.h index f1a7a04..bb5533e 100644 --- a/bsp/rp2040/bsp-info.h +++ b/bsp/rp2040/bsp-info.h @@ -20,7 +20,7 @@ #else #define CFG_TUD_CDC 2 #endif -#define CFG_TUD_VENDOR 2 +#define CFG_TUD_VENDOR 3 /* don't access storage for RAM-only builds */ #if !PICO_NO_FLASH diff --git a/bsp/rp2040/m_ftdi/ftdi_hw.c b/bsp/rp2040/m_ftdi/ftdi_hw.c new file mode 100644 index 0000000..dac32df --- /dev/null +++ b/bsp/rp2040/m_ftdi/ftdi_hw.c @@ -0,0 +1,315 @@ +// vim: set et: + +/* include order matters here */ +#include +#include +#include +#include +#include + +#include "m_ftdi/ftdi_hw.h" + +struct ftdi_hw itf_bsp_data[2]; + +static int piosm_to_dreq(int pio, int sm) { + return DREQ_PIO0_TX0 + pio * 8 + sm; +} + +static void ftdihw_dma_isr(); + +void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf) { + memset(fr, 0, sizeof *fr); + + fr->itf = itf; + fr->pio = ftdihw_itf_to_pio(itf); + fr->pinbase = ftdihw_itf_to_base(itf); + + fr->rx.prg_off = 0xff; + fr->rx.piosm = 0xff; + fr->rx.dmach = 0xff; + fr->tx.prg_off = 0xff; + fr->tx.piosm = 0xff; + fr->tx.dmach = 0xff; + + // we start with nothing to write out, but we can start filling a buffer already + fr->rx.dmabuf_dend = sizeof(fr->dma_in_buf) - 1; + + irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), false); + irq_set_exclusive_handler(DMA_IRQ_0 + (fr->itf->index & 1), ftdihw_dma_isr); + irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), true); +} +void ftdihw_deinit(struct ftdi_hw* hw) { + irq_set_enabled(DMA_IRQ_0 + (hw->itf->index & 1), false); + irq_remove_handler(DMA_IRQ_0 + (hw->itf->index & 1), ftdihw_dma_isr); +} + +bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* hw, const void* prg) { + int off, sm, dmach; + sm = pio_claim_unused_sm(hw->pio, false); + if (sm == -1) return false; + + dmach = dma_claim_unused_channel(false); + if (dmach == -1) { + pio_sm_unclaim(hw->pio, sm); + return false; + } + + if (!pio_can_add_program(hw->pio, prg)) { + dma_channel_unclaim(dmach); + pio_sm_unclaim(hw->pio, sm); + return false; + } + + off = pio_add_program(hw->pio, prg); + + ch->prg = prg; + ch->prg_off = off; + ch->piosm = sm; + ch->dmach = dmach; + + return true; +} +void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* ch, struct ftdi_hw* hw) { + dma_channel_unclaim(ch->dmach); + pio_sm_set_enabled(hw->pio, ch->piosm, false); + pio_sm_unclaim(hw->pio, ch->piosm); + pio_remove_program(hw->pio, ch->prg, ch->prg_off); + + ch->dmach = 0xff; + ch->piosm = 0xff; + ch->prg_off = 0xff; + ch->prg = NULL; +} + +void ftdihw_dma_rx_setup(struct ftdi_hw* hw, bool start) { + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false); + + if (hw->rx.dmabuf_dend == hw->rx.dmabuf_dstart) + --hw->rx.dmabuf_dend; // mod 256 automatically + + dma_channel_config dcfg = dma_channel_get_default_config(hw->rx.dmach); + channel_config_set_read_increment(&dcfg, false); + channel_config_set_write_increment(&dcfg, true); + channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->rx.piosm)); + channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8); + channel_config_set_ring(&dcfg, true, 8); // 1<<8 -sized ring buffer on write end + dma_channel_configure(hw->rx.dmach, &dcfg, + &hw->dma_in_buf[hw->rx.dmabuf_dstart], &hw->pio->rxf[hw->rx.piosm], + (hw->rx.dmabuf_dend - hw->rx.dmabuf_dstart) % sizeof(hw->dma_in_buf), start); + + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, true); +} +void ftdihw_dma_rx_stop(struct ftdi_hw* hw) { + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false); + dma_channel_abort(hw->rx.dmach); +} + +void ftdihw_dma_tx_setup(struct ftdi_hw* hw, bool start) { + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false); + + if (hw->tx.dmabuf_dend == hw->tx.dmabuf_dstart) + --hw->tx.dmabuf_dend; // mod 256 automatically + + dma_channel_config dcfg = dma_channel_get_default_config(hw->tx.dmach); + channel_config_set_read_increment(&dcfg, true); + channel_config_set_write_increment(&dcfg, false); + channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->tx.piosm)); + channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8); + channel_config_set_ring(&dcfg, false, 8); // 1<<8 -sized ring buffer on read end + dma_channel_configure(hw->tx.dmach, &dcfg, + &hw->pio->txf[hw->tx.piosm], &hw->dma_out_buf[hw->tx.dmabuf_dstart], + (hw->tx.dmabuf_dend - hw->tx.dmabuf_dstart) % sizeof(hw->dma_out_buf), start); + + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, true); +} +void ftdihw_dma_tx_stop(struct ftdi_hw* hw) { + dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false); + dma_channel_abort(hw->tx.dmach); +} + +size_t ftdihw_dma_read(struct ftdi_hw* hw, uint8_t* dest, size_t maxsize) { + // DMA is sending data from PIO to between dstart and dend, and is planned + // to continue up to dataend. when it finishes an xfer (between dstart and + // dend), it looks at dataend to see if it should continue sending more. if + // not, it pauses itself as tehre's no more work to be done, and sets a + // FIFO overrun flag in the FTDI error things + // + // here we read from dataend to dstart, careful not to touch the area the + // DMA is currently writing to (while holding the channel paused), and then + // moving the dataend marker. the channel is always reenabled, as either it + // was busy and now it has more space to put data in, or needs to be + // restarted as there is space now + // + // pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but + // nothing drastic + + if (maxsize == 0) return 0; + + size_t rv = 0; + + // TODO: make time between pause & resume shorter by moving stuff around? + bool wasbusy = dma_hw->ch[hw->rx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS; + hw_clear_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ) + + // dstart can get modified by the IRQ; IRQ reads dataend + uint8_t dstart = hw->rx.dmabuf_dstart, dataend = hw->rx.dmabuf_dataend; + if (dataend > dstart) dstart += sizeof(hw->dma_in_buf); + // nothing ready yet - bail out + if (dstart == (dataend + 1) % sizeof(hw->dma_in_buf)) { + goto END; + } + + __compiler_memory_barrier(); + + // copy from data in ringbuffer that was read in by PIO + rv = (size_t)dstart - (size_t)dataend - 1; + if (rv > maxsize) rv = maxsize; + for (size_t i = 0; i < rv; ++i) { + dest[i] = hw->dma_in_buf[(dataend + i) % sizeof(hw->dma_in_buf)]; + } + uint8_t dataend_new = (dataend + rv) % sizeof(hw->dma_in_buf); + + hw->rx.dmabuf_dataend = dataend_new; + + hw->itf->modemstat &= ~sio_modem_fifoerr; + +END: + if (!wasbusy) { + hw->rx.dmabuf_dstart = hw->rx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code + hw->rx.dmabuf_dend = hw->rx.dmabuf_dataend; + ftdihw_dma_rx_setup(hw, true); + } else // if already busy easlier, simply reenable + hw_set_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ) + return rv; + return rv; +} +bool ftdihw_dma_write(struct ftdi_hw* hw, const uint8_t* src, size_t size) { + // DMA is sending data between dstart and dend to PIO, and is planned to + // continue up to dataend. when it finishes an xfer (between dstart and + // dend), it looks at dataned to see if it should continue sending more. if + // not, it pauses itself as there's no more work to be done + // + // here we insert some data in the ring buffer, careful to not overwrite + // what the DMA is currently transferring (while holding the channel + // paused), and then moving the dataend marker. the channel is always + // reenabled, as either it was busy and now has more work to do, or needs + // to be restarted as there's data now + // + // pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but + // nothing drastic + + if (size >= 255) return false; // don't even bother + + bool rv = true; + + // TODO: make time between pause & resume shorter by moving stuff around? + bool wasbusy = dma_hw->ch[hw->tx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS; + hw_clear_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ) + + // dstart can get modified by the IRQ; IRQ reads dataend + uint8_t dstart = hw->tx.dmabuf_dstart, dataend = hw->tx.dmabuf_dataend; + if (dataend > dstart) dstart += sizeof(hw->dma_out_buf); + // no space to put it in - overrun error + if ((size_t)dstart - (size_t)dataend < size + 1) { + hw->itf->modemstat |= sio_modem_fifoerr; + rv = false; + goto END; + } + + __compiler_memory_barrier(); + + // copy data to buffer, to be copied next + for (size_t i = 0; i < size; ++i) { + hw->dma_out_buf[(dataend + i) % sizeof(hw->dma_out_buf)] = src[i]; + } + uint8_t dataend_new = (dataend + size) % sizeof(hw->dma_out_buf); + + hw->tx.dmabuf_dataend = dataend_new; + + hw->itf->modemstat &= ~sio_modem_temt; + +END: + if (!wasbusy) { + hw->tx.dmabuf_dstart = hw->tx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code + hw->tx.dmabuf_dend = hw->tx.dmabuf_dataend; + ftdihw_dma_tx_setup(hw, true); + } else // if already busy easlier, simply reenable + hw_set_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ) + return rv; +} + +void ftdihw_dma_rx_flush(struct ftdi_hw* hw) { + ftdihw_dma_rx_stop(hw); + hw->rx.dmabuf_dstart = 0; + hw->rx.dmabuf_dend = 0; + hw->rx.dmabuf_dataend = 0; +} +void ftdihw_dma_tx_flush(struct ftdi_hw* hw) { + ftdihw_dma_tx_stop(hw); + hw->tx.dmabuf_dstart = 0; + hw->tx.dmabuf_dend = 0; + hw->tx.dmabuf_dataend = 0; +} + +static void ftdihw_dma_rx_irq(struct ftdi_hw* hw) { + // an rx DMA transfer has finished. if in the meantime (between DMA xfer + // start and now) more room has become available, restart the DMA channel + // to write to the new space instead. otherwise, if there's no more space + // left, we're in a data overrun condition, so don't restart, and set the + // relevant FTDI error flags + + uint8_t dend = hw->rx.dmabuf_dend, dataend = hw->rx.dmabuf_dataend; + + if (dend == dataend) { + // data overrun, stop stuff (until read() restarts the DMA) + hw->itf->modemstat |= sio_modem_fifoerr; + } else { + hw->rx.dmabuf_dstart = dend; + hw->rx.dmabuf_dend = dataend; + ftdihw_dma_rx_setup(hw, true); + } +} +static void ftdihw_dma_tx_irq(struct ftdi_hw* hw) { + // a tx DMA transfer has finished. if in the meantile (between DMA xfer + // start and now) more data has become available, restart the DMA channel + // to read from the new data instead. otherwise, if there's no more data to + // be read, we have nothing to do and we can leave the DMA channel in its + // idle state. + + uint8_t dend = hw->tx.dmabuf_dend, dataend = hw->tx.dmabuf_dataend; + + if (dend == dataend) { + // nothing to do + hw->itf->modemstat |= sio_modem_temt; + } else { + hw->tx.dmabuf_dstart = dend; + hw->tx.dmabuf_dend = dataend; + ftdihw_dma_tx_setup(hw, true); + } +} + +static void ftdihw_dma_isr() { + // interrupt service routine: dispatch to functions above depending on INTS0 + uint32_t flags = dma_hw->ints0; + uint32_t allflg = 1u << itf_bsp_data[0].rx.dmach; + allflg |= 1u << itf_bsp_data[0].tx.dmach; + allflg |= 1u << itf_bsp_data[1].rx.dmach; + allflg |= 1u << itf_bsp_data[1].tx.dmach; + + for (size_t i = 0; (i < 2) && (flags & allflg); ++i) { + uint32_t flg = 1u << itf_bsp_data[i].rx.dmach; + if (flags & flg) { + flags &= ~flg; + dma_hw->ints0 = flg; // ack int + ftdihw_dma_rx_irq(&itf_bsp_data[i]); + } + + flg = 1u << itf_bsp_data[i].tx.dmach; + if (flags & flg) { + flags &= ~flg; + dma_hw->ints0 = flg; // ack int + ftdihw_dma_tx_irq(&itf_bsp_data[i]); + } + } +} + diff --git a/bsp/rp2040/m_ftdi/ftdi_hw.h b/bsp/rp2040/m_ftdi/ftdi_hw.h new file mode 100644 index 0000000..7796324 --- /dev/null +++ b/bsp/rp2040/m_ftdi/ftdi_hw.h @@ -0,0 +1,73 @@ +// vim: set et: + +#ifndef FTDI_BASE_H_ +#define FTDI_BASE_H_ + +#include +#include + +#include "m_ftdi/pinout.h" +#include "m_ftdi/ftdi.h" + +struct ftdi_hw_ch { + const void* prg; + + uint8_t prg_off, piosm, dmach; + + volatile uint8_t dmabuf_dstart, dmabuf_dend, dmabuf_dataend; +}; + +struct ftdi_hw { + struct ftdi_interface* itf; + PIO pio; + + struct ftdi_hw_ch rx, tx; + uint8_t pinbase; + + volatile uint8_t dma_in_buf[256/*CFG_TUD_VENDOR_TX_BUFSIZE*/]; + volatile uint8_t dma_out_buf[256/*CFG_TUD_VENDOR_RX_BUFSIZE*/]; +}; + +extern struct ftdi_hw itf_bsp_data[2]; + +void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf); +void ftdihw_deinit(struct ftdi_hw* fr); + +bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* fr, const void* prg); +void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* fr, struct ftdi_hw* hw); + +void ftdihw_dma_rx_setup(struct ftdi_hw* fr, bool start); +void ftdihw_dma_rx_stop(struct ftdi_hw* fr); + +void ftdihw_dma_tx_setup(struct ftdi_hw* fr, bool start); +void ftdihw_dma_tx_stop(struct ftdi_hw* fr); + +size_t ftdihw_dma_read(struct ftdi_hw* fr, uint8_t* dest, size_t maxsize); +bool ftdihw_dma_write(struct ftdi_hw* fr, const uint8_t* src, size_t size); + +void ftdihw_dma_rx_flush(struct ftdi_hw* fr); +void ftdihw_dma_tx_flush(struct ftdi_hw* fr); + +static inline int ftdihw_idx_to_base(int itf_idx) { + return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE; +} +static inline int ftdihw_itf_to_base(struct ftdi_interface* itf) { + return ftdihw_idx_to_base(itf->index); +} + +static inline PIO ftdihw_idx_to_pio(int itf_idx) { + return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO; +} +static inline PIO ftdihw_itf_to_pio(struct ftdi_interface* itf) { + return ftdihw_idx_to_pio(itf->index); +} + +static inline struct ftdi_hw* ftdihw_idx_to_hw(int itf_idx) { + return &itf_bsp_data[itf_idx & 1]; +} +static inline struct ftdi_hw* ftdihw_itf_to_hw(struct ftdi_interface* itf) { + return ftdihw_idx_to_hw(itf->index); +} + +#endif + diff --git a/bsp/rp2040/m_ftdi/base.c b/bsp/rp2040/m_ftdi/ftdi_proto.c similarity index 91% rename from bsp/rp2040/m_ftdi/base.c rename to bsp/rp2040/m_ftdi/ftdi_proto.c index 026dd63..a97b441 100644 --- a/bsp/rp2040/m_ftdi/base.c +++ b/bsp/rp2040/m_ftdi/ftdi_proto.c @@ -1,6 +1,9 @@ // vim: set et: +#include + #include "m_ftdi/ftdi.h" +#include "m_ftdi/ftdi_hw.h" static void init_mode(struct ftdi_interface* itf, enum ftdi_mode mode) { switch (mode) { @@ -28,10 +31,18 @@ static void deinit_mode(struct ftdi_interface* itf, enum ftdi_mode mode) { } void ftdi_if_init(struct ftdi_interface* itf) { + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); + + ftdihw_init(hw, itf); + init_mode(itf, ftdi_if_get_mode(itf)); } void ftdi_if_deinit(struct ftdi_interface* itf) { deinit_mode(itf, ftdi_if_get_mode(itf)); + + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); + + ftdihw_deinit(hw); } void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data) { @@ -78,8 +89,12 @@ void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask, } void ftdi_if_sio_reset(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ } -void ftdi_if_sio_tciflush(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ } -void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ } +void ftdi_if_sio_tciflush(struct ftdi_interface* itf) { + (void)itf; /* TODO: ? */ +} +void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) { + (void)itf; /* TODO: ? */ +} void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency) { (void)itf; (void)latency; /* TODO: ? */ } uint8_t ftdi_if_get_latency(struct ftdi_interface* itf) { return itf->latency; /* TODO: ? */ } diff --git a/bsp/rp2040/m_ftdi/pinout.h b/bsp/rp2040/m_ftdi/pinout.h index c0723af..c6278e2 100644 --- a/bsp/rp2040/m_ftdi/pinout.h +++ b/bsp/rp2040/m_ftdi/pinout.h @@ -2,6 +2,8 @@ #ifndef BSP_PINOUT_M_FTDI_H_ #define BSP_PINOUT_M_FTDI_H_ +/* NOTE: no C code here! needs to be include-able from PIO asm! */ + #define PINOUT_ITF_A_BASE 2 #define PINOUT_ITF_B_BASE 14 @@ -113,21 +115,5 @@ #define PINOUT_CPUFIFO_nRD_OFF 10 #define PINOUT_CPUFIFO_nWR_OFF 11 -struct ftdi_interface; - -static inline int PINOUT_idx_to_base(int itf_idx) { - return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE; -} -static inline int PINOUT_itf_to_base(struct ftdi_interface* itf) { - return PINOUT_idx_to_base(*(int*)itf); // can't access "index" directly here, so, shrug -} - -static inline void* PINOUT_idx_to_pio(int itf_idx) { - return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO; -} -static inline void* PINOUT_itf_to_pio(struct ftdi_interface* itf) { - return PINOUT_idx_to_pio(*(int*)itf); // can't access "index" directly here, so, shrug -} - #endif diff --git a/bsp/rp2040/m_ftdi/uart.c b/bsp/rp2040/m_ftdi/uart.c index 961d032..a77fade 100644 --- a/bsp/rp2040/m_ftdi/uart.c +++ b/bsp/rp2040/m_ftdi/uart.c @@ -1,23 +1,18 @@ // vim: set et: -#include "m_ftdi/ftdi.h" - #include #include #include -#include #include #include "m_ftdi/pinout.h" +#include "m_ftdi/ftdi_hw.h" + #include "ftdi_uart_rx.pio.h" #include "ftdi_uart_tx.pio.h" -struct chdat { - uint8_t off, sm, dmach; -}; struct uart_state { uint32_t baudrate; - struct chdat rx, tx; bool enabled; }; @@ -28,113 +23,83 @@ static struct uart_state state[2] = { #define STATEOF(itf) (state[(itf)->index & 1]) -static bool init_sm(PIO pio, struct chdat* d, const pio_program_t* prg) { - int off, sm, dmach; - sm = pio_claim_unused_sm(pio, false); - if (sm == -1) return false; - - dmach = dma_claim_unused_channel(false); - if (dmach == -1) { - pio_sm_unclaim(pio, sm); - return false; - } - - if (!pio_can_add_program(pio, prg)) { - dma_channel_unclaim(dmach); - pio_sm_unclaim(pio, sm); - return false; - } - - off = pio_add_program(pio, prg); - - d->off = off; - d->sm = sm; - d->dmach = dmach; - - return true; -} -static void deinit_sm(PIO pio, struct chdat* d, const pio_program_t* prg) { - dma_channel_unclaim(d->dmach); - pio_sm_set_enabled(pio, d->sm, false); - pio_sm_unclaim(pio, d->sm); - pio_remove_program(pio, prg, d->off); -} - -static void setup_dmach(struct ftdi_interface* itf, struct chdat* d) { - // set up PIO->dma_in_buf DMA: - // * src=pio dst=buf 8bit 256words pacing=pio - // * IRQ: set overrun bit - // * start it - // * SETUP AT MODE ENTER, STOP AT MODE EXIT - // - // set up dma_out_buf->PIO DMA: - // * src=buf dst=pio 8bit words pacing=pio - // * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below) - // * DO NOT start it on mode enter! - // * STOP AT MODE EXIT - // - // read routine: - // * abort DMA - // * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes) - // * resetup & start DMA: - // - // write routine: - // * if DMA running: set overrun bit, bail out? - // * should use ringbuffer-like structure - // * pointers: dma start, dma end, data end (after dma, contiguous) - // * dma end can be calculated from DMA MMIO, but, race conditions so no - // * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually) - // * do not start next block if data end == dma start - // * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad - // * only bail out when data end == dma start - 1 - // * copy data to dma_out_buf - // * set up & start DMA - // - // * what with buffers larger than 256 bytes? - // * what is the actual FTDI buffer size?? - // * which bits get set on errors? - // * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto - // handling buffers? -} -static void stop_dmach(struct chdat* d) { - -} +// set up PIO->dma_in_buf DMA: +// * src=pio dst=buf 8bit 256words pacing=pio +// * IRQ: set overrun bit +// * start it +// * SETUP AT MODE ENTER, STOP AT MODE EXIT +// +// set up dma_out_buf->PIO DMA: +// * src=buf dst=pio 8bit words pacing=pio +// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below) +// * DO NOT start it on mode enter! +// * STOP AT MODE EXIT +// +// read routine: +// * abort DMA +// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes) +// * resetup & start DMA: +// +// write routine: +// * if DMA running: set overrun bit, bail out? +// * should use ringbuffer-like structure +// * pointers: dma start, dma end, data end (after dma, contiguous) +// * dma end can be calculated from DMA MMIO, but, race conditions so no +// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually) +// * do not start next block if data end == dma start +// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad +// * only bail out when data end == dma start - 1 +// * copy data to dma_out_buf +// * set up & start DMA +// +// * what with buffers larger than 256 bytes? +// * just drop for now ig +// * what is the actual FTDI buffer size?? +// * device-dependent so aaaa +// * which bits get set on errors? +// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto +// handling buffers? void ftdi_if_uart_init(struct ftdi_interface* itf) { if (STATEOF(itf).enabled) return; // shrug - PIO pio = PINOUT_itf_to_pio(itf); - int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF, - pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF; + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); - if (!init_sm(pio, &STATEOF(itf).rx, &ftdi_uart_rx_program)) return; - if (!init_sm(pio, &STATEOF(itf).tx, &ftdi_uart_tx_program)) return; + if (!ftdihw_dma_ch_init(&hw->rx, hw, &ftdi_uart_rx_program)) return; + if (!ftdihw_dma_ch_init(&hw->tx, hw, &ftdi_uart_tx_program)) { + ftdihw_dma_ch_deinit(&hw->rx, hw); + return; + } - ftdi_uart_rx_program_init(pio, STATEOF(itf).rx.sm, STATEOF(itf).rx.off, + int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF, + pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF; + + ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, STATEOF(itf).baudrate); - ftdi_uart_tx_program_init(pio, STATEOF(itf).tx.sm, STATEOF(itf).tx.off, + ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, STATEOF(itf).baudrate); gpio_set_function(pin_rx, GPIO_FUNC_PIO0 + itf->index); gpio_set_function(pin_tx, GPIO_FUNC_PIO0 + itf->index); - /*dma_channel_start(STATEOF(itf).rx.dmach); - dma_channel_start(STATEOF(itf).tx.dmach);*/ + ftdihw_dma_rx_setup(hw, true); + ftdihw_dma_tx_setup(hw, false); STATEOF(itf).enabled = true; } void ftdi_if_uart_deinit(struct ftdi_interface* itf) { if (!STATEOF(itf).enabled) return; // shrug - PIO pio = PINOUT_itf_to_pio(itf); - int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF, - pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF; + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); - dma_channel_abort(STATEOF(itf).rx.dmach); - dma_channel_abort(STATEOF(itf).tx.dmach); + int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF, + pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF; - deinit_sm(pio, &STATEOF(itf).rx, &ftdi_uart_rx_program); - deinit_sm(pio, &STATEOF(itf).tx, &ftdi_uart_tx_program); + ftdihw_dma_rx_flush(hw); + ftdihw_dma_tx_flush(hw); + + ftdihw_dma_ch_deinit(&hw->rx, hw); + ftdihw_dma_ch_deinit(&hw->tx, hw); gpio_set_function(pin_rx, GPIO_FUNC_NULL); gpio_set_function(pin_tx, GPIO_FUNC_NULL); @@ -146,14 +111,13 @@ void ftdi_if_uart_deinit(struct ftdi_interface* itf) { void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) { if (!STATEOF(itf).enabled) return; - PIO pio = PINOUT_itf_to_pio(itf); - int pin_rx = PINOUT_itf_to_base(itf) + PINOUT_UART_RXD_OFF, - pin_tx = PINOUT_itf_to_base(itf) + PINOUT_UART_TXD_OFF; + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); - ftdi_uart_rx_program_init(pio, STATEOF(itf).rx.sm, STATEOF(itf).rx.off, - pin_rx, baudrate); - ftdi_uart_tx_program_init(pio, STATEOF(itf).tx.sm, STATEOF(itf).tx.off, - pin_tx, baudrate); + int pin_rx = ftdihw_itf_to_base(itf) + PINOUT_UART_RXD_OFF, + pin_tx = ftdihw_itf_to_base(itf) + PINOUT_UART_TXD_OFF; + + ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, baudrate); + ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, baudrate); STATEOF(itf).baudrate = baudrate; } @@ -167,32 +131,13 @@ void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lin } void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) { - // for writes, we can simply do a blocking DMA for now - // TODO: rewrite to use background DMA I guess - // (TODO: what if prev DMA still busy? --> overrun error!) - // TODO: ^ will need status bits! + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); - PIO pio = PINOUT_itf_to_pio(itf); - - dma_channel_config dcfg = dma_channel_get_default_config(STATEOF(itf).tx.dmach); - channel_config_set_read_increment(&dcfg, true); - channel_config_set_write_increment(&dcfg, false); - channel_config_set_dreq(&dcfg, - DREQ_PIO0_TX0 + itf->index*8/*PIO num*/ + STATEOF(itf).tx.sm); - channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8); - dma_channel_configure(STATEOF(itf).tx.dmach, &dcfg, - &pio->txf[STATEOF(itf).tx.sm], data, datasize, true); - - // TODO: not this - dma_channel_wait_for_finish_blocking(STATEOF(itf).tx.dmach); + ftdihw_dma_write(hw, data, datasize); } size_t ftdi_if_uart_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) { - (void)itf; (void)data; (void)maxsize; + struct ftdi_hw* hw = ftdihw_itf_to_hw(itf); - // TODO: background thing going to a buffer which is then collected later - // on by this function, needs some buffer mgmt - // TODO: handle overruns - - return 0; + return ftdihw_dma_read(hw, data, maxsize); } diff --git a/src/m_ftdi/_ftdi.c b/src/m_ftdi/_ftdi.c index 9ed4fc7..b51db44 100644 --- a/src/m_ftdi/_ftdi.c +++ b/src/m_ftdi/_ftdi.c @@ -1,5 +1,6 @@ // vim: set et: +#include "tusb_config.h" #include #include "mode.h" @@ -19,20 +20,25 @@ static cothread_t ftdithread_ifa, ftdithread_ifb; static uint8_t ftdistack_ifa[THREAD_STACK_SIZE>>1], ftdistack_ifb[THREAD_STACK_SIZE>>1]; static void ftdi_thread_fn_ifa(void) { + printf("fn ifa thread!\n"); + while (1) { - ftdi_task_ifa(); + //ftdi_task_ifa(); thread_yield(); } } static void ftdi_thread_fn_ifb(void) { + printf("fn ifb thread!\n"); while (1) { - ftdi_task_ifb(); + //ftdi_task_ifb(); thread_yield(); } } #endif static void enter_cb(void) { + printf("mode5 enter begin\n"); + #ifdef USE_USBCDC_FOR_STDIO stdio_usb_set_itf_num(CDC_N_STDIO); #endif @@ -43,20 +49,25 @@ static void enter_cb(void) { ftdithread_ifb = co_derive(ftdistack_ifb, sizeof ftdistack_ifb, ftdi_thread_fn_ifb); #endif - if (!data_dirty) { + /*if (!data_dirty) { struct mode_info mi = storage_mode_get_info(5); if (mi.size != 0 && mi.version == 0x0010) { storage_mode_read(5, ftdi_eeprom, 0, sizeof ftdi_eeprom); } - } + }*/ + + //ftdi_init(); + printf("mode5 enter end\n"); } static void leave_cb(void) { + printf("mode5 leave\n"); #ifdef DBOARD_HAS_FTDI - ftdi_deinit(); + //ftdi_deinit(); #endif } static void task_cb(void) { + printf("mode5 task\n"); #ifdef DBOARD_HAS_FTDI tud_task(); thread_enter(ftdithread_ifa); @@ -66,7 +77,14 @@ static void task_cb(void) { } static void handle_cmd_cb(uint8_t cmd) { + printf("mode5 handlecmd %02x\n", cmd); + uint8_t resp = 0; + switch (cmd) { + case mode_cmd_get_features: + resp = 0; // TODO: what do we put here? + vnd_cfg_write_resp(cfg_resp_ok, 1, &resp); + break; default: vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode5 command %02x", cmd); break; @@ -184,6 +202,7 @@ static const uint8_t* my_descriptor_device_cb(void) { #if CFG_TUD_CDC > 0 static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) { + printf("mode5 linecoding %02x, %lu\n", itf, line_coding->bit_rate); switch (itf) { #ifdef USE_USBCDC_FOR_STDIO case CDC_N_STDIO: @@ -225,7 +244,7 @@ struct mode m_05_ftdi = { }, #ifdef DBOARD_HAS_FTDI - .tud_descriptor_device_cb = my_descriptor_device_cb, + //.tud_descriptor_device_cb = my_descriptor_device_cb, #endif #if CFG_TUD_CDC > 0 @@ -233,7 +252,7 @@ struct mode m_05_ftdi = { #endif #ifdef DBOARD_HAS_FTDI - .tud_vendor_control_xfer_cb = ftdi_control_xfer_cb, + //.tud_vendor_control_xfer_cb = ftdi_control_xfer_cb, #endif }; // clang-format on diff --git a/src/m_ftdi/ftdi.c b/src/m_ftdi/ftdi.c index d1e9cae..4a1f9c8 100644 --- a/src/m_ftdi/ftdi.c +++ b/src/m_ftdi/ftdi.c @@ -75,6 +75,8 @@ int __builtin_ctz(unsigned int v); uint16_t ftdi_eeprom[128]; +struct ftdi_interface ftdi_ifa, ftdi_ifb; + void ftdi_init(void) { // init eeprom defaults memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom); @@ -97,6 +99,8 @@ void ftdi_init(void) { ftdi_ifb.lineprop = sio_bits_8 | sio_stop_1; // 8n1 ftdi_ifa.index = 0; ftdi_ifb.index = 1; + ftdi_ifa.modemstat = sio_modem_temt; + ftdi_ifb.modemstat = sio_modem_temt; ftdi_if_init(&ftdi_ifa); ftdi_if_init(&ftdi_ifb); } diff --git a/src/m_ftdi/ftdi.h b/src/m_ftdi/ftdi.h index fd4c873..783c5f5 100644 --- a/src/m_ftdi/ftdi.h +++ b/src/m_ftdi/ftdi.h @@ -259,9 +259,6 @@ struct ftdi_interface { uint8_t readbuf [CFG_TUD_VENDOR_TX_BUFSIZE]; uint8_t bufbuf [CFG_TUD_VENDOR_RX_BUFSIZE]; // for buffered IO uint32_t rxavail, rxpos; - - uint8_t dma_in_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; - uint8_t dma_out_buf[CFG_TUD_VENDOR_RX_BUFSIZE]; }; extern struct ftdi_interface ftdi_ifa, ftdi_ifb; diff --git a/src/main.c b/src/main.c index 7570ca5..3e3584f 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,8 @@ static void vndcfg_thread_fn(void) { } int main() { + stdio_uart_init(); + thread_init(); board_init(); // tinyusb hardware support function diff --git a/src/modeset.c b/src/modeset.c index 22eea42..71e9db5 100644 --- a/src/modeset.c +++ b/src/modeset.c @@ -7,7 +7,7 @@ #include "mode.h" #include "storage.h" -extern struct mode m_01_default, m_03_jscan, m_04_sump; +extern struct mode m_01_default, m_03_jscan, m_04_sump, m_05_ftdi; // clang-format off const struct mode* const mode_list[16] = { @@ -16,6 +16,7 @@ const struct mode* const mode_list[16] = { NULL, // mode 2 (hw chip programming stuff) not implemented yet &m_03_jscan, &m_04_sump, + &m_05_ftdi, NULL, // terminating entry }; // clang-format on @@ -99,6 +100,8 @@ void modes_switch(uint8_t newmode) { // maybe wait a second or so for the host to notice this sleep_ms(500/2); + //printf("disconnect\n"); + if (newmode == 0) bsp_reset_bootloader(); // now apply the new tusb settings @@ -164,11 +167,15 @@ void modes_switch(uint8_t newmode) { // clang-format on } + //printf("reconnect\n"); + // and reconnect tud_connect(); sleep_ms(500/2); //while (!tud_mounted()) sleep_ms(5); + //printf("enter\n"); + if (mode_current) mode_current->enter(); } diff --git a/src/usbstdio.h b/src/usbstdio.h index b80a93c..1f23b86 100644 --- a/src/usbstdio.h +++ b/src/usbstdio.h @@ -13,5 +13,7 @@ void stdio_usb_set_itf_num(int itf); void stdio_usb_line_coding_cb(cdc_line_coding_t const* line_coding); #endif +void stdio_uart_init(void); + #endif