#include "general.h"
#include "platform.h"
#include "usb.h"
#include "traceswo.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/rcc.h>
static bool decoding = false;
static uint8_t trace_usb_buf[64];
static uint8_t trace_usb_buf_size;
void traceswo_init(uint32_t swo_chan_bitmask)
{
TRACE_TIM_CLK_EN();
timer_ic_set_input(TRACE_TIM, TIM_IC1, TIM_IC_IN_TI1);
timer_ic_set_input(TRACE_TIM, TIM_IC2, TIM_IC_IN_TI1);
timer_ic_set_polarity(TRACE_TIM, TIM_IC1, TIM_IC_RISING);
timer_ic_set_polarity(TRACE_TIM, TIM_IC2, TIM_IC_FALLING);
timer_slave_set_trigger(TRACE_TIM, TIM_SMCR_TS_TI1FP1);
timer_slave_set_mode(TRACE_TIM, TIM_SMCR_SMS_RM);
nvic_set_priority(TRACE_IRQ, IRQ_PRI_TRACE);
nvic_enable_irq(TRACE_IRQ);
timer_enable_irq(TRACE_TIM, TIM_DIER_CC1IE);
timer_ic_enable(TRACE_TIM, TIM_IC1);
timer_ic_enable(TRACE_TIM, TIM_IC2);
timer_enable_counter(TRACE_TIM);
traceswo_setmask(swo_chan_bitmask);
decoding = (swo_chan_bitmask != 0);
}
void trace_buf_push(uint8_t *buf, int len)
{
if (decoding)
traceswo_decode(usbdev, CDCACM_UART_ENDPOINT, buf, len);
else if (usbd_ep_write_packet(usbdev, USB_REQ_TYPE_IN | TRACE_ENDPOINT, buf, len) != len) {
if (trace_usb_buf_size + len > 64) {
usbd_ep_stall_set(usbdev, USB_REQ_TYPE_IN | TRACE_ENDPOINT, 1);
trace_usb_buf_size = 0;
return;
}
memcpy(trace_usb_buf + trace_usb_buf_size, buf, len);
trace_usb_buf_size += len;
}
}
void trace_buf_drain(usbd_device *dev, uint8_t ep)
{
if (!trace_usb_buf_size)
return;
if (decoding)
traceswo_decode(dev, CDCACM_UART_ENDPOINT, trace_usb_buf, trace_usb_buf_size);
else
usbd_ep_write_packet(dev, ep, trace_usb_buf, trace_usb_buf_size);
trace_usb_buf_size = 0;
}
#define ALLOWED_DUTY_ERROR 5
void TRACE_ISR(void)
{
uint16_t status = TIM_SR(TRACE_TIM);
static uint16_t bt;
static uint8_t lastbit;
static uint8_t decbuf[17];
static uint8_t decbuf_pos;
static uint8_t halfbit;
static uint8_t notstart;
if (status & (TIM_SR_CC1OF | TIM_SR_UIF)) {
timer_clear_flag(TRACE_TIM, TIM_SR_CC1OF | TIM_SR_UIF);
if (!(status & (TIM_SR_CC2IF | TIM_SR_CC1IF)))
goto flush_and_reset;
}
const uint16_t cycle = TIM_CCR1(TRACE_TIM);
uint16_t duty = TIM_CCR2(TRACE_TIM);
if ((bt && (duty / bt > 2U || duty / bt == 0)) || duty == 0)
goto flush_and_reset;
if (!(status & TIM_SR_CC1IF))
notstart = 1;
if (!bt) {
if (notstart) {
notstart = 0;
return;
}
duty -= ALLOWED_DUTY_ERROR;
const uint16_t duty_cycle = cycle / duty;
if (duty_cycle != 2U && duty_cycle != 3U)
return;
bt = duty;
lastbit = 1;
halfbit = 0;
timer_set_period(TRACE_TIM, duty * 6U);
timer_clear_flag(TRACE_TIM, TIM_SR_UIF);
timer_enable_irq(TRACE_TIM, TIM_DIER_UIE);
} else {
if (duty / bt > 1U) {
if (!halfbit)
goto flush_and_reset;
halfbit = 0;
lastbit ^= 1U;
}
decbuf[decbuf_pos >> 3U] |= lastbit << (decbuf_pos & 7U);
++decbuf_pos;
}
if (!(status & TIM_SR_CC1IF) || (cycle - duty) / bt > 2)
goto flush_and_reset;
if ((cycle - duty) / bt > 1) {
if (halfbit)
goto flush_and_reset;
halfbit = 1;
lastbit ^= 1U;
decbuf[decbuf_pos >> 3U] |= lastbit << (decbuf_pos & 7U);
++decbuf_pos;
}
if (decbuf_pos < 128U)
return;
flush_and_reset:
timer_set_period(TRACE_TIM, -1);
timer_disable_irq(TRACE_TIM, TIM_DIER_UIE);
trace_buf_push(decbuf, decbuf_pos >> 3U);
bt = 0;
decbuf_pos = 0;
memset(decbuf, 0, sizeof(decbuf));
}