267 lines
6.4 KiB
C
267 lines
6.4 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <kernel_structs.h>
|
|
#include <logging/kernel_event_logger.h>
|
|
#include <misc/printk.h>
|
|
#include <misc/util.h>
|
|
#include <zephyr.h>
|
|
|
|
#include <systemview/SEGGER_SYSVIEW.h>
|
|
#include <rtt/SEGGER_RTT.h>
|
|
|
|
K_THREAD_STACK_DEFINE(printer_stack, 1024);
|
|
K_THREAD_STACK_DEFINE(calc_stack, 1024);
|
|
K_THREAD_STACK_DEFINE(sysview_stack, 1024);
|
|
|
|
static struct k_thread printer_thread_data;
|
|
static struct k_thread calc_thread_data;
|
|
static struct k_thread sysview_thread_data;
|
|
|
|
static u32_t timestamp, interrupt;
|
|
|
|
extern SEGGER_RTT_CB _SEGGER_RTT;
|
|
|
|
u32_t sysview_get_timestamp(void)
|
|
{
|
|
return timestamp;
|
|
}
|
|
|
|
u32_t sysview_get_interrupt(void)
|
|
{
|
|
return interrupt;
|
|
}
|
|
|
|
static void publish_context_switch(u32_t *event_data)
|
|
{
|
|
#if defined(CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH)
|
|
SEGGER_SYSVIEW_OnTaskStartExec(event_data[1]);
|
|
#endif
|
|
}
|
|
|
|
static void publish_interrupt(u32_t *event_data)
|
|
{
|
|
#if defined(CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT)
|
|
interrupt = event_data[1];
|
|
|
|
/* FIXME: RecordEnter and RecordExit seem to be required to keep
|
|
* SystemView happy; however, there's currently no way to measure
|
|
* the time it takes for an ISR to execute.
|
|
*/
|
|
SEGGER_SYSVIEW_RecordEnterISR();
|
|
SEGGER_SYSVIEW_RecordExitISR();
|
|
#endif
|
|
}
|
|
|
|
static void publish_sleep(u32_t *event_data)
|
|
{
|
|
#if defined(CONFIG_KERNEL_EVENT_LOGGER_SLEEP)
|
|
SEGGER_SYSVIEW_OnIdle();
|
|
#endif
|
|
}
|
|
|
|
static void publish_task(u32_t *event_data)
|
|
{
|
|
#if defined(CONFIG_KERNEL_EVENT_LOGGER_THREAD)
|
|
u32_t thread_id;
|
|
|
|
thread_id = event_data[1];
|
|
|
|
switch ((enum sys_k_event_logger_thread_event)event_data[2]) {
|
|
case KERNEL_LOG_THREAD_EVENT_READYQ:
|
|
SEGGER_SYSVIEW_OnTaskStartReady(thread_id);
|
|
break;
|
|
case KERNEL_LOG_THREAD_EVENT_PEND:
|
|
SEGGER_SYSVIEW_OnTaskStopReady(thread_id, 3<<3);
|
|
break;
|
|
case KERNEL_LOG_THREAD_EVENT_EXIT:
|
|
SEGGER_SYSVIEW_OnTaskTerminate(thread_id);
|
|
break;
|
|
}
|
|
|
|
/* FIXME: Maybe we need a KERNEL_LOG_THREAD_EVENT_CREATE? */
|
|
#endif
|
|
}
|
|
|
|
static void send_system_desc(void)
|
|
{
|
|
SEGGER_SYSVIEW_SendSysDesc("N=ZephyrSysViewSample");
|
|
SEGGER_SYSVIEW_SendSysDesc("D=" CONFIG_BOARD " "
|
|
CONFIG_SOC " " CONFIG_ARCH);
|
|
SEGGER_SYSVIEW_SendSysDesc("O=Zephyr");
|
|
}
|
|
|
|
static void sysview_api_send_task_list(void)
|
|
{
|
|
struct k_thread *thr;
|
|
|
|
for (thr = _kernel.threads; thr; thr = thr->next_thread) {
|
|
char name[20];
|
|
|
|
snprintk(name, sizeof(name), "T%xE%x", (uintptr_t)thr,
|
|
(uintptr_t)thr->entry);
|
|
|
|
/* NOTE: struct k_thread is inside the stack on Zephyr 1.7.
|
|
* This is not guaranteed by the API, and is likely to change
|
|
* in the future. Hence, StackBase/StackSize are not set here;
|
|
* these could be stored as part of the kernel event.
|
|
*/
|
|
SEGGER_SYSVIEW_SendTaskInfo(&(SEGGER_SYSVIEW_TASKINFO) {
|
|
.TaskID = (u32_t)(uintptr_t)thr,
|
|
.sName = name,
|
|
.Prio = thr->base.prio,
|
|
});
|
|
}
|
|
}
|
|
|
|
static u32_t zephyr_to_sysview(int event_type)
|
|
{
|
|
static const u32_t lut[] = {
|
|
[KERNEL_EVENT_LOGGER_CONTEXT_SWITCH_EVENT_ID] =
|
|
SYSVIEW_EVTMASK_TASK_START_EXEC,
|
|
[KERNEL_EVENT_LOGGER_INTERRUPT_EVENT_ID] =
|
|
SYSVIEW_EVTMASK_ISR_ENTER |
|
|
SYSVIEW_EVTMASK_ISR_EXIT,
|
|
[KERNEL_EVENT_LOGGER_SLEEP_EVENT_ID] =
|
|
SYSVIEW_EVTMASK_IDLE,
|
|
[KERNEL_EVENT_LOGGER_THREAD_EVENT_ID] =
|
|
SYSVIEW_EVTMASK_TASK_CREATE |
|
|
SYSVIEW_EVTMASK_TASK_START_READY |
|
|
SYSVIEW_EVTMASK_TASK_STOP_READY |
|
|
SYSVIEW_EVTMASK_TASK_STOP_EXEC,
|
|
};
|
|
|
|
return lut[event_type];
|
|
}
|
|
|
|
#define MUST_LOG(event) \
|
|
(IS_ENABLED(CONFIG_ ## event) && \
|
|
sys_k_must_log_event(event ## _EVENT_ID))
|
|
|
|
#define ENABLE_SYSVIEW_EVENT(event) \
|
|
(MUST_LOG(event) ? zephyr_to_sysview(event ## _EVENT_ID) : 0)
|
|
|
|
static void sysview_setup(void)
|
|
{
|
|
static const SEGGER_SYSVIEW_OS_API api = {
|
|
.pfGetTime = NULL, /* sysview_get_timestamp() used instead */
|
|
.pfSendTaskList = sysview_api_send_task_list,
|
|
};
|
|
u32_t evs;
|
|
|
|
printk("RTT block address is %p\n", &_SEGGER_RTT);
|
|
|
|
/* NOTE: SysView does not support dynamic frequency scaling */
|
|
SEGGER_SYSVIEW_Init(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC,
|
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC,
|
|
&api, send_system_desc);
|
|
|
|
#if defined(CONFIG_PHYS_RAM_ADDR) /* x86 */
|
|
SEGGER_SYSVIEW_SetRAMBase(CONFIG_PHYS_RAM_ADDR);
|
|
#elif defined(CONFIG_SRAM_BASE_ADDRESS) /* arm, default */
|
|
SEGGER_SYSVIEW_SetRAMBase(CONFIG_SRAM_BASE_ADDRESS);
|
|
#else
|
|
/* Setting RAMBase is just an optimization: this value is subtracted
|
|
* from all pointers in order to save bandwidth. It's not an error
|
|
* if a platform does not set this value.
|
|
*/
|
|
#endif
|
|
|
|
evs = SYSVIEW_EVTMASK_PRINT_FORMATTED |
|
|
ENABLE_SYSVIEW_EVENT(KERNEL_EVENT_LOGGER_SLEEP) |
|
|
ENABLE_SYSVIEW_EVENT(KERNEL_EVENT_LOGGER_CONTEXT_SWITCH) |
|
|
ENABLE_SYSVIEW_EVENT(KERNEL_EVENT_LOGGER_INTERRUPT) |
|
|
ENABLE_SYSVIEW_EVENT(KERNEL_EVENT_LOGGER_THREAD);
|
|
SEGGER_SYSVIEW_EnableEvents(evs);
|
|
}
|
|
|
|
#undef ENABLE_SYSVIEW_EVENT
|
|
#undef MUST_LOG
|
|
|
|
static void sysview_thread(void)
|
|
{
|
|
sysview_setup();
|
|
|
|
for (;;) {
|
|
u32_t event_data[4];
|
|
u16_t event_id;
|
|
u8_t dropped;
|
|
u8_t event_data_size = (u8_t)ARRAY_SIZE(event_data);
|
|
int ret;
|
|
|
|
ret = sys_k_event_logger_get_wait(&event_id,
|
|
&dropped,
|
|
event_data,
|
|
&event_data_size);
|
|
if (ret < 0) {
|
|
continue;
|
|
}
|
|
|
|
timestamp = event_data[0];
|
|
|
|
switch (event_id) {
|
|
case KERNEL_EVENT_LOGGER_CONTEXT_SWITCH_EVENT_ID:
|
|
publish_context_switch(event_data);
|
|
break;
|
|
case KERNEL_EVENT_LOGGER_INTERRUPT_EVENT_ID:
|
|
publish_interrupt(event_data);
|
|
break;
|
|
case KERNEL_EVENT_LOGGER_SLEEP_EVENT_ID:
|
|
publish_sleep(event_data);
|
|
break;
|
|
case KERNEL_EVENT_LOGGER_THREAD_EVENT_ID:
|
|
publish_task(event_data);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void printer_thread(void)
|
|
{
|
|
for (;;) {
|
|
SEGGER_SYSVIEW_Print("Printer thread says hello");
|
|
k_sleep(MSEC_PER_SEC);
|
|
}
|
|
}
|
|
|
|
static void calc_thread(void)
|
|
{
|
|
int denom = 0;
|
|
|
|
for (;;) {
|
|
const int val = 0xbebacafe;
|
|
|
|
denom = (denom + 1) % 16;
|
|
|
|
if (denom == 0) {
|
|
SEGGER_SYSVIEW_Warn("Not calculating: denom is 0");
|
|
} else {
|
|
SEGGER_SYSVIEW_PrintfHost("Calculated: %d",
|
|
val / denom);
|
|
}
|
|
|
|
k_sleep(MSEC_PER_SEC);
|
|
}
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
k_thread_create(&sysview_thread_data, sysview_stack,
|
|
K_THREAD_STACK_SIZEOF(sysview_stack),
|
|
(k_thread_entry_t)sysview_thread,
|
|
NULL, NULL, NULL, K_PRIO_COOP(1), 0, 0);
|
|
|
|
k_thread_create(&printer_thread_data, printer_stack,
|
|
K_THREAD_STACK_SIZEOF(printer_stack),
|
|
(k_thread_entry_t)printer_thread,
|
|
NULL, NULL, NULL, K_PRIO_COOP(1), 0, 0);
|
|
|
|
k_thread_create(&calc_thread_data, calc_stack,
|
|
K_THREAD_STACK_SIZEOF(calc_stack),
|
|
(k_thread_entry_t)calc_thread,
|
|
NULL, NULL, NULL, K_PRIO_COOP(1), 0, 0);
|
|
}
|