Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bricks/_common/sources.mk
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\
drv/uart/uart_stm32l4_ll_dma.c \
drv/usb/usb_ev3.c \
drv/usb/usb_stm32.c \
drv/watchdog/watchdog_ev3.c \
drv/watchdog/watchdog_stm32.c \
platform/$(PBIO_PLATFORM)/platform.c \
src/angle.c \
Expand Down
9 changes: 5 additions & 4 deletions lib/pbio/drv/block_device/block_device_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <pbdrv/block_device.h>
#include <pbdrv/clock.h>
#include <pbdrv/compiler.h>
#include <pbdrv/gpio.h>

#include <tiam1808/edma.h>
Expand Down Expand Up @@ -486,8 +487,8 @@ static pbio_error_t spi_begin_for_flash(

spi_dev.status = SPI_STATUS_WAIT_TX | SPI_STATUS_WAIT_RX;

// TODO: pbio probably needs a framework for memory barriers and DMA cache management
__asm__ volatile ("" ::: "memory");
// TODO: eventually needs DMA cache management
pbdrv_compiler_memory_barrier();

EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_TX, EDMA3_TRIG_MODE_EVENT);
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_RX, EDMA3_TRIG_MODE_EVENT);
Expand Down Expand Up @@ -805,8 +806,8 @@ static pbio_error_t pbdrv_block_device_ev3_spi_begin_for_adc(const uint32_t *cmd

spi_dev.status = SPI_STATUS_WAIT_TX | SPI_STATUS_WAIT_RX;

// TODO: pbio probably needs a framework for memory barriers and DMA cache management
__asm__ volatile ("" ::: "memory");
// TODO: eventually needs DMA cache management
pbdrv_compiler_memory_barrier();

EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_TX, EDMA3_TRIG_MODE_EVENT);
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_RX, EDMA3_TRIG_MODE_EVENT);
Expand Down
62 changes: 58 additions & 4 deletions lib/pbio/drv/reset/reset_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,90 @@

#if PBDRV_CONFIG_RESET_EV3

#include <stdbool.h>

#include <pbdrv/compiler.h>
#include <pbdrv/reset.h>
#include <pbdrv/gpio.h>

#include "../drv/gpio/gpio_ev3.h"

#include <tiam1808/hw/soc_AM1808.h>
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
#include <tiam1808/hw/hw_types.h>
#include <tiam1808/timer.h>

#define BOOTLOADER_UPDATE_MODE_VALUE 0x5555AAAA

// 'Pybr'
#define RESET_REASON_FLAG_WDT 0x50796272
// 'rboo'
#define RESET_REASON_FLAG_SOFT_RESET 0x72626f6f

typedef struct {
// 0xffff1ff0
uint32_t _dummy0;
// 0xffff1ff4
// Pybricks uses this flag to determine software reset vs other resets
uint32_t reset_reason_flag;
// 0xffff1ff8
// Have not fully investigated this, but the bootloader seems to store
// the result of (DDR) memory testing at 0xffff1ffa and 0xffff1ffb
uint32_t _bootloader_unk_ram_test;
// 0xffff1ffc
uint32_t bootloader_update_flag;
} persistent_data_t;
// This is defined as an extern variable so that its address can be specified
// in the platform.ld linker script. This means that the linker script
// contains information about *all* fixed memory locations.
//
// This lives at the very end of the ARM local RAM.
extern volatile persistent_data_t ev3_persistent_data;

static uint32_t saved_reset_reason_flag;

static const pbdrv_gpio_t poweroff_pin = PBDRV_GPIO_EV3_PIN(13, 19, 16, 6, 11);

void pbdrv_reset_init(void) {
pbdrv_gpio_out_high(&poweroff_pin);
saved_reset_reason_flag = ev3_persistent_data.reset_reason_flag;
ev3_persistent_data.reset_reason_flag = RESET_REASON_FLAG_WDT;
}

void pbdrv_reset(pbdrv_reset_action_t action) {
for (;;) {
switch (action) {
case PBDRV_RESET_ACTION_RESET_IN_UPDATE_MODE:
// TODO
case PBDRV_RESET_ACTION_POWER_OFF:
pbdrv_reset_power_off();
break;
// TODO: implement case PBDRV_RESET_ACTION_RESET
case PBDRV_RESET_ACTION_RESET_IN_UPDATE_MODE:
ev3_persistent_data.bootloader_update_flag = BOOTLOADER_UPDATE_MODE_VALUE;
PBDRV_FALL_THROUGH;
default:
// PBDRV_RESET_ACTION_RESET

ev3_persistent_data.reset_reason_flag = RESET_REASON_FLAG_SOFT_RESET;

// Poke the watchdog timer with a bad value to immediately trigger it
HWREG(SOC_TMR_1_REGS + TMR_WDTCR) = 0;
break;
}
}
}

pbdrv_reset_reason_t pbdrv_reset_get_reason(void) {
if (saved_reset_reason_flag == RESET_REASON_FLAG_SOFT_RESET) {
return PBDRV_RESET_REASON_SOFTWARE;
}
if (saved_reset_reason_flag == RESET_REASON_FLAG_WDT) {
return PBDRV_RESET_REASON_WATCHDOG;
}
return PBDRV_RESET_REASON_NONE;
}

void pbdrv_reset_ev3_early_init(void) {
pbdrv_gpio_out_high(&poweroff_pin);
}

void pbdrv_reset_power_off(void) {
pbdrv_gpio_out_low(&poweroff_pin);
}
Expand Down
12 changes: 12 additions & 0 deletions lib/pbio/drv/reset/reset_ev3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 The Pybricks Authors

// The EV3 requires a GPIO pin to be set in order to stay powered on.
// We want to be able to do this as early as possible.

#ifndef _INTERNAL_PBDRV_RESET_EV3_H_
#define _INTERNAL_PBDRV_RESET_EV3_H_

void pbdrv_reset_ev3_early_init(void);

#endif // _INTERNAL_PBDRV_RESET_EV3_H_
31 changes: 31 additions & 0 deletions lib/pbio/drv/watchdog/watchdog_ev3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 The Pybricks Authors

// Watchdog timer driver for EV3.

#include <pbdrv/config.h>

#if PBDRV_CONFIG_WATCHDOG_EV3

#include <tiam1808/hw/soc_AM1808.h>
#include <tiam1808/timer.h>

// The input to Timer1 is PLL0_AUXCLK which is 24 MHz
// Configure the timeout to be 3 seconds
#define WDT_TIMEOUT_SECONDS 3ull
#define WDT_PERIOD_LSB ((WDT_TIMEOUT_SECONDS * SOC_ASYNC_2_FREQ) & 0xffffffff)
#define WDT_PERIOD_MSB (((WDT_TIMEOUT_SECONDS * SOC_ASYNC_2_FREQ) >> 32) & 0xffffffff)

void pbdrv_watchdog_init(void) {
TimerDisable(SOC_TMR_1_REGS, TMR_TIMER_BOTH);
TimerConfigure(SOC_TMR_1_REGS, TMR_CFG_64BIT_WATCHDOG);
TimerPeriodSet(SOC_TMR_1_REGS, TMR_TIMER12, WDT_PERIOD_LSB);
TimerPeriodSet(SOC_TMR_1_REGS, TMR_TIMER34, WDT_PERIOD_MSB);
TimerWatchdogActivate(SOC_TMR_1_REGS);
}

void pbdrv_watchdog_update(void) {
TimerWatchdogReactivate(SOC_TMR_1_REGS);
}

#endif // PBDRV_CONFIG_WATCHDOG_EV3
12 changes: 12 additions & 0 deletions lib/pbio/include/pbdrv/compiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 The Pybricks Authors

#ifndef _PBDRV_COMPILER_H_

// Marks a switch case that intentionally falls through to the next one
#define PBDRV_FALL_THROUGH __attribute__((fallthrough))

// Forces the compiler to not reorder memory access around this line
#define pbdrv_compiler_memory_barrier() __asm__ volatile ("" ::: "memory")

#endif
3 changes: 3 additions & 0 deletions lib/pbio/platform/ev3/pbdrvconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@

#define PBDRV_CONFIG_STACK (1)
#define PBDRV_CONFIG_STACK_EMBEDDED (1)

#define PBDRV_CONFIG_WATCHDOG (1)
#define PBDRV_CONFIG_WATCHDOG_EV3 (1)
11 changes: 9 additions & 2 deletions lib/pbio/platform/ev3/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <tiam1808/hw/soc_AM1808.h>
#include <tiam1808/i2c.h>
#include <tiam1808/psc.h>
#include <tiam1808/timer.h>
#include <tiam1808/uart.h>

#include <umm_malloc.h>
Expand All @@ -64,8 +65,8 @@
#include "../../drv/led/led_dual.h"
#include "../../drv/led/led_pwm.h"
#include "../../drv/pwm/pwm_ev3.h"
#include "../../drv/reset/reset_ev3.h"
#include "../../drv/uart/uart_ev3.h"
#include "../../drv/reset/reset.h"

enum {
LED_DEV_0_STATUS,
Expand Down Expand Up @@ -489,6 +490,12 @@ void ev3_panic_handler(int except_type, ev3_panic_ctx *except_data) {
panic_putu32(except_data->spsr);

panic_puts("\r\nSystem will now reboot...\r\n");

// Poke the watchdog timer with a bad value to immediately trigger it
// if it has already been configured. If it has *not* been configured,
// that means we are crashing in early boot, and we let the jump back
// to the reset vector take care of rebooting the system.
HWREG(SOC_TMR_1_REGS + TMR_WDTCR) = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a new pbdrv_reset_reason_t for panic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need one? It currently reports as a watchdog reset

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it is worth it or not. That is why I ask. 😄

There is a slight difference in that the true watchdog reset only happens if the event loops stops running while a panic is a different mode of failure. So could be potentially useful for getting info on the cause of a crash. But would anyone ever actually do that? Maybe not very likely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem too likely, since right now crash dumps aren't saved anywhere and you need to have the debug console connected to see them.

Perhaps we can change "watchdog" to a generic "fatal error"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we'll leave it the way it is. I would prefer to stick with calling it watchdog and add a new reason if we need to differentiate.

}

/**
Expand Down Expand Up @@ -663,7 +670,7 @@ void SystemInit(void) {

// Must set the power enable bin before disabling the pull up on the power
// pin below, otherwise the hub will power off.
pbdrv_reset_init();
pbdrv_reset_ev3_early_init();

// Disable all pull-up/pull-down groups.
HWREG(SOC_SYSCFG_1_REGS + SYSCFG1_PUPD_ENA) &= ~0xFFFFFFFF;
Expand Down
8 changes: 7 additions & 1 deletion lib/pbio/platform/ev3/platform.ld
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ MEMORY
SRAM_PRU1 (rw) : ORIGIN = 0x80010000, LENGTH = 64K
DDR_unused (rwx) : ORIGIN = 0xC0000000, LENGTH = 0x8000
DDR (rwx) : ORIGIN = 0xC0008000, LENGTH = (64M - 0x8000)
ARM_LRAM (rwx) : ORIGIN = 0xFFFF0000, LENGTH = 8K
ARM_LRAM (rwx) : ORIGIN = 0xFFFF0000, LENGTH = (8K - 16)
}

_minimal_stack_size = 4M;
pbdrv_stack_end = ORIGIN(DDR) + LENGTH(DDR) - 4;
/* Extra heap for large allocations (images, etc). */
pb_umm_heap_size = 2M;
/*
We declare this in this style rather than creating a section.
If we do create a section, the loader (U-boot) will always clear it.
This defeats what we are trying to do to determine reset causes.
*/
ev3_persistent_data = ORIGIN(ARM_LRAM) + LENGTH(ARM_LRAM);

SECTIONS
{
Expand Down