OpenOCD
esp32_sysview.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /***************************************************************************
4  * ESP32 sysview tracing module *
5  * Copyright (C) 2020 Espressif Systems Ltd. *
6  ***************************************************************************/
7 
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11 
12 #include <helper/log.h>
13 #include "esp32_apptrace.h"
14 #include "esp32_sysview.h"
15 #include "segger_sysview.h"
16 
17 /* in SystemView mode core ID is passed in event ID field */
18 #define ESP32_SYSVIEW_USER_BLOCK_CORE(_v_) (0) /* not used */
19 #define ESP32_SYSVIEW_USER_BLOCK_LEN(_v_) (_v_)
20 #define ESP32_SYSVIEW_USER_BLOCK_HDR_SZ 2
21 
23  uint8_t block_sz;
24  uint8_t wr_sz;
25 };
26 #define SYSVIEW_BLOCK_SIZE_OFFSET 0
27 #define SYSVIEW_WR_SIZE_OFFSET 1
28 
29 static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format);
30 static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf);
31 static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len);
32 
34  struct command_invocation *cmd,
35  int mode,
36  bool mcore_format,
37  const char **argv,
38  int argc)
39 {
40  struct esp32_sysview_cmd_data *cmd_data;
41 
42  if (argc < 1) {
43  command_print(cmd, "Not enough args! Need trace data destination!");
44  return ERROR_FAIL;
45  }
46 
47  int res = esp32_apptrace_cmd_ctx_init(cmd_ctx, cmd, mode);
48  if (res != ERROR_OK)
49  return res;
50 
51  int core_num = cmd_ctx->cores_num;
52 
53  if (!mcore_format && argc < core_num) {
54  command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num);
55  res = ERROR_FAIL;
56  goto on_error;
57  }
58 
59  cmd_data = calloc(1, sizeof(*cmd_data));
60  if (!cmd_data) {
61  command_print(cmd, "No memory for command data!");
62  res = ERROR_FAIL;
63  goto on_error;
64  }
65  cmd_ctx->cmd_priv = cmd_data;
66  cmd_data->mcore_format = mcore_format;
67 
68  /*outfile1 [outfile2] [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] */
69  int dests_num = esp32_apptrace_dest_init(cmd_data->data_dests, argv, !mcore_format ? core_num : 1);
70  if (!mcore_format && dests_num < core_num) {
71  command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num);
72  free(cmd_data);
73  res = ERROR_FAIL;
74  goto on_error;
75  }
76  cmd_data->apptrace.max_len = UINT32_MAX;
77  cmd_data->apptrace.poll_period = 0 /*ms*/;
78  cmd_ctx->stop_tmo = -1.0; /* infinite */
79  if (argc > dests_num) {
80  /* parse remaining args */
82  &cmd_data->apptrace,
83  &argv[dests_num],
84  argc - dests_num);
85  }
86  LOG_USER("App trace params: from %d cores, size %u bytes, stop_tmo %g s, "
87  "poll period %u ms, wait_rst %d, skip %u bytes",
88  cmd_ctx->cores_num,
89  cmd_data->apptrace.max_len,
90  cmd_ctx->stop_tmo,
91  cmd_data->apptrace.poll_period,
92  cmd_data->apptrace.wait4halt,
93  cmd_data->apptrace.skip_len);
94 
98 
100  if (res != ERROR_OK) {
101  command_print(cmd, "Failed to write trace header (%d)!", res);
102  esp32_apptrace_dest_cleanup(cmd_data->data_dests, core_num);
103  free(cmd_data);
104  return res;
105  }
106  return ERROR_OK;
107 on_error:
108  cmd_ctx->running = 0;
110  return res;
111 }
112 
114 {
115  struct esp32_sysview_cmd_data *cmd_data = cmd_ctx->cmd_priv;
116 
117  esp32_apptrace_dest_cleanup(cmd_data->data_dests, cmd_ctx->cores_num);
118  free(cmd_data);
119  cmd_ctx->cmd_priv = NULL;
121  return ERROR_OK;
122 }
123 
124 static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf)
125 {
126  /* for sysview compressed apptrace header is used, so core id is encoded in sysview packet */
127  return 0;
128 }
129 
130 static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len)
131 {
134 }
135 
137 {
138  struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
139  char *hdr_str;
140  int dests_num;
141 
142  if (!mcore_format) {
143  hdr_str = ";\n"
144  "; Version " SYSVIEW_MIN_VER_STRING "\n"
145  "; Author Espressif Inc\n"
146  ";\n";
147  dests_num = ctx->cores_num;
148  } else {
149  hdr_str = ";\n"
150  "; Version " SYSVIEW_MIN_VER_STRING "\n"
151  "; Author Espressif Inc\n"
152  "; ESP_Extension\n"
153  ";\n";
154  dests_num = 1;
155  }
156 
157  int hdr_len = strlen(hdr_str);
158  for (int i = 0; i < dests_num; i++) {
159  int res = cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv,
160  (uint8_t *)hdr_str,
161  hdr_len);
162  if (res != ERROR_OK) {
163  LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", hdr_len, i);
164  return ERROR_FAIL;
165  }
166  }
167  return ERROR_OK;
168 }
169 
170 static void sysview_encode_u32(uint8_t **dest, uint32_t val)
171 {
172  uint8_t *sv_ptr = *dest;
173  while (val > 0x7F) {
174  *sv_ptr++ = (uint8_t)(val | 0x80);
175  val >>= 7;
176  }
177  *sv_ptr++ = (uint8_t)val;
178  *dest = sv_ptr;
179 }
180 
181 static uint32_t esp_sysview_decode_u32(uint8_t **ptr)
182 {
183  uint32_t val = 0;
184  for (int k = 0;; k++, (*ptr)++) {
185  if (**ptr & 0x80) {
186  val |= (uint32_t)(**ptr & ~0x80) << 7 * k;
187  } else {
188  val |= (uint32_t)**ptr << 7 * k;
189  (*ptr)++;
190  break;
191  }
192  }
193  return val;
194 }
195 
196 static uint16_t esp_sysview_decode_plen(uint8_t **ptr)
197 {
198  uint16_t payload_len = 0;
199  uint8_t *p = *ptr;
200  /* here pkt points to encoded payload length */
201  if (*p & 0x80) {
202  payload_len = *(p + 1); /* higher part */
203  payload_len = (payload_len << 7) | (*p & ~0x80);/* lower 7 bits */
204  p += 2; /* payload len (2 bytes) */
205  } else {
206  payload_len = *p;
207  p++; /* payload len (1 byte) */
208  }
209  *ptr = p;
210 
211  return payload_len;
212 }
213 
214 static uint16_t esp_sysview_get_predef_payload_len(uint16_t id, uint8_t *pkt)
215 {
216  uint16_t len;
217  uint8_t *ptr = pkt;
218 
219  switch (id) {
229  /*ENCODE_U32 */
231  len = ptr - pkt;
232  break;
235  /*2*ENCODE_U32 */
238  len = ptr - pkt;
239  break;
241  /*str(128 + 1) */
242  len = *ptr + 1;
243  break;
246  /*2*ENCODE_U32 + str */
249  /* TODO: add support for strings longer then 255 bytes */
250  len = ptr - pkt + *ptr + 1;
251  break;
253  /*4*ENCODE_U32 */
258  len = ptr - pkt;
259  break;
264  case SYSVIEW_EVTID_IDLE:
267  len = 0;
268  break;
269 
270  /*case SYSVIEW_EVTID_NOP: */
271  default:
272  LOG_ERROR("sysview: Unsupported predef event %d!", id);
273  len = 0;
274  }
275  return len;
276 }
277 
278 static uint16_t esp_sysview_parse_packet(uint8_t *pkt_buf,
279  uint32_t *pkt_len,
280  unsigned int *pkt_core_id,
281  uint32_t *delta,
282  uint32_t *delta_len,
283  bool clear_core_bit)
284 {
285  uint8_t *pkt = pkt_buf;
286  uint16_t event_id = 0, payload_len = 0;
287 
288  *pkt_core_id = 0;
289  *pkt_len = 0;
290  /* 1-2 byte of message type, 0-2 byte of payload length, payload, 1-5 bytes of timestamp. */
291  if (*pkt & 0x80) {
292  if (*(pkt + 1) & (1 << 6)) {
293  if (clear_core_bit)
294  *(pkt + 1) &= ~(1 << 6); /* clear core_id bit */
295  *pkt_core_id = 1;
296  }
297  event_id = *(pkt + 1) & ~(1 << 6); /* higher part */
298  event_id = (event_id << 7) | (*pkt & ~0x80); /* lower 7 bits */
299  pkt += 2; /* event_id (2 bytes) */
300  /* here pkt points to encoded payload length */
301  payload_len = esp_sysview_decode_plen(&pkt);
302  } else {
303  if (*pkt & (1 << 6)) {
304  if (clear_core_bit)
305  *pkt &= ~(1 << 6); /* clear core_id bit */
306  *pkt_core_id = 1;
307  }
308  /* event_id (1 byte) */
309  event_id = *pkt & ~(1 << 6);
310  pkt++;
311  if (event_id < 24)
312  payload_len = esp_sysview_get_predef_payload_len(event_id, pkt);
313  else
314  payload_len = esp_sysview_decode_plen(&pkt);
315  }
316  pkt += payload_len;
317  uint8_t *delta_start = pkt;
318  *delta = esp_sysview_decode_u32(&pkt);
319  *delta_len = pkt - delta_start;
320  *pkt_len = pkt - pkt_buf;
321  LOG_DEBUG("sysview: evt %d len %d plen %d dlen %d",
322  event_id,
323  *pkt_len,
324  payload_len,
325  *delta_len);
326  return event_id;
327 }
328 
330  int pkt_core_id, uint32_t pkt_len, uint8_t *pkt_buf, uint32_t delta_len, uint8_t *delta_buf)
331 {
332  if (!cmd_data->data_dests[pkt_core_id].write)
333  return ERROR_FAIL;
334 
335  int res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, pkt_buf, pkt_len);
336 
337  if (res != ERROR_OK) {
338  LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, pkt_core_id);
339  return res;
340  }
341  if (delta_len) {
342  /* write packet with modified delta */
343  res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, delta_buf, delta_len);
344  if (res != ERROR_OK) {
345  LOG_ERROR("sysview: Failed to write %u bytes of delta to dest %d!", delta_len, pkt_core_id);
346  return res;
347  }
348  }
349  return ERROR_OK;
350 }
351 
353  unsigned int pkt_core_id, uint16_t event_id, uint32_t delta, uint32_t delta_len,
354  uint32_t pkt_len, uint8_t *pkt_buf)
355 {
356  struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
357  int pkt_core_changed = 0;
358  uint32_t new_delta_len = 0;
359  uint8_t new_delta_buf[10];
360  uint32_t wr_len = pkt_len;
361 
362  if (ctx->cores_num > 1) {
363  if (cmd_data->sv_last_core_id == pkt_core_id) {
364  /* if this packet is for the same core as the prev one acc delta and write packet unmodified */
365  cmd_data->sv_acc_time_delta += delta;
366  } else {
367  /* if this packet is for another core then prev one set acc delta to the packet's delta */
368  uint8_t *delta_ptr = new_delta_buf;
369  sysview_encode_u32(&delta_ptr, delta + cmd_data->sv_acc_time_delta);
370  cmd_data->sv_acc_time_delta = delta;
371  wr_len -= delta_len;
372  new_delta_len = delta_ptr - new_delta_buf;
373  pkt_core_changed = 1;
374  }
375  cmd_data->sv_last_core_id = pkt_core_id;
376  }
377  if (pkt_core_id >= ctx->cores_num) {
378  LOG_WARNING("sysview: invalid core ID in packet %d, must be less then %d! Event id %d",
379  pkt_core_id,
380  ctx->cores_num,
381  event_id);
382  return ERROR_FAIL;
383  }
384  int res = esp32_sysview_write_packet(cmd_data,
385  pkt_core_id,
386  wr_len,
387  pkt_buf,
388  new_delta_len,
389  new_delta_buf);
390  if (res != ERROR_OK)
391  return res;
392  for (unsigned int i = 0; i < ctx->cores_num; i++) {
393  if (pkt_core_id == i)
394  continue;
395  switch (event_id) {
396  /* messages below should be sent to trace destinations for all cores */
405  case SYSVIEW_EVTID_INIT:
409  /* if packet's source core has changed */
410  wr_len = pkt_len;
411  if (pkt_core_changed) {
412  /* clone packet with unmodified delta */
413  new_delta_len = 0;
414  } else {
415  /* clone packet with modified delta */
416  uint8_t *delta_ptr = new_delta_buf;
417  sysview_encode_u32(&delta_ptr, cmd_data->sv_acc_time_delta /*delta has been accumulated above*/);
418  wr_len -= delta_len;
419  new_delta_len = delta_ptr - new_delta_buf;
420  }
421  LOG_DEBUG("sysview: Redirect %d bytes of event %d to dest %d", wr_len, event_id, i);
422  res = esp32_sysview_write_packet(cmd_data,
423  i,
424  wr_len,
425  pkt_buf,
426  new_delta_len,
427  new_delta_buf);
428  if (res != ERROR_OK)
429  return res;
430  /* messages above are cloned to trace files for both cores,
431  * so reset acc time delta, both files have actual delta
432  * info */
433  cmd_data->sv_acc_time_delta = 0;
434  break;
435  default:
436  break;
437  }
438  }
439  return ERROR_OK;
440 }
441 
443  unsigned int core_id,
444  uint8_t *data,
445  uint32_t data_len)
446 {
447  struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
448 
449  LOG_DEBUG("sysview: Read from target %d bytes [%x %x %x %x]",
450  data_len,
451  data[0],
452  data[1],
453  data[2],
454  data[3]);
455  int res;
456  uint32_t processed = 0;
457  if (core_id >= ctx->cores_num) {
458  LOG_ERROR("sysview: Invalid core id %d in user block!", core_id);
459  return ERROR_FAIL;
460  }
461  if (cmd_data->mcore_format)
462  core_id = 0;
463  if (ctx->tot_len == 0) {
464  /* handle sync seq */
465  if (data_len < SYSVIEW_SYNC_LEN) {
466  LOG_ERROR("sysview: Invalid init seq len %d!", data_len);
467  return ERROR_FAIL;
468  }
469  LOG_DEBUG("sysview: Process %d sync bytes", SYSVIEW_SYNC_LEN);
470  uint8_t sync_seq[SYSVIEW_SYNC_LEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
471  if (memcmp(data, sync_seq, SYSVIEW_SYNC_LEN) != 0) {
472  LOG_ERROR("sysview: Invalid init seq [%x %x %x %x %x %x %x %x %x %x]",
473  data[0], data[1], data[2], data[3], data[4], data[5], data[6],
474  data[7], data[8], data[9]);
475  return ERROR_FAIL;
476  }
477  res = cmd_data->data_dests[core_id].write(cmd_data->data_dests[core_id].priv,
478  data,
480  if (res != ERROR_OK) {
481  LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!",
483  core_id);
484  return res;
485  }
486  if (!cmd_data->mcore_format) {
487  for (unsigned int i = 0; i < ctx->cores_num; i++) {
488  if (core_id == i)
489  continue;
490  res =
491  cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv,
492  data,
494  if (res != ERROR_OK) {
495  LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!", SYSVIEW_SYNC_LEN, core_id ? 0 : 1);
496  return res;
497  }
498  }
499  }
500  ctx->tot_len += SYSVIEW_SYNC_LEN;
501  processed += SYSVIEW_SYNC_LEN;
502  }
503  while (processed < data_len) {
504  unsigned int pkt_core_id;
505  uint32_t delta_len = 0;
506  uint32_t pkt_len = 0, delta = 0;
507  uint16_t event_id = esp_sysview_parse_packet(data + processed,
508  &pkt_len,
509  &pkt_core_id,
510  &delta,
511  &delta_len,
512  !cmd_data->mcore_format);
513  LOG_DEBUG("sysview: Process packet: core %d, %d id, %d bytes [%x %x %x %x]",
514  pkt_core_id,
515  event_id,
516  pkt_len,
517  data[processed + 0],
518  data[processed + 1],
519  data[processed + 2],
520  data[processed + 3]);
521  if (!cmd_data->mcore_format) {
523  pkt_core_id,
524  event_id,
525  delta,
526  delta_len,
527  pkt_len,
528  data + processed);
529  if (res != ERROR_OK)
530  return res;
531  } else {
532  res = cmd_data->data_dests[0].write(cmd_data->data_dests[0].priv, data + processed, pkt_len);
533  if (res != ERROR_OK) {
534  LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, 0);
535  return res;
536  }
537  }
538  if (event_id == SYSVIEW_EVTID_TRACE_STOP)
539  cmd_data->sv_trace_running = 0;
540  ctx->tot_len += pkt_len;
541  processed += pkt_len;
542  }
543  LOG_USER("%u ", ctx->tot_len);
544  /* check for stop condition */
545  if (ctx->tot_len > cmd_data->apptrace.skip_len &&
546  (ctx->tot_len - cmd_data->apptrace.skip_len >= cmd_data->apptrace.max_len)) {
547  ctx->running = 0;
548  if (duration_measure(&ctx->read_time) != 0) {
549  LOG_ERROR("Failed to stop trace read time measure!");
550  return ERROR_FAIL;
551  }
552  }
553  return ERROR_OK;
554 }
enum arm_mode mode
Definition: armv4_5.c:277
void command_print(struct command_invocation *cmd, const char *format,...)
Definition: command.c:443
void esp32_apptrace_cmd_args_parse(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct esp32_apptrace_cmd_data *cmd_data, const char **argv, int argc)
int esp32_apptrace_dest_cleanup(struct esp32_apptrace_dest dest[], unsigned int max_dests)
int esp32_apptrace_dest_init(struct esp32_apptrace_dest dest[], const char *dest_paths[], unsigned int max_dests)
int esp32_apptrace_cmd_ctx_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct command_invocation *cmd, int mode)
int esp32_apptrace_cmd_ctx_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx)
static void sysview_encode_u32(uint8_t **dest, uint32_t val)
static int esp32_sysview_write_packet(struct esp32_sysview_cmd_data *cmd_data, int pkt_core_id, uint32_t pkt_len, uint8_t *pkt_buf, uint32_t delta_len, uint8_t *delta_buf)
static uint32_t esp_sysview_decode_u32(uint8_t **ptr)
#define SYSVIEW_BLOCK_SIZE_OFFSET
Definition: esp32_sysview.c:26
static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf)
int esp32_sysview_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct command_invocation *cmd, int mode, bool mcore_format, const char **argv, int argc)
Definition: esp32_sysview.c:33
int esp32_sysview_process_data(struct esp32_apptrace_cmd_ctx *ctx, unsigned int core_id, uint8_t *data, uint32_t data_len)
static uint16_t esp_sysview_get_predef_payload_len(uint16_t id, uint8_t *pkt)
static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format)
static uint16_t esp_sysview_decode_plen(uint8_t **ptr)
#define SYSVIEW_WR_SIZE_OFFSET
Definition: esp32_sysview.c:27
static uint16_t esp_sysview_parse_packet(uint8_t *pkt_buf, uint32_t *pkt_len, unsigned int *pkt_core_id, uint32_t *delta, uint32_t *delta_len, bool clear_core_bit)
static int esp32_sysview_process_packet(struct esp32_apptrace_cmd_ctx *ctx, unsigned int pkt_core_id, uint16_t event_id, uint32_t delta, uint32_t delta_len, uint32_t pkt_len, uint8_t *pkt_buf)
static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len)
int esp32_sysview_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx)
#define ESP32_SYSVIEW_USER_BLOCK_LEN(_v_)
Definition: esp32_sysview.c:19
#define ESP32_SYSVIEW_USER_BLOCK_HDR_SZ
Definition: esp32_sysview.c:20
#define LOG_USER(expr ...)
Definition: log.h:135
#define LOG_WARNING(expr ...)
Definition: log.h:129
#define ERROR_FAIL
Definition: log.h:170
#define LOG_ERROR(expr ...)
Definition: log.h:132
#define LOG_DEBUG(expr ...)
Definition: log.h:109
#define ERROR_OK
Definition: log.h:164
#define SYSVIEW_EVTID_TASK_STOP_EXEC
#define SYSVIEW_EVTID_NUMMODULES
#define SYSVIEW_EVTID_STACK_INFO
#define SYSVIEW_EVTID_ISR_ENTER
#define SYSVIEW_EVTID_OVERFLOW
#define SYSVIEW_EVTID_IDLE
#define SYSVIEW_EVTID_SYSTIME_US
#define SYSVIEW_EVTID_TASK_START_EXEC
#define SYSVIEW_EVTID_TRACE_STOP
#define SYSVIEW_EVTID_TASK_START_READY
#define SYSVIEW_SYNC_LEN
#define SYSVIEW_EVTID_USER_START
#define SYSVIEW_EVTID_TRACE_START
#define SYSVIEW_MIN_VER_STRING
#define SYSVIEW_EVTID_USER_STOP
#define SYSVIEW_EVTID_TIMER_ENTER
#define SYSVIEW_EVTID_INIT
#define SYSVIEW_EVTID_SYSTIME_CYCLES
#define SYSVIEW_EVTID_MODULEDESC
#define SYSVIEW_EVTID_TIMER_EXIT
#define SYSVIEW_EVTID_TASK_STOP_READY
#define SYSVIEW_EVTID_SYSDESC
#define SYSVIEW_EVTID_ISR_EXIT
#define SYSVIEW_EVTID_TASK_INFO
#define SYSVIEW_EVTID_TASK_CREATE
#define SYSVIEW_EVTID_ISR_TO_SCHEDULER
When run_command is called, a new instance will be created on the stack, filled with the proper value...
Definition: command.h:76
struct duration read_time
struct esp32_apptrace_format trace_format
int(* write)(void *priv, uint8_t *data, int size)
int(* core_id_get)(struct target *target, uint8_t *hdr_buf)
uint32_t(* usr_block_len_get)(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len)
unsigned int sv_last_core_id
Definition: esp32_sysview.h:20
struct esp32_apptrace_cmd_data apptrace
Definition: esp32_sysview.h:16
struct esp32_apptrace_dest data_dests[ESP32_APPTRACE_MAX_CORES_NUM]
Definition: esp32_sysview.h:17
Definition: target.h:116
int duration_measure(struct duration *duration)
Update the duration->elapsed field to finish the duration measurement.
Definition: time_support.c:74
#define NULL
Definition: usb.h:16
uint8_t cmd
Definition: vdebug.c:1