cts: Add real interrupt test

Interrupt test checks whether DUT can be interrupted by an interrupt
and an interrupt handler can be invoked as expected.

Note the previous interrupt test ported from test/interrupt.c runs in
an emulated environment on the host, thus does not test the real
interrupt capability of the chip.

BUG=chromium:653195
BRANCH=none
TEST=Run cts.py -m interrupt

Change-Id: I21cecff07594f048633d1c1b699fb3a1876379e0
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/363943
Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
Daisuke Nojiri
2016-07-27 15:46:42 -07:00
committed by chrome-bot
parent 241d9e3728
commit 473ecbe2b3
6 changed files with 193 additions and 116 deletions

View File

@@ -16,6 +16,16 @@ void button_event(enum gpio_signal signal)
gpio_set_level(GPIO_LED_U, 1);
}
#ifdef CTS_MODULE
/*
* Dummy interrupt handler. It's supposed to be overwritten by each suite
* if needed.
*/
__attribute__((weak)) void cts_irq(enum gpio_signal signal)
{
}
#endif
#include "gpio_list.h"
void tick_event(void)

View File

@@ -9,6 +9,16 @@
* Note: Those with interrupt handlers must be declared first. */
GPIO_INT(USER_BUTTON, PIN(C, 13), GPIO_INT_FALLING, button_event)
#ifdef CTS_MODULE
#ifndef CTS_MODULE_GPIO
/* Overload C1 for interrupt. Enabled only for non-GPIO suites as
* GPIO tests don't require a separate notification line. */
GPIO_INT(CTS_IRQ, PIN(C, 1), GPIO_INT_FALLING | GPIO_PULL_UP , cts_irq)
/* Used to disable interrupt. This IRQ# has to match the number used for the
* pin set above */
#define CTS_IRQ_NUMBER STM32_IRQ_EXTI0_1
#endif
#endif
/* Outputs */
GPIO(LED_U, PIN(A, 5), GPIO_OUT_LOW)
@@ -29,6 +39,8 @@ ALTERNATE(PIN_MASK(B, 0x00C0), GPIO_ALT_F1, MODULE_I2C, GPIO_PULL_UP)
/* CTS Signals */
GPIO(HANDSHAKE_INPUT, PIN(A, 4), GPIO_INPUT | GPIO_PULL_UP)
GPIO(HANDSHAKE_OUTPUT, PIN(B, 0), GPIO_ODR_LOW)
#ifdef CTS_MODULE_GPIO
GPIO(INPUT_TEST, PIN(C, 1), GPIO_INPUT | GPIO_PULL_UP)
#endif
GPIO(OUTPUT_TEST, PIN(C, 0), GPIO_ODR_LOW)
#endif

View File

@@ -27,7 +27,8 @@
#define CTS_DEBUG_PRINTF(format, args...)
#endif
#define READ_WAIT_TIME_MS 100
#define READ_WAIT_TIME_MS 100
#define CTS_INTERRUPT_TRIGGER_DELAY_US (250 * MSEC)
/* In a single test, only one board can return unknown, the other must
* return a useful result (i.e. success, failure, etc)

View File

@@ -0,0 +1,20 @@
/* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Test interrupt_enable/disable */
CTS_TEST(test_interrupt_enable)
CTS_TEST(test_interrupt_disable)
/* Test task_wait_for_event */
CTS_TEST(test_task_wait_event)
/* Test task_disable_irq */
CTS_TEST(test_task_disable_irq)
/*
* Other ideas
*
* Test back-to-back interrupts, NVIC, Priorities
*/

View File

@@ -1,84 +1,135 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
/* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Test interrupt support of EC emulator.
*/
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "registers.h"
#include "task.h"
#include "test_util.h"
#include "dut_common.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"
static int main_count;
static int has_error;
static int interrupt_count;
static int got_interrupt;
static int wake_me_up;
/* period between 50us and 3.2ms */
#define PERIOD_US(num) (((num % 64) + 1) * 50)
void my_isr(void)
/*
* Raw busy loop. Returns 1 if loop finishes before interrupt is triggered.
* Loop length is controlled by busy_loop_timeout. It has to be set to the
* value which makes the loop last longer than CTS_INTERRUPT_TRIGGER_DELAY_US.
*/
static int busy_loop(void)
{
int i = main_count;
/* TODO: Derive a proper value from clock speed */
const uint32_t busy_loop_timeout = 0xfffff;
uint32_t counter = 0;
udelay(3 * PERIOD_US(prng_no_seed()));
if (i != main_count || !in_interrupt_context())
has_error = 1;
interrupt_count++;
}
void interrupt_generator(void)
{
while (1) {
udelay(3 * PERIOD_US(prng_no_seed()));
task_trigger_test_interrupt(my_isr);
while (counter++ < busy_loop_timeout) {
if (got_interrupt)
break;
watchdog_reload();
}
if (counter > busy_loop_timeout)
return 1;
return 0;
}
static int interrupt_test(void)
/*
* Interrupt handler.
*/
void cts_irq(enum gpio_signal signal)
{
timestamp_t deadline = get_time();
/* test some APIs */
got_interrupt = in_interrupt_context();
deadline.val += SECOND / 2;
while (!timestamp_expired(deadline, NULL))
++main_count;
ccprintf("Interrupt count: %d\n", interrupt_count);
ccprintf("Main thread tick: %d\n", main_count);
TEST_ASSERT(!has_error);
TEST_ASSERT(!in_interrupt_context());
return EC_SUCCESS;
/* Wake up the CTS task */
if (wake_me_up)
task_wake(TASK_ID_CTS);
}
static int interrupt_disable_test(void)
enum cts_rc test_task_wait_event(void)
{
timestamp_t deadline = get_time();
int start_int_cnt, end_int_cnt;
uint32_t event;
deadline.val += SECOND / 2;
wake_me_up = 1;
/* Sleep and wait for interrupt. This shouldn't time out. */
event = task_wait_event(CTS_INTERRUPT_TRIGGER_DELAY_US * 2);
if (event != TASK_EVENT_WAKE) {
CPRINTS("Woke up by 0x%08x", event);
return CTS_RC_FAILURE;
}
if (!got_interrupt) {
CPRINTS("Interrupt context not detected");
return CTS_RC_TIMEOUT;
}
return CTS_RC_SUCCESS;
}
enum cts_rc test_task_disable_irq(void)
{
uint32_t event;
wake_me_up = 1;
task_disable_irq(CTS_IRQ_NUMBER);
/* Sleep and wait for interrupt. This should time out. */
event = task_wait_event(CTS_INTERRUPT_TRIGGER_DELAY_US * 2);
if (event != TASK_EVENT_TIMER) {
CPRINTS("Woke up by 0x%08x", event);
return CTS_RC_FAILURE;
}
task_enable_irq(CTS_IRQ_NUMBER);
return CTS_RC_SUCCESS;
}
enum cts_rc test_interrupt_enable(void)
{
if (busy_loop()) {
CPRINTS("Timeout before interrupt");
return CTS_RC_TIMEOUT;
}
return CTS_RC_SUCCESS;
}
enum cts_rc test_interrupt_disable(void)
{
interrupt_disable();
start_int_cnt = interrupt_count;
while (!timestamp_expired(deadline, NULL))
;
end_int_cnt = interrupt_count;
interrupt_enable();
TEST_ASSERT(start_int_cnt == end_int_cnt);
return EC_SUCCESS;
if (!busy_loop()) {
CPRINTS("Expected timeout but didn't");
return CTS_RC_FAILURE;
}
return CTS_RC_SUCCESS;
}
#include "cts_testlist.h"
void cts_task(void)
{
test_reset();
enum cts_rc rc;
int i;
RUN_TEST(interrupt_test);
RUN_TEST(interrupt_disable_test);
gpio_enable_interrupt(GPIO_CTS_IRQ);
interrupt_enable();
for (i = 0; i < CTS_TEST_ID_COUNT; i++) {
got_interrupt = 0;
wake_me_up = 0;
sync();
rc = tests[i].run();
interrupt_enable();
CPRINTF("\n%s %d\n", tests[i].name, rc);
cflush();
}
test_print_result();
CPRINTS("Interrupt test suite finished");
cflush();
while (1) {
watchdog_reload();
sleep(1);
}
}

View File

@@ -1,84 +1,67 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
/* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Test interrupt support of EC emulator.
*/
#include "common.h"
#include "console.h"
#include "task.h"
#include "test_util.h"
#include "th_common.h"
#include "gpio.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"
static int main_count;
static int has_error;
static int interrupt_count;
/* period between 50us and 3.2ms */
#define PERIOD_US(num) (((num % 64) + 1) * 50)
void my_isr(void)
static void trigger_interrupt(void)
{
int i = main_count;
udelay(3 * PERIOD_US(prng_no_seed()));
if (i != main_count || !in_interrupt_context())
has_error = 1;
interrupt_count++;
usleep(CTS_INTERRUPT_TRIGGER_DELAY_US);
gpio_set_level(GPIO_OUTPUT_TEST, 0);
usleep(CTS_INTERRUPT_TRIGGER_DELAY_US);
}
void interrupt_generator(void)
enum cts_rc test_task_wait_event(void)
{
while (1) {
udelay(3 * PERIOD_US(prng_no_seed()));
task_trigger_test_interrupt(my_isr);
}
trigger_interrupt();
return CTS_RC_SUCCESS;
}
static int interrupt_test(void)
enum cts_rc test_task_disable_irq(void)
{
timestamp_t deadline = get_time();
deadline.val += SECOND / 2;
while (!timestamp_expired(deadline, NULL))
++main_count;
ccprintf("Interrupt count: %d\n", interrupt_count);
ccprintf("Main thread tick: %d\n", main_count);
TEST_ASSERT(!has_error);
TEST_ASSERT(!in_interrupt_context());
return EC_SUCCESS;
trigger_interrupt();
return CTS_RC_SUCCESS;
}
static int interrupt_disable_test(void)
enum cts_rc test_interrupt_enable(void)
{
timestamp_t deadline = get_time();
int start_int_cnt, end_int_cnt;
deadline.val += SECOND / 2;
interrupt_disable();
start_int_cnt = interrupt_count;
while (!timestamp_expired(deadline, NULL))
;
end_int_cnt = interrupt_count;
interrupt_enable();
TEST_ASSERT(start_int_cnt == end_int_cnt);
return EC_SUCCESS;
trigger_interrupt();
return CTS_RC_SUCCESS;
}
enum cts_rc test_interrupt_disable(void)
{
trigger_interrupt();
return CTS_RC_SUCCESS;
}
#include "cts_testlist.h"
void cts_task(void)
{
test_reset();
enum cts_rc rc;
int i;
RUN_TEST(interrupt_test);
RUN_TEST(interrupt_disable_test);
gpio_set_flags(GPIO_OUTPUT_TEST, GPIO_ODR_HIGH);
test_print_result();
for (i = 0; i < CTS_TEST_ID_COUNT; i++) {
gpio_set_level(GPIO_OUTPUT_TEST, 1);
sync();
rc = tests[i].run();
CPRINTF("\n%s %d\n", tests[i].name, rc);
cflush();
}
CPRINTS("Interrupt test suite finished");
cflush();
while (1) {
watchdog_reload();
sleep(1);
}
}