EVE 1.0
debug-swo.c
1 /* vi: set noai ts=2 sw=2 expandtab: */
2 #include <stdint.h>
3 #include <string.h>
4 #include <debug-uart.h>
5 #include <core/uwork.h>
6 #include <core/semihosting.h>
7 #include <core/pm.h>
8 #include <nrf_soc.h>
9 #include <nrf_nvic.h>
10 #include <app_util_platform.h>
11 
12 #ifndef DBG_XMIT_BUFFER_LEN
13 #define DBG_XMIT_BUFFER_LEN (1024)
14 #endif
15 
16 #ifndef DBG_SWO_XMIT_INTERVAL
17 #define DBG_SWO_XMIT_INTERVAL (UWORK_USEC(100))
18 #endif
19 
20 static void dbg_do_xmit(struct uwork_t *work);
21 static void dbg_do_semi_xmit(void);
22 
23 struct dbg_state_t
24 {
25  uint8_t *volatile xmit_buffer_head; /**< Read position */
26  uint8_t *volatile xmit_buffer_tail; /**< Write position */
27  uint8_t *volatile xmit_buffer_phead; /**< Panic mode read position */
28  struct uwork_t xmit_work; /**< Transmit work */
29  uint8_t write_overrun; /**< Write overrun flag */
30  uint8_t semihosting; /**< Semihosting enabled */
31  uint8_t enabled; /**< Debug output enabled */
32  uint8_t xmit_buffer[DBG_XMIT_BUFFER_LEN]; /**< Transmit buffer */
33 };
34 
35 static struct dbg_state_t dbg_state;
36 #define XMIT_BUFFER_END &dbg_state.xmit_buffer[DBG_XMIT_BUFFER_LEN]
37 
38 
39 void dbg_setup(void)
40 {
41  dbg_state.xmit_work = UWORK_INIT_TYPED(dbg_state.xmit_work, dbg_do_xmit);
42  dbg_state.xmit_buffer_head = dbg_state.xmit_buffer;
43  dbg_state.xmit_buffer_tail = dbg_state.xmit_buffer;
44 
45  if (dbg_connected())
46  {
47  /* ITM Channel 0 is used for UART output */
48  ITM->TER |= (1UL << 0);
49  dbg_state.enabled = 1;
50  }
51 }
52 
53 void dbg_force_enabled(void)
54 {
55  ITM->TER |= (1UL << 0);
56  dbg_state.enabled = 1;
57 }
58 
59 static void dbg_do_xmit(struct uwork_t *work)
60 {
61  uint8_t *head = dbg_state.xmit_buffer_head;
62  uint8_t *tail = dbg_state.xmit_buffer_tail;
63  unsigned interval = DBG_SWO_XMIT_INTERVAL;
64 
65  while (head != tail) {
66  if (ITM->PORT[0].u32 == 0)
67  break;
68  ITM->PORT[0].u8 = (uint8_t) *head;
69  if (++head == XMIT_BUFFER_END)
70  head = dbg_state.xmit_buffer;
71  }
72 
73  CRITICAL_REGION_ENTER();
74  if (head != dbg_state.xmit_buffer_tail) {
75  work->at += interval;
76  uwork_schedule(work);
77  }
78  dbg_state.xmit_buffer_head = head;
79  CRITICAL_REGION_EXIT();
80 }
81 
82 static void dbg_do_semi_xmit(void)
83 {
84  uint8_t *head = dbg_state.xmit_buffer_phead;
85  uint8_t *tail = dbg_state.xmit_buffer_tail;
86 
87  while (head != tail) {
88  int chunk;
89  if (tail > head)
90  chunk = tail - head;
91  else
92  chunk = XMIT_BUFFER_END - head;
93 
94  if (chunk > 1) {
95  volatile uint8_t *p = &head[chunk - 1];
96  uint8_t term = *p;
97  *p = 0;
98  call_semihost(SYS_WRITE0, head);
99  *p = term;
100  }
101  call_semihost(SYS_WRITE0, &head[chunk - 1]);
102 
103  head += chunk;
104  if (head == XMIT_BUFFER_END)
105  head = dbg_state.xmit_buffer;
106  }
107 
108  dbg_state.xmit_buffer_phead = head;
109 }
110 
111 unsigned int dbg_send_bytes(const uint8_t *seq, unsigned int len)
112 {
113  if (dbg_state.enabled) {
114 
115  /* Interrupts are disabled to protect against possible concurrent
116  calls to dbg_send_bytes() from different runlevels */
117  CRITICAL_REGION_ENTER();
118 
119  uint8_t *head = dbg_state.xmit_buffer_head;
120  uint8_t *tail = dbg_state.xmit_buffer_tail;
121 
122  if (tail >= head) {
123  /* Free space wraps */
124  unsigned int xfer_len = XMIT_BUFFER_END - tail;
125  unsigned int free = DBG_XMIT_BUFFER_LEN - (tail - head) - 1;
126 
127  if (len > free)
128  len = free;
129  if (xfer_len < len) {
130  memcpy(tail, seq, xfer_len);
131  seq += xfer_len;
132  xfer_len = len - xfer_len;
133  memcpy(dbg_state.xmit_buffer, seq, xfer_len);
134  tail = dbg_state.xmit_buffer + xfer_len;
135  } else {
136  memcpy(tail, seq, len);
137  tail += len;
138  if (tail == XMIT_BUFFER_END)
139  tail = dbg_state.xmit_buffer;
140  }
141  } else {
142  /* Free space continuous */
143  unsigned int free = (head - tail) - 1;
144  if (len > free)
145  len = free;
146  memcpy(tail, seq, len);
147  tail += len;
148  }
149 
150  dbg_state.xmit_buffer_tail = tail;
151  if (!uwork_pending(&dbg_state.xmit_work)) {
152  dbg_state.xmit_work.at = uwork_now();
153  uwork_schedule(&dbg_state.xmit_work);
154  }
155  CRITICAL_REGION_EXIT();
156  }
157 
158  return len;
159 }
160 
161 void dbg_putchar(const char ch)
162 {
163  if (dbg_state.write_overrun) {
164  if (dbg_send_bytes((const uint8_t*)"^", 1) != 1)
165  return;
166  }
167  dbg_state.write_overrun = 0;
168  if (dbg_send_bytes((const uint8_t*)&ch, 1) != 1) {
169  dbg_state.write_overrun = 1;
170  }
171 }
172 
173 void dbg_blocking_putchar(const char ch)
174 {
175  if (dbg_state.write_overrun) {
176  while (dbg_send_bytes((const uint8_t*)"^", 1) != 1)
177  __WFE();
178  }
179  dbg_state.write_overrun = 0;
180  while (dbg_send_bytes((const uint8_t*)&ch, 1) != 1)
181  __WFE();
182  __SEV();
183 }
184 
185 void dbg_drain()
186 {
187  if (dbg_state.enabled) {
188  CRITICAL_REGION_ENTER();
189  while(dbg_state.xmit_buffer_tail != dbg_state.xmit_buffer_head)
190  dbg_do_xmit(&dbg_state.xmit_work);
191  if (dbg_state.semihosting)
192  dbg_do_semi_xmit();
193  CRITICAL_REGION_EXIT();
194  }
195 }
196 
197 void dbg_panic(void)
198 {
199  dbg_state.xmit_buffer_phead = dbg_state.xmit_buffer_tail;
200  dbg_state.semihosting = true;
201 }
Header file for the EVE power management framework.
void uwork_schedule(struct uwork_t *work)
static int32_t call_semihost(uint32_t cmd, const void *msg)
Definition: semihosting.h:128
uwork_time_t at
Definition: uwork.h:144
uint32_t seq
Definition: usbnet.h:140
static bool uwork_pending(struct uwork_t *work)
Definition: uwork.h:207
Header file for the EVE microsecond-scale work scheduling.
#define UWORK_INIT_TYPED(x, callback)
Definition: uwork.h:86
Definition: uwork.h:142
uwork_time_t uwork_now(void)
Semihosting interface.