Intel® Quark™ Microcontroller Software Interface 1.4.0 Intel® Quark™ Microcontroller BSP

- Main Page
- Modules
- Data Structures
- Files

- File List

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 */
467 void soc_watch_trigger_flush()
468 {
469 soc_watch_event_buffer_flush();
470 }
471 #endif /* !(defined(SOC_WATCH) && (!QM_SENSOR)) */
SOCW_EVENT_INTERRUPT
CPU interrupt generated.
Definition: soc_watch.h:38
SOCW_EVENT_SLEEP
Sleep mode entered.
Definition: soc_watch.h:39
soc_watch_log_event
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
soc_watch_trigger_flush
void soc_watch_trigger_flush()
Trigger a buffer flush via watchpoint.
Definition: soc_watch.c:467
SOCW_EVENT_HALT
CPU Halt.
Definition: soc_watch.h:37
soc_watch_event_t
soc_watch_event_t
Power profiling events enumeration.
Definition: soc_watch.h:36
soc_watch_log_app_event
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
SOCW_EVENT_MAX
End of events sentinel.
Definition: soc_watch.h:43