diff --git a/bsp/rp2040/DAP_config.h b/bsp/rp2040/DAP_config.h index 84d9b68..acfb9c3 100644 --- a/bsp/rp2040/DAP_config.h +++ b/bsp/rp2040/DAP_config.h @@ -140,7 +140,7 @@ This information includes: /// Indicate that Manchester Serial Wire Output (SWO) trace is available. /// This information is returned by the command \ref DAP_Info as part of Capabilities. -#define SWO_MANCHESTER 0 ///< SWO Manchester: 1 = available, 0 = not available. +#define SWO_MANCHESTER 1 ///< SWO Manchester: 1 = available, 0 = not available. /// SWO Trace Buffer Size. #define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n). diff --git a/bsp/rp2040/m_default/dap_swo.c b/bsp/rp2040/m_default/dap_swo.c index 8f61773..00b6279 100644 --- a/bsp/rp2040/m_default/dap_swo.c +++ b/bsp/rp2040/m_default/dap_swo.c @@ -8,7 +8,8 @@ #include #include -#include "uart_rx.pio.h" +#include "swo_uart_rx.pio.h" +#include "swo_manchester_encoding.pio.h" static uint32_t swo_baudrate = 115200, @@ -24,6 +25,10 @@ static bool mode_enabled = false; uint32_t SWO_Mode_UART(uint32_t enable) { //for(;;);//printf("SWO mode %lu\n", enable); if (enable) { + if (mode_enabled) { // already inited! + return 0; + } + swo_sm = pio_claim_unused_sm(SWO_PIO, false); if (swo_sm == -1) { //for(;;);//printf("E: no PIO\n"); @@ -38,7 +43,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) { return 0; } - if (!pio_can_add_program(SWO_PIO, &uart_rx_program)) { + if (!pio_can_add_program(SWO_PIO, &swo_uart_rx_program)) { //for(;;);//printf("E: no prg\n"); dma_channel_unclaim(swo_dmach); swo_dmach = -1; @@ -47,8 +52,9 @@ uint32_t SWO_Mode_UART(uint32_t enable) { return 0; } - swo_pio_off = pio_add_program(SWO_PIO, &uart_rx_program); - uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, swo_baudrate); + swo_pio_off = pio_add_program(SWO_PIO, &swo_uart_rx_program); + swo_uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, swo_baudrate); + gpio_set_function(PINOUT_SWO, GPIO_FUNC_PIO1); mode_enabled = true; } else { @@ -65,7 +71,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) { swo_sm = -1; } if (~swo_pio_off != 0) { - pio_remove_program(SWO_PIO, &uart_rx_program, swo_pio_off); + pio_remove_program(SWO_PIO, &swo_uart_rx_program, swo_pio_off); swo_pio_off = ~(uint32_t)0; } @@ -82,9 +88,10 @@ uint32_t SWO_Mode_UART(uint32_t enable) { // return: actual baudrate or 0 when not configured uint32_t SWO_Baudrate_UART(uint32_t baudrate) { //for(;;);//printf("SWO baudrate %lu\n", baudrate); + swo_baudrate = baudrate; if (!mode_enabled) return 0; - uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, baudrate); + swo_uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, baudrate); return baudrate; // should be ok } @@ -141,3 +148,121 @@ uint32_t SWO_GetCount_UART(void) { return swo_num - remaining; } +/*** MANCHESTER **************************************************************/ + +uint32_t SWO_Mode_Manchester(uint32_t enable) { + //for(;;);//printf("SWOM mode %lu\n", enable); + if (enable) { + if (mode_enabled) { // already inited! + return 0; + } + + swo_sm = pio_claim_unused_sm(SWO_PIO, false); + if (swo_sm == -1) { + //for(;;);//printf("E: no PIO\n"); + return 0; + } + + swo_dmach = dma_claim_unused_channel(false); + if (swo_dmach == -1) { + //for(;;);//printf("E: no DMA\n"); + pio_sm_unclaim(SWO_PIO, swo_sm); + swo_sm = -1; + return 0; + } + + if (!pio_can_add_program(SWO_PIO, &swo_manchester_rx_program)) { + //for(;;);//printf("E: no prg\n"); + dma_channel_unclaim(swo_dmach); + swo_dmach = -1; + pio_sm_unclaim(SWO_PIO, swo_sm); + swo_sm = -1; + return 0; + } + + swo_pio_off = pio_add_program(SWO_PIO, &swo_manchester_rx_program); + swo_manchester_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, swo_baudrate); + gpio_set_function(PINOUT_SWO, GPIO_FUNC_PIO1); + + mode_enabled = true; + } else { + mode_enabled = false; + + if (swo_dmach >= 0) { + dma_channel_unclaim(swo_dmach); // ugh why is it "dma_channel_xyz" and "dma_xyz_channel" + swo_dmach = -1; + } + + if (swo_sm >= 0) { + pio_sm_set_enabled(SWO_PIO, swo_sm, false); + pio_sm_unclaim(SWO_PIO, swo_sm); + swo_sm = -1; + } + if (~swo_pio_off != 0) { + pio_remove_program(SWO_PIO, &swo_manchester_rx_program, swo_pio_off); + swo_pio_off = ~(uint32_t)0; + } + + // hi-Z nothing + gpio_set_function(PINOUT_SWO, GPIO_FUNC_NULL); + gpio_set_pulls(PINOUT_SWO, false, false); + } + + return 1; + return 0; +} + +uint32_t SWO_Baudrate_Manchester(uint32_t baudrate) { + //for(;;);//printf("SWOM baudrate %lu\n", baudrate); + swo_baudrate = baudrate; + if (!mode_enabled) return 0; + + swo_manchester_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, baudrate); + + return baudrate; // should be ok +} + +uint32_t SWO_Control_Manchester(uint32_t active) { + //for(;;);//printf("SWOM control %lu\n", active); + if (!mode_enabled) return 0; + + if (active) { + pio_sm_set_enabled(SWO_PIO, swo_sm, true); + dma_channel_start(swo_dmach); + } + else { + dma_channel_abort(swo_dmach); + pio_sm_set_enabled(SWO_PIO, swo_sm, false); + swo_num = 0; + } + + return 1; +} + +void SWO_Capture_Manchester(uint8_t* buf, uint32_t num) { + //for(;;);//printf("SWOM capture %p 0x%lx\n", buf, num); + if (!mode_enabled) return; + + swo_num = num; + + // set up DMA params + dma_channel_config dcfg = dma_channel_get_default_config(swo_dmach); + channel_config_set_read_increment(&dcfg, false); + channel_config_set_write_increment(&dcfg, true); + channel_config_set_dreq(&dcfg, pio_get_dreq(SWO_PIO, swo_sm, false)); + channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8); + dma_channel_configure(swo_dmach, &dcfg, buf, &SWO_PIO->rxf[swo_sm], num, false); +} + +uint32_t SWO_GetCount_Manchester(void) { + // DMA hw decreases transfer_count by one on every transfer, so it contains + // the number of remaining bytes to be received. + // however, CMSIS-DAP wants (badly worded) the number of bytes already + // received + if (!mode_enabled || swo_num == 0) return 0; // not initialized + + uint32_t remaining = dma_hw->ch[swo_dmach].transfer_count; + //for(;;);//printf("SWO getcount -> 0x%lx\n", swo_num - remaining); + return swo_num - remaining; +} + diff --git a/bsp/rp2040/m_default/swo_manchester_encoding.pio b/bsp/rp2040/m_default/swo_manchester_encoding.pio new file mode 100644 index 0000000..d333714 --- /dev/null +++ b/bsp/rp2040/m_default/swo_manchester_encoding.pio @@ -0,0 +1,94 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program swo_manchester_tx +.side_set 1 opt + +; Transmit one bit every 12 cycles. a '0' is encoded as a high-low sequence +; (each part lasting half a bit period, or 6 cycles) and a '1' is encoded as a +; low-high sequence. +; +; Side-set bit 0 must be mapped to the GPIO used for TX. +; Autopull must be enabled -- this program does not care about the threshold. +; The program starts at the public label 'start'. + +.wrap_target +do_1: + nop side 0 [5] ; Low for 6 cycles (5 delay, +1 for nop) + jmp get_bit side 1 [3] ; High for 4 cycles. 'get_bit' takes another 2 cycles +do_0: + nop side 1 [5] ; Output high for 6 cycles + nop side 0 [3] ; Output low for 4 cycles +public start: +get_bit: + out x, 1 ; Always shift out one bit from OSR to X, so we can + jmp !x do_0 ; branch on it. Autopull refills the OSR when empty. +.wrap + +% c-sdk { +static inline void swo_manchester_tx_program_init(PIO pio, uint sm, uint offset, uint pin, float div) { + pio_sm_set_pins_with_mask(pio, sm, 0, 1u << pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + pio_gpio_init(pio, pin); + + pio_sm_config c = swo_manchester_tx_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, pin); + sm_config_set_out_shift(&c, true, true, 32); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset + swo_manchester_tx_offset_start, &c); + + pio_sm_set_enabled(pio, sm, true); +} +%} + +.program swo_manchester_rx + +; Assumes line is idle low, first bit is 0 +; One bit is 12 cycles +; a '0' is encoded as 10 +; a '1' is encoded as 01 +; +; Both the IN base and the JMP pin mapping must be pointed at the GPIO used for RX. +; Autopush must be enabled. +; Before enabling the SM, it should be placed in a 'wait 1, pin` state, so that +; it will not start sampling until the initial line idle state ends. + +start_of_0: ; We are 0.25 bits into a 0 - signal is high + wait 0 pin 0 ; Wait for the 1->0 transition - at this point we are 0.5 into the bit + in y, 1 [8] ; Emit a 0, sleep 3/4 of a bit + jmp pin start_of_0 ; If signal is 1 again, it's another 0 bit, otherwise it's a 1 + +.wrap_target +start_of_1: ; We are 0.25 bits into a 1 - signal is 1 + wait 1 pin 0 ; Wait for the 0->1 transition - at this point we are 0.5 into the bit + in x, 1 [8] ; Emit a 1, sleep 3/4 of a bit + jmp pin start_of_0 ; If signal is 0 again, it's another 1 bit otherwise it's a 0 +.wrap + +% c-sdk { +static inline void swo_manchester_rx_program_init(PIO pio, uint sm, uint offset, uint pin, float div) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + + pio_sm_config c = swo_manchester_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT + sm_config_set_jmp_pin(&c, pin); // for JMP + sm_config_set_in_shift(&c, true, true, 32); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + + // X and Y are set to 0 and 1, to conveniently emit these to ISR/FIFO. + pio_sm_exec(pio, sm, pio_encode_set(pio_x, 1)); + pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0)); + // Assume line is idle low, and first transmitted bit is 0. Put SM in a + // wait state before enabling. RX will begin once the first 0 symbol is + // detected. + pio_sm_exec(pio, sm, pio_encode_wait_pin(1, 0) | pio_encode_delay(2)); + pio_sm_set_enabled(pio, sm, true); +} +%} diff --git a/bsp/rp2040/m_default/uart_rx.pio b/bsp/rp2040/m_default/swo_uart_rx.pio similarity index 86% rename from bsp/rp2040/m_default/uart_rx.pio rename to bsp/rp2040/m_default/swo_uart_rx.pio index 0bd370e..a9f0ad9 100644 --- a/bsp/rp2040/m_default/uart_rx.pio +++ b/bsp/rp2040/m_default/swo_uart_rx.pio @@ -4,7 +4,7 @@ ; SPDX-License-Identifier: BSD-3-Clause ; -.program uart_rx_mini +.program swo_uart_rx_mini ; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits ; with the correct timing. @@ -21,12 +21,12 @@ bitloop: ; Loop 8 times #include "hardware/clocks.h" #include "hardware/gpio.h" -static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { +static inline void swo_uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); pio_gpio_init(pio, pin); gpio_pull_up(pin); - pio_sm_config c = uart_rx_mini_program_get_default_config(offset); + pio_sm_config c = swo_uart_rx_mini_program_get_default_config(offset); sm_config_set_in_pins(&c, pin); // for WAIT, IN // Shift to right, autopush enabled sm_config_set_in_shift(&c, true, true, 8); @@ -40,7 +40,7 @@ static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint } %} -.program uart_rx +.program swo_uart_rx ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and ; break conditions more gracefully. @@ -63,12 +63,12 @@ good_stop: ; No delay before returning to start; a little slack is % c-sdk { -static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { +static inline void swo_uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); pio_gpio_init(pio, pin); gpio_pull_up(pin); - pio_sm_config c = uart_rx_program_get_default_config(offset); + pio_sm_config c = swo_uart_rx_program_get_default_config(offset); sm_config_set_in_pins(&c, pin); // for WAIT, IN sm_config_set_jmp_pin(&c, pin); // for JMP // Shift to right, autopull disabled @@ -83,7 +83,7 @@ static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, //pio_sm_set_enabled(pio, sm, true); } -static inline char uart_rx_program_getc(PIO pio, uint sm) { +static inline char swo_uart_rx_program_getc(PIO pio, uint sm) { // 8-bit read from the uppermost byte of the FIFO, as data is left-justified io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3; while (pio_sm_is_rx_fifo_empty(pio, sm))