Skip to content

Commit

Permalink
ext qmsi: Add config support and kernel events for enabling SoCWatch
Browse files Browse the repository at this point in the history
tests: power_states app updated to support SoCWatch collection

Add CONFIG_SOC_WATCH option for enabling the SoCWatch
 QMSI driver and associated instrumentation hooks.

Bug fix for soc_watch.c to use local irq_lock/unlock
 This will be put into QMSI as well.

JIRA: ZEP-1121

Change-Id: I0514324e81ca02c1d01ffc2d6cf4d31aee491544
Signed-off-by: Jon Moeller <[email protected]>
  • Loading branch information
Jon Moeller authored and Anas Nashif committed Nov 12, 2016
1 parent ff23cb5 commit 8358468
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 4 deletions.
1 change: 1 addition & 0 deletions ext/hal/qmsi/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ obj-$(CONFIG_SPI_QMSI_SS) += drivers/spi/qm_ss_spi.o
obj-$(CONFIG_GPIO_QMSI_SS) += drivers/gpio/qm_ss_gpio.o
obj-$(CONFIG_I2C_QMSI_SS) += drivers/i2c/qm_ss_i2c.o
obj-$(CONFIG_ADC_QMSI_SS) += drivers/adc/qm_ss_adc.o
obj-$(CONFIG_SOC_WATCH) += drivers/soc_watch.o
7 changes: 7 additions & 0 deletions ext/hal/qmsi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,11 @@ config QMSI_INSTALL_PATH
installed. Make sure this option is properly set when QMSI_LIBRARY
is enabled otherwise the build will fail.

config SOC_WATCH
bool "Enable SoCWatch drivers and related instrumentation"
default n
depends on KERNEL_EVENT_LOGGER
help
This option enables the SoCWatch driver and related instrumentation.

endif
6 changes: 6 additions & 0 deletions ext/hal/qmsi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ endif
ifdef CONFIG_SYS_POWER_DEEP_SLEEP
KBUILD_CPPFLAGS +=-DENABLE_RESTORE_CONTEXT
endif

SOC_WATCH_ENABLE ?= 0
ifeq ($(CONFIG_SOC_WATCH),y)
SOC_WATCH_ENABLE := 1
CFLAGS += -DSOC_WATCH_ENABLE
endif
64 changes: 60 additions & 4 deletions ext/hal/qmsi/drivers/soc_watch.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#include <x86intrin.h>
#include "qm_common.h"
#include "qm_soc_regs.h"
#include "qm_interrupt.h"
#include "soc_watch.h"

/*
Expand Down Expand Up @@ -238,6 +237,62 @@ static void eb_write_uint32(uint32_t *data)
soc_watch_event_buffer.eb_idx += sizeof(uint32_t);
}

/* x86 CPU FLAGS.IF register field (Interrupt enable Flag, bit 9), indicating
* whether or not CPU interrupts are enabled.
*/
#define X86_FLAGS_IF BIT(9)

/**
* Save interrupt state and disable all interrupts on the CPU.
* Defined locally for modularity when used in other contexts (i.e. RTOS)
*
* @return An architecture-dependent lock-out key representing the "interrupt
* disable state" prior to the call.
*/
static inline unsigned int soc_watch_irq_lock(void)
{
unsigned int key = 0;

/*
* Store the CPU FLAGS register into the variable `key' and disable
* interrupt delivery to the core.
*/
__asm__ __volatile__("pushfl;\n\t"
"cli;\n\t"
"popl %0;\n\t"
: "=g"(key)
:
: "memory");

return key;
}

/**
*
* Restore previous interrupt state on the CPU saved via soc_watch_irq_lock().
* Defined locally for modularity when used in other contexts (i.e. RTOS)
*
* @param[in] key architecture-dependent lock-out key returned by a previous
* invocation of soc_watch_irq_lock().
*/
static inline void soc_watch_irq_unlock(unsigned int key)
{
/*
* `key' holds the CPU FLAGS register content at the time when
* soc_watch_irq_lock() was called.
*/
if (!(key & X86_FLAGS_IF)) {
/*
* Interrupts were disabled when soc_watch_irq_lock() was invoked:
* do not re-enable interrupts.
*/
return;
}

/* Enable interrupts */
__asm__ __volatile__("sti;\n\t" : :);
}

/* Log an event with one parameter. */
void soc_watch_log_event(soc_watch_event_t event_id, uintptr_t ev_data)
{
Expand All @@ -255,13 +310,14 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype,
static uint8_t record_rtc = 0;
const uint32_t *rtc_ctr = (uint32_t *)&QM_RTC->rtc_ccvr;
const char *cp;
unsigned int irq_flag = 0;
uint64_t tsc = __builtin_ia32_rdtsc(); /* Grab hi-res timestamp */
uint32_t rtc_val = *rtc_ctr;

#define AVG_EVENT_SIZE 8 /* Size of a typical message in bytes. */

MLOG('[');
qm_irq_disable();
irq_flag = soc_watch_irq_lock();
/* TODO: We know exactly how many bytes of storage we need,
* since we know the event code. So don't do an "AVG" size thing
* here--use the exact size!
Expand Down Expand Up @@ -289,7 +345,7 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype,
if (event_id >= SOCW_EVENT_MAX) {
SOC_WATCH_TRACE("Unknown event id: 0x%x\n", event_id);
MLOG('?');
qm_irq_enable();
soc_watch_irq_unlock(irq_flag);
return;
}
cp = ev_strs[event_id]; /* Look up event string */
Expand Down Expand Up @@ -348,7 +404,7 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype,
}
MLOG(':');
MLOG_BYTE(soc_watch_event_buffer.eb_idx);
qm_irq_enable();
soc_watch_irq_unlock(irq_flag);
MLOG(']');
}

Expand Down
32 changes: 32 additions & 0 deletions tests/power/power_states/prj_socwatch.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
CONFIG_ARC_INIT=n
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_DEEP_SLEEP=n
CONFIG_SYS_POWER_LOW_POWER_STATE=y
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_TICKLESS_IDLE=y

# RTC Wake Event
CONFIG_RTC=y

# COUNTER Wake Event
CONFIG_COUNTER=n

# GPIO 1 Wake Event
CONFIG_GPIO=n
CONFIG_GPIO_QMSI=n
CONFIG_GPIO_QMSI_1=n
CONFIG_GPIO_QMSI_1_NAME="GPIO_1"
CONFIG_GPIO_QMSI_1_PRI=2

# Comparator Wake Event
CONFIG_AIO_COMPARATOR=n

# Enable SoCWatch power event tracing
CONFIG_SOC_WATCH=y
CONFIG_RING_BUFFER=y
CONFIG_NANO_TIMEOUTS=y
CONFIG_IDLE_STACK_SIZE=512
CONFIG_KERNEL_EVENT_LOGGER=y
CONFIG_KERNEL_EVENT_LOGGER_BUFFER_SIZE=16
CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH=y
CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT=y
1 change: 1 addition & 0 deletions tests/power/power_states/src/Makefile
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
obj-y = main.o
obj-$(CONFIG_SOC_WATCH) += soc_watch_logger.o
6 changes: 6 additions & 0 deletions tests/power/power_states/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <power.h>
#include <soc_power.h>
#include <string.h>
#include "soc_watch_logger.h"

static enum power_states states_list[] = {
SYS_POWER_STATE_CPU_LPS,
Expand Down Expand Up @@ -386,6 +387,11 @@ void main(void)

build_suspend_device_list();

#ifdef CONFIG_SOC_WATCH
/* Start the event monitoring thread */
soc_watch_logger_thread_start();
#endif

/* All our application does is putting the task to sleep so the kernel
* triggers the suspend operation.
*/
Expand Down
107 changes: 107 additions & 0 deletions tests/power/power_states/src/soc_watch_logger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "soc_watch_logger.h"

#define STSIZE 512
char __stack soc_watch_event_logger_stack[1][STSIZE];

/**
* @brief soc_watch data collector thread
*
* @details Collect the kernel event messages and pass them to
* soc_watch
*
* @return No return value.
*/
void soc_watch_data_collector(void)
{
#ifdef CONFIG_SOC_WATCH
int res;
uint32_t data[4];
uint8_t dropped_count;
uint16_t event_id;

/* We register the thread as collector to avoid this thread generating a
* context switch event every time it collects the data
*/
sys_k_event_logger_register_as_collector();

while (1) {
/* collect the data */
uint8_t data_length = SIZE32_OF(data);

res = sys_k_event_logger_get_wait(&event_id, &dropped_count,
data, &data_length);

if (res > 0) {

/* process the data */
switch (event_id) {
#ifdef CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH
case KERNEL_EVENT_LOGGER_CONTEXT_SWITCH_EVENT_ID:
if (data_length != 2) {
PRINTF("\x1b[13;1HError in context switch message. "
"event_id = %d, Expected %d, received %d\n",
event_id, 2, data_length);
} else {
/* Log context switch event for SoCWatch */
SOC_WATCH_LOG_APP_EVENT(SOCW_EVENT_APP,
event_id, data[1]);
}
break;
#endif
#ifdef CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT
case KERNEL_EVENT_LOGGER_INTERRUPT_EVENT_ID:
if (data_length != 2) {
PRINTF("\x1b[13;1HError in interrupt message. "
"event_id = %d, Expected %d, received %d\n",
event_id, 2, data_length);
} else {
/* Log interrupt event for SoCWatch */
SOC_WATCH_LOG_EVENT(SOCW_EVENT_INTERRUPT, data[1]);
}
break;
#endif
default:
PRINTF("unrecognized event id %d", event_id);
}
} else {
/* This error should never happen */
if (res == -EMSGSIZE) {
PRINTF("FATAL ERROR. The buffer provided to collect the "
"profiling events is too small\n");
}
}
}
#endif
}

/**
* @brief Start the soc_watch logger thread
*
* @details Start the soc_watch data collector thread
*
* @return No return value.
*/
void soc_watch_logger_thread_start(void)
{
PRINTF("\x1b[2J\x1b[15;1H");

k_thread_spawn(&soc_watch_event_logger_stack[0][0], STSIZE,
(k_thread_entry_t) soc_watch_data_collector, 0, 0, 0,
6, 0, 0);
}
31 changes: 31 additions & 0 deletions tests/power/power_states/src/soc_watch_logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <zephyr.h>
#include <misc/printk.h>
#include <string.h>
#include <misc/kernel_event_logger.h>
#include "soc_watch.h"

#if defined(CONFIG_STDOUT_CONSOLE)
#define PRINTF(...) { char output[256]; \
sprintf(output, __VA_ARGS__); puts(output); }
#else
#define PRINTF(...) printk(__VA_ARGS__)
#endif

void soc_watch_data_collector(void);
void soc_watch_logger_thread_start(void);
6 changes: 6 additions & 0 deletions tests/power/power_states/testcase.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@
build_only = true
tags = samples power
filter = CONFIG_SOC_QUARK_SE_C1000

[test_socwatch]
extra_args = CONF_FILE="prj_socwatch.conf"
build_only = true
tags = samples power
filter = CONFIG_SOC_QUARK_SE_C1000

0 comments on commit 8358468

Please sign in to comment.