Skip to content
Merged
4 changes: 2 additions & 2 deletions bricks/_common/arm_none_eabi.mk
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,8 @@ $(BUILD)/u-boot.bin:
$(BUILD)/pru_ledpwm.bin:
$(ECHO) "Downloading pru_ledpwm.bin"
$(Q)mkdir -p $(dir $@)
$(Q)curl -sL -o $@ https://github.com/pybricks/pybricks-pru/releases/download/v0.0.1/pru_ledpwm.bin
$(Q)echo "c0138addb8ebb3d0f531499b6f45ccc71f524afbb6ce55ca3ab462a001ec28d2 $@" | sha256sum -c --strict
$(Q)curl -sL -o $@ https://github.com/pybricks/pybricks-pru/releases/download/v1.0.0/pru_ledpwm.bin
$(Q)echo "b4f1225e277bb22efa5394ce782cc19a3e2fdd54367e40b9d09e9ca99c6ef6d0 $@" | sha256sum -c --strict

MAKE_BOOTABLE_IMAGE = $(PBTOP)/bricks/ev3/make_bootable_image.py

Expand Down
34 changes: 32 additions & 2 deletions lib/pbio/drv/gpio/gpio_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include <tiam1808/gpio.h>
#include <tiam1808/psc.h>

#include "../rproc/rproc.h"
#include "../rproc/rproc_ev3.h"

static uint32_t get_pin_index(const pbdrv_gpio_t *gpio) {
// TI API indexes pins from 1 to 144, so need to add 1.
pbdrv_gpio_ev3_mux_t *mux_info = gpio->bank;
Expand All @@ -31,13 +34,32 @@ static void pbdrv_gpio_alt_gpio(const pbdrv_gpio_t *gpio) {
pbdrv_gpio_alt(gpio, mux_info->gpio_mode);
}

// If the pin is not in banks 0/1, the PRU is not involved.
// Otherwise, if the PRU is initialized, do direction changes
// via the PRU in order to prevent race conditions.
// If the PRU is not initialized (i.e. doing direction changes
// during early boot), also set the direction directly via
// the ARM. The PRU code polls these direction change registers
// continuously and is not expected to block for too long,
// only on the order of microseconds.
#define PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index) \
((pin_index) > 32 || !pbdrv_rproc_is_ready())

static void gpio_write(const pbdrv_gpio_t *gpio, uint8_t value) {
if (!gpio) {
return;
}
pbdrv_gpio_alt_gpio(gpio);
uint32_t pin_index = get_pin_index(gpio);
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_OUTPUT) {
Copy link
Member

@laurensvalk laurensvalk Aug 7, 2025

Choose a reason for hiding this comment

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

Shouldn't this check go as the first if, so we don't re-set it for pins owned by the ARM either?

(I know it used to double-set it before as well, but now that we have the test we might as well cover both cases.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I figured that code path was cheap enough (a single register write) that it wasn't worth bothering

uint32_t val = 1 << (pin_index - 1);
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr = val;
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr) {
// Wait for the PRU to process the command
}
}
GPIOPinWrite(SOC_GPIO_0_REGS, pin_index, value);
}

Expand All @@ -55,7 +77,15 @@ uint8_t pbdrv_gpio_input(const pbdrv_gpio_t *gpio) {
}
pbdrv_gpio_alt_gpio(gpio);
uint32_t pin_index = get_pin_index(gpio);
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_INPUT) {
uint32_t val = 1 << (pin_index - 1);
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set = val;
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set) {
// Wait for the PRU to process the command
}
}
return GPIOPinRead(SOC_GPIO_0_REGS, pin_index) == GPIO_PIN_HIGH;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/pbio/drv/pwm/pwm_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static pbio_error_t pbdrv_pwm_tiam1808_set_duty(pbdrv_pwm_dev_t *dev, uint32_t c
return PBIO_SUCCESS;
}

pbdrv_rproc_ev3_pru1_shared_ram.pwms[ch] = value;
pbdrv_rproc_ev3_pru1_shared_ram.pwm_duty_cycle[ch] = value;
return PBIO_SUCCESS;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/pbio/drv/rproc/rproc_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string.h>

#include <tiam1808/hw/hw_syscfg0_AM1808.h>
#include <tiam1808/hw/hw_types.h>
#include <tiam1808/hw/soc_AM1808.h>
#include <tiam1808/pruss.h>
#include <tiam1808/psc.h>
Expand Down Expand Up @@ -56,6 +57,10 @@ void pbdrv_rproc_init(void) {
unsigned int *fw_start = (unsigned int *)&pbdrv_rproc_ev3_pru1_fw_start;
uint32_t fw_sz = &pbdrv_rproc_ev3_pru1_fw_end - &pbdrv_rproc_ev3_pru1_fw_start;
PRUSSDRVPruWriteMemory(PRUSS0_PRU1_IRAM, 0, fw_start, fw_sz);
// Clear data RAM
for (int i = 0; i < PRUSS_DATARAM_SIZE; i += 4) {
HWREG(DATARAM1_PHYS_BASE + i) = 0;
}
// Set constant table C30 to point to shared memory
PRUSSDRVPruSetCTable(1, 30, (((uint32_t)&pbdrv_rproc_ev3_pru1_shared_ram) >> 8) & 0xffff);
PRUSSDRVPruEnable(1);
Expand Down
9 changes: 1 addition & 8 deletions lib/pbio/drv/rproc/rproc_ev3.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,11 @@
#ifndef _INTERNAL_PBDRV_RPROC_EV3_H_
#define _INTERNAL_PBDRV_RPROC_EV3_H_

#include <stdint.h>

// EV3 PRU interfaces and ABI
// These need to match the interface expected by the PRU firmware

// PRU1 interface

#define PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS 4

typedef struct {
uint8_t pwms[PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS];
} pbdrv_rproc_ev3_pru1_shared_ram_t;
#include "rproc_ev3_pru1.h"

extern volatile pbdrv_rproc_ev3_pru1_shared_ram_t pbdrv_rproc_ev3_pru1_shared_ram;

Expand Down
30 changes: 30 additions & 0 deletions lib/pbio/drv/rproc/rproc_ev3_pru1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 The Pybricks Authors

#ifndef _INTERNAL_PBDRV_RPROC_EV3_PRU1_H_
#define _INTERNAL_PBDRV_RPROC_EV3_PRU1_H_

#include <stdint.h>

// This file is shared between the PRU1 source code
// and the Pybricks source code. It defines the ABI
// between the two codebases and must be kept in sync.

#define PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS 4

typedef struct {
union {
// u32 used by the PRU codebase to get more-efficient codegen
uint32_t pwms;
// PWM duty cycle for each channel, range [0-255]
uint8_t pwm_duty_cycle[PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS];
};
// Because the PRU needs to manipulate the GPIO direction
// for doing I2C, and because these registers do *not*
// support atomic bit access, we give the PRU full ownership
// of them and route ARM accesses through the PRU.
uint32_t gpio_bank_01_dir_set;
uint32_t gpio_bank_01_dir_clr;
} pbdrv_rproc_ev3_pru1_shared_ram_t;

#endif // _INTERNAL_PBDRV_RPROC_EV3_PRU1_H_