SWO manchester mode

This commit is contained in:
Triss 2021-09-16 01:34:15 +02:00
parent 1b35f7f63c
commit 6d91cabcee
4 changed files with 233 additions and 14 deletions

View File

@ -140,7 +140,7 @@ This information includes:
/// Indicate that Manchester Serial Wire Output (SWO) trace is available. /// Indicate that Manchester Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>. /// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#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. /// SWO Trace Buffer Size.
#define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n). #define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n).

View File

@ -8,7 +8,8 @@
#include <hardware/pio.h> #include <hardware/pio.h>
#include <hardware/structs/dma.h> #include <hardware/structs/dma.h>
#include "uart_rx.pio.h" #include "swo_uart_rx.pio.h"
#include "swo_manchester_encoding.pio.h"
static uint32_t static uint32_t
swo_baudrate = 115200, swo_baudrate = 115200,
@ -24,6 +25,10 @@ static bool mode_enabled = false;
uint32_t SWO_Mode_UART(uint32_t enable) { uint32_t SWO_Mode_UART(uint32_t enable) {
//for(;;);//printf("SWO mode %lu\n", enable); //for(;;);//printf("SWO mode %lu\n", enable);
if (enable) { if (enable) {
if (mode_enabled) { // already inited!
return 0;
}
swo_sm = pio_claim_unused_sm(SWO_PIO, false); swo_sm = pio_claim_unused_sm(SWO_PIO, false);
if (swo_sm == -1) { if (swo_sm == -1) {
//for(;;);//printf("E: no PIO\n"); //for(;;);//printf("E: no PIO\n");
@ -38,7 +43,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) {
return 0; 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"); //for(;;);//printf("E: no prg\n");
dma_channel_unclaim(swo_dmach); dma_channel_unclaim(swo_dmach);
swo_dmach = -1; swo_dmach = -1;
@ -47,8 +52,9 @@ uint32_t SWO_Mode_UART(uint32_t enable) {
return 0; return 0;
} }
swo_pio_off = pio_add_program(SWO_PIO, &uart_rx_program); swo_pio_off = pio_add_program(SWO_PIO, &swo_uart_rx_program);
uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, swo_baudrate); 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; mode_enabled = true;
} else { } else {
@ -65,7 +71,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) {
swo_sm = -1; swo_sm = -1;
} }
if (~swo_pio_off != 0) { 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; 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 // return: actual baudrate or 0 when not configured
uint32_t SWO_Baudrate_UART(uint32_t baudrate) { uint32_t SWO_Baudrate_UART(uint32_t baudrate) {
//for(;;);//printf("SWO baudrate %lu\n", baudrate); //for(;;);//printf("SWO baudrate %lu\n", baudrate);
swo_baudrate = baudrate;
if (!mode_enabled) return 0; 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 return baudrate; // should be ok
} }
@ -141,3 +148,121 @@ uint32_t SWO_GetCount_UART(void) {
return swo_num - remaining; 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;
}

View File

@ -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);
}
%}

View File

@ -4,7 +4,7 @@
; SPDX-License-Identifier: BSD-3-Clause ; 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 ; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
; with the correct timing. ; with the correct timing.
@ -21,12 +21,12 @@ bitloop: ; Loop 8 times
#include "hardware/clocks.h" #include "hardware/clocks.h"
#include "hardware/gpio.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_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin); pio_gpio_init(pio, pin);
gpio_pull_up(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 sm_config_set_in_pins(&c, pin); // for WAIT, IN
// Shift to right, autopush enabled // Shift to right, autopush enabled
sm_config_set_in_shift(&c, true, true, 8); 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 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
; break conditions more gracefully. ; break conditions more gracefully.
@ -63,12 +63,12 @@ good_stop: ; No delay before returning to start; a little slack is
% c-sdk { % 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_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin); pio_gpio_init(pio, pin);
gpio_pull_up(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_in_pins(&c, pin); // for WAIT, IN
sm_config_set_jmp_pin(&c, pin); // for JMP sm_config_set_jmp_pin(&c, pin); // for JMP
// Shift to right, autopull disabled // 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); //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 // 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; io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3;
while (pio_sm_is_rx_fifo_empty(pio, sm)) while (pio_sm_is_rx_fifo_empty(pio, sm))