Intel® Quark™ Microcontroller Software Interface  1.4.0
Intel® Quark™ Microcontroller BSP
soc_watch.c
1 /*
2  * {% copyright %}
3  */
4 
5 /*
6  * SoC Watch - QMSI power profiler
7  */
8 
9 #if (SOC_WATCH_ENABLE)
10 
11 /*
12  * Header files common to LMT and ARC Sensor.
13  */
14 #include "soc_watch.h"
15 #include "qm_common.h"
16 #include "qm_soc_regs.h"
17 
18 /*
19  * Header files and macro defines specific to LMT and ARC Sensor.
20  */
21 #if (!QM_SENSOR)
22 #include <x86intrin.h>
23 /* 64bit Timestamp counter. */
24 #define get_ticks() _rdtsc()
25 #else
26 #include "qm_sensor_regs.h"
27 /* Timestamp counter for sensor subsystem is 32bit. */
28 #define get_ticks() __builtin_arc_lr(QM_SS_TSC_BASE + QM_SS_TIMER_COUNT)
29 #endif
30 /*
31  * Define a macro for exposing some functions and other definitions
32  * only when unit testing. If we're not unit testing, then declare
33  * them as static, so that their declarations are hidden to normal
34  * code.
35  */
36 #if (UNIT_TEST)
37 #define NONUTSTATIC
38 #else
39 #define NONUTSTATIC static
40 #endif
41 
42 /**
43  * "Event strings" table, describing message structures.
44  * The first character is the event code to write to the data file.
45  * The 2nd and subsequent characters describe how to format the record's
46  * data. Note that the ordering here needs to agree with the
47  * enumeration list in qmsw_stub.h.
48  *
49  * Table characters:
50  * + First char = event code to write into the result file.
51  * + T = TSC Timestamp (Hi-res timestamp)
52  * + t = RTC Timestamp (lo-res timestamp)
53  * + 1 = interpret ev_data as a 1-byte value
54  * + 4 = interpret ev_data as a 4-byte value
55  * + R = Using ev_data as a register enumeration, read that register,
56  * + and put that 4-byte value into the data file.
57  * + L = Trigger an RTC timestamp Later
58  */
59 NONUTSTATIC const char *ev_strs[] = {
60  "HT", /* Halt event */
61  "IT1", /* Interrupt event */
62  "STtL", /* Sleep event */
63  "RT1R", /* Register read event: Timestamp, reg enum, reg value*/
64  "UTs4", /* User event: timestamp, subtype, data value. */
65  "FTf", /* Frequency change event */
66 };
67 
68 /*
69  * This list of registers corresponds to the SoC Watch register ID
70  * enumeration in soc_watch.h, and MUST STAY IN AGREEMENT with that
71  * list, since that enumeration is used to index this list.
72  *
73  * To record a register value, the SoC Watch code indexes into this
74  * array, and reads the corresponding address found in that slot.
75  */
76 #if (QUARK_D2000)
77 static const uint32_t *platform_regs[] = {
78  (uint32_t *)(&QM_SCSS_CCU->osc0_cfg1),
79  (uint32_t *)(&QM_SCSS_CCU->ccu_lp_clk_ctl),
80  (uint32_t *)(&QM_SCSS_CCU->ccu_sys_clk_ctl),
81  /* Clock Gating Registers */
82  (uint32_t *)(&QM_SCSS_CCU->ccu_periph_clk_gate_ctl),
83  (uint32_t *)(&QM_SCSS_CCU->ccu_ext_clock_ctl),
84  /* Power Consumption regs */
85  (uint32_t *)(&QM_SCSS_CMP->cmp_pwr),
86  (uint32_t *)(&QM_SCSS_PMUX->pmux_pullup),
87  (uint32_t *)(&QM_SCSS_PMUX->pmux_slew),
88  (uint32_t *)(&QM_SCSS_PMUX->pmux_in_en)};
89 #elif(QUARK_SE)
90 static const uint32_t *platform_regs[] = {
91  (uint32_t *)(&QM_SCSS_CCU->osc0_cfg1),
92  (uint32_t *)(&QM_SCSS_CCU->ccu_lp_clk_ctl),
93  (uint32_t *)(&QM_SCSS_CCU->ccu_sys_clk_ctl),
94  /* Clock Gating Registers */
95  (uint32_t *)(&QM_SCSS_CCU->ccu_periph_clk_gate_ctl),
96  (uint32_t *)(&QM_SCSS_CCU->ccu_ss_periph_clk_gate_ctl),
97  (uint32_t *)(&QM_SCSS_CCU->ccu_ext_clock_ctl),
98  /* Power Consumption regs */
99  (uint32_t *)(&QM_SCSS_CMP->cmp_pwr), (uint32_t *)(&QM_SCSS_PMU->slp_cfg),
100  (uint32_t *)(&QM_SCSS_PMUX->pmux_pullup),
101  (uint32_t *)(&QM_SCSS_PMUX->pmux_pullup[1]),
102  (uint32_t *)(&QM_SCSS_PMUX->pmux_pullup[2]),
103  (uint32_t *)(&QM_SCSS_PMUX->pmux_pullup[3]),
104  (uint32_t *)(&QM_SCSS_PMUX->pmux_slew),
105  (uint32_t *)(&QM_SCSS_PMUX->pmux_slew[1]),
106  (uint32_t *)(&QM_SCSS_PMUX->pmux_slew[2]),
107  (uint32_t *)(&QM_SCSS_PMUX->pmux_slew[3]),
108  (uint32_t *)(&QM_SCSS_PMUX->pmux_in_en),
109  (uint32_t *)(&QM_SCSS_PMUX->pmux_in_en[1]),
110  (uint32_t *)(&QM_SCSS_PMUX->pmux_in_en[2]),
111  (uint32_t *)(&QM_SCSS_PMUX->pmux_in_en[3])};
112 #endif /* QUARK_SE */
113 
114 /* Define VERBOSE to turn on printf-based logging */
115 #ifdef VERBOSE
116 #define SOC_WATCH_TRACE QM_PRINTF
117 #else
118 #define SOC_WATCH_TRACE(...)
119 #endif
120 
121 /*
122  * mlog routines -- low-level memory debug logging.
123  * Only enable if there's a need to debug this module.
124  */
125 #ifdef MLOG_ENABLE
126 #define MLOG(e) mlog(e)
127 #define MLOG_BYTE(b) mlog_byte(b)
128 #define MLOG_SIZE 512 /* Must be a power of 2 */
129 static uint8_t mlog_events[MLOG_SIZE];
130 static uint16_t mlog_idx = 0;
131 void mlog(uint8_t event)
132 {
133  mlog_events[++mlog_idx % (MLOG_SIZE)] = event;
134 }
135 void mlog_byte(uint8_t byte)
136 {
137  const char c[] = {"0123456789ABCDEF"};
138  MLOG(c[byte >> 4]);
139  MLOG(c[byte & 4]);
140 }
141 #else /* !MLOG_ENABLE */
142 #define MLOG(event)
143 #define MLOG_BYTE(b)
144 #endif /* !MLOG_ENABLE */
145 
146 /*
147  * Defines for frequency related platform registers.
148  */
149 #define SW_OSC0_CFG1 (0)
150 #define SW_SYS_CLK_CTL (2)
151 
152 /*
153  * CONFIGURABLE: Set this to control the number of bytes of RAM you
154  * want to dedicate to event buffering. The larger the buffer,
155  * the fewer (expensive) flushes we will have to do. The smaller,
156  * the lower the memory cost, but the more flushes you will do.
157  */
158 #define SOC_WATCH_EVENT_BUFFER_SIZE (256) /* Measured in bytes */
159 
160 /**
161  * Power profiling event data buffer. Symbol must be globally
162  * visible, so that it can be seen by external tools.
163  */
164 struct sw_profiling_event_buffer {
165  uint8_t eb_idx; /* Index of next byte to be written */
166  uint8_t eb_size; /* Buffer size == SOC_WATCH_EVENT_BUFFER_SIZE */
167  uint8_t event_data[SOC_WATCH_EVENT_BUFFER_SIZE - 2]; /* Event data -
168  sizeof(idx + size) */
169 } soc_watch_event_buffer = {0, SOC_WATCH_EVENT_BUFFER_SIZE - 1, {0}};
170 
171 /* High water mark, i.e. "start trying to flush" point. */
172 #define SW_EB_HIGH_WATER (((SOC_WATCH_EVENT_BUFFER_SIZE - 2) * 7) >> 3)
173 
174 NONUTSTATIC int soc_watch_buffer_full(void)
175 {
176  return (soc_watch_event_buffer.eb_idx >= SW_EB_HIGH_WATER);
177 }
178 
179 /**
180  * Flag used by the JTAG data extraction routine. During setup, a HW
181  * watchpoint is placed on this address. During the flush routine,
182  * software writes to it, causing the HW watchpoint to fire, and
183  * OpenOCD to extract the data. This symbol MUST be globally visible
184  * in order for JTAG data transfer to work.
185  */
186 volatile uint8_t soc_watch_flush_flag = 0;
187 
188 /*
189  * soc_watch_event_buffer_flush -- Trigger the data buffer flush.
190  */
191 static void soc_watch_event_buffer_flush(void)
192 {
193 /* Figure out if we can successfully flush the data out.
194  * If we're sleeping, the JTAG query will fail.
195  */
196 #if (QUARK_D2000)
197  /**
198  * If the "we're going to sleep" bit is set, parts of the
199  * SOC are already asleep, and transferring data over
200  * the JTAG port is not always reliable. So defer transferring
201  * the data until later.
202  * @TODO: Determine if there is also a sensitivity to the
203  * clock rate.
204  */
205  MLOG('F');
206  if (QM_SCSS_PMU->aon_vr & QM_AON_VR_VREG_SEL) {
207  MLOG('-');
208  return; /* We would only send junk, so don't flush. */
209  }
210 #endif
211 
212  soc_watch_flush_flag = 1; /* Trigger the data extract brkpt */
213  soc_watch_event_buffer.eb_idx = 0;
214  MLOG('+');
215 }
216 
217 /* Store a byte in the event buffer. */
218 static void eb_write_char(uint8_t data)
219 {
220  SOC_WATCH_TRACE("c%d:0x%x [0]=%x\n", soc_watch_event_buffer.eb_idx,
221  data, soc_watch_event_buffer.event_data[0]);
222  soc_watch_event_buffer.event_data[soc_watch_event_buffer.eb_idx++] =
223  data;
224 }
225 
226 /* Store a word in the event buffer. */
227 static void eb_write_uint32(uint32_t *data)
228 {
229  uint32_t dst_data = *data;
230  uint8_t byte_count = 0;
231  SOC_WATCH_TRACE("I%d:0x%x\n", soc_watch_event_buffer.eb_idx, *data);
232  while (byte_count < sizeof(uint32_t)) {
233  soc_watch_event_buffer
234  .event_data[soc_watch_event_buffer.eb_idx++] =
235  ((dst_data & 0xFF));
236  dst_data = dst_data >> 8;
237  byte_count++;
238  }
239 }
240 
241 /*
242  * soc_watch is duplicating the implementation of qm_irq_lock/unlock APIs
243  * for both sensor and x86. This is required to remove the dependency on
244  * qm_interrupt.h
245  * Reason: soc_watch driver is common to both zephyr and QMSI based applications
246  * and zephyr
247  * has it's own interrupt handling implementation.
248  * Both the functions are added as static inline and hence no side-effects.
249  */
250 
251 #if (QM_SENSOR)
252 static inline unsigned int soc_watch_irq_lock(void)
253 {
254  unsigned int key = 0;
255 
256  /*
257  * Store the ARC STATUS32 register fields relating to interrupts into
258  * the variable `key' and disable interrupt delivery to the core.
259  */
260  __asm__ __volatile__("clri %0" : "=r"(key));
261 
262  return key;
263 }
264 
265 static inline void soc_watch_irq_unlock(unsigned int key)
266 {
267  /*
268  * Restore the ARC STATUS32 register fields relating to interrupts based
269  * on the variable `key' populated by qm_irq_lock().
270  */
271  __asm__ __volatile__("seti %0" : : "ir"(key));
272 }
273 
274 #else /* x86 */
275 /* x86 CPU FLAGS.IF register field (Interrupt enable Flag, bit 9), indicating
276  * whether or not CPU interrupts are enabled.
277  */
278 #define X86_FLAGS_IF BIT(9)
279 
280 /**
281  * Save interrupt state and disable all interrupts on the CPU.
282  * Defined locally for modularity when used in other contexts (i.e. RTOS)
283  *
284  * @return An architecture-dependent lock-out key representing the "interrupt
285  * disable state" prior to the call.
286  */
287 static inline unsigned int soc_watch_irq_lock(void)
288 {
289  unsigned int key = 0;
290 
291  /*
292  * Store the CPU FLAGS register into the variable `key' and disable
293  * interrupt delivery to the core.
294  */
295  __asm__ __volatile__("pushfl;\n\t"
296  "cli;\n\t"
297  "popl %0;\n\t"
298  : "=g"(key)
299  :
300  : "memory");
301 
302  return key;
303 }
304 
305 /**
306  *
307  * Restore previous interrupt state on the CPU saved via soc_watch_irq_lock().
308  * Defined locally for modularity when used in other contexts (i.e. RTOS)
309  *
310  * @param[in] key architecture-dependent lock-out key returned by a previous
311  * invocation of soc_watch_irq_lock().
312  */
313 static inline void soc_watch_irq_unlock(unsigned int key)
314 {
315  /*
316  * `key' holds the CPU FLAGS register content at the time when
317  * soc_watch_irq_lock() was called.
318  */
319  if (!(key & X86_FLAGS_IF)) {
320  /*
321  * Interrupts were disabled when soc_watch_irq_lock() was
322  * invoked:
323  * do not re-enable interrupts.
324  */
325  return;
326  }
327 
328  /* Enable interrupts */
329  __asm__ __volatile__("sti;\n\t" : :);
330 }
331 #endif
332 
333 /* Log an event with one parameter. */
334 void soc_watch_log_event(soc_watch_event_t event_id, uintptr_t ev_data)
335 {
336  soc_watch_log_app_event(event_id, 0, ev_data);
337 }
338 
339 /*
340  * Log an event with two parameters, where the subtype comes from
341  * the user. Note that what actually makes this an 'application event' is
342  * the event_id, not the fact that it is coming in via this interface.
343  */
344 void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype,
345  uintptr_t ev_data)
346 {
347  static uint8_t record_rtc = 0;
348  const uint32_t *rtc_ctr = (uint32_t *)&QM_RTC[QM_RTC_0]->rtc_ccvr;
349  const char *cp;
350  unsigned int irq_flag = 0;
351 #if (!QM_SENSOR)
352  uint64_t tsc = 0; /* hi-res timestamp */
353 #else
354  uint32_t tsc = 0;
355 #endif
356  uint32_t rtc_val = *rtc_ctr;
357 
358 #define AVG_EVENT_SIZE 8 /* Size of a typical message in bytes. */
359 
360  MLOG('[');
361  irq_flag = soc_watch_irq_lock();
362  /* TODO: We know exactly how many bytes of storage we need,
363  * since we know the event code. So don't do an "AVG" size thing
364  * here--use the exact size!
365  */
366  if ((soc_watch_event_buffer.eb_idx + AVG_EVENT_SIZE) <=
367  soc_watch_event_buffer.eb_size) {
368 
369 /* Map a halt event to a sleep event where appropriate. */
370 #if (QUARK_D2000)
371  if (event_id == SOCW_EVENT_HALT) {
372  if (QM_SCSS_PMU->aon_vr & QM_AON_VR_VREG_SEL) {
373  event_id = SOCW_EVENT_SLEEP;
374  }
375  }
376 #endif
377 
378  /* Record the RTC of the waking event, if it's rousing us from
379  * sleep. */
380  if (record_rtc) {
381  eb_write_char('t');
382  eb_write_uint32((uint32_t *)(&rtc_val)); /* Timestamp */
383  record_rtc = 0;
384  }
385 
386  if (event_id >= SOCW_EVENT_MAX) {
387  SOC_WATCH_TRACE("Unknown event id: 0x%x\n", event_id);
388  MLOG('?');
389  soc_watch_irq_unlock(irq_flag);
390  return;
391  }
392  cp = ev_strs[event_id]; /* Look up event string */
393  SOC_WATCH_TRACE("%c", *cp);
394  MLOG(*cp);
395  eb_write_char(*cp); /* Write event code */
396  while (*++cp) {
397  switch (*cp) {
398  case 'T':
399  tsc = get_ticks();
400  eb_write_uint32((uint32_t *)(&tsc)); /* Hi-res
401  Timestamp */
402  break;
403  case 't':
404  eb_write_uint32(
405  (uint32_t *)(&rtc_val)); /* Lo-res
406  Timestamp */
407  break;
408  case 'L':
409  record_rtc = 1;
410  break;
411  case 'R': /* Register data value */
412  eb_write_uint32(
413  (uint32_t *)platform_regs[ev_data]);
414  break;
415  case '4': /* 32-bit data value */
416  eb_write_uint32((uint32_t *)&ev_data);
417  break;
418  case '1':
419  /* Register ID */
420  eb_write_char(((uint32_t)ev_data) & 0xff);
421  break;
422  case 's':
423  /* Event subtype */
424  eb_write_char(((uint32_t)ev_subtype) & 0xff);
425  break;
426  case 'f':
427  eb_write_uint32(
428  (uint32_t *)platform_regs[SW_OSC0_CFG1]);
429  eb_write_uint32(
430  (uint32_t *)platform_regs[SW_SYS_CLK_CTL]);
431  break;
432  default:
433  SOC_WATCH_TRACE(
434  "Unknown string char: 0x%x on string "
435  "0x%x\n",
436  *cp, event_id);
437  break;
438  }
439  }
440  }
441 
442  /*
443  * If this is an interrupt which roused the CPU out of a sleep state,
444  * don't flush the buffer. (Due to a bug in OpenOCD, doing so will
445  * clear the HW watchpoint, ensuring no further flushes are seen by
446  * OpenOCD.)
447  */
448  if ((soc_watch_buffer_full()) && (event_id != SOCW_EVENT_INTERRUPT)) {
449  SOC_WATCH_TRACE("\n --- FLUSH: idx= %d ---\n",
450  soc_watch_event_buffer.eb_idx);
451  soc_watch_event_buffer_flush();
452  }
453  MLOG(':');
454  MLOG_BYTE(soc_watch_event_buffer.eb_idx);
455  soc_watch_irq_unlock(irq_flag);
456  MLOG(']');
457 }
458 
459 /*
460  * Trigger the Watchpoint to flush the data.
461  * Application can use this API to trigger the transfer of
462  * profiler information to the host whenever it requires.
463  * The static function soc_watch_event_buffer_flush() is also used internally
464  * when the soc_watch_buffer_full flag is set and is not exposed to the
465  * application.
466  */
468 {
469  soc_watch_event_buffer_flush();
470 }
471 #endif /* !(defined(SOC_WATCH) && (!QM_SENSOR)) */
CPU interrupt generated.
Definition: soc_watch.h:38
Sleep mode entered.
Definition: soc_watch.h:39
void soc_watch_log_event(soc_watch_event_t event_id, uintptr_t ev_data)
Log a power profile event.
Definition: soc_watch.c:334
void soc_watch_trigger_flush()
Trigger a buffer flush via watchpoint.
Definition: soc_watch.c:467
CPU Halt.
Definition: soc_watch.h:37
soc_watch_event_t
Power profiling events enumeration.
Definition: soc_watch.h:36
void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype, uintptr_t ev_data)
Log an application event via the power profile logger.
Definition: soc_watch.c:344
End of events sentinel.
Definition: soc_watch.h:43