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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ LDFLAGS += -g -nostdlib -mmcu=am1808.pru1.specs

all: $(BUILD_TARGET).bin

# Manual dependency tracking
main.o: am1808.h

$(BUILD_TARGET).bin: $(BUILD_TARGET).elf

$(BUILD_TARGET).elf: start.o main.o
Expand Down
71 changes: 71 additions & 0 deletions am1808.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#ifndef __PRU_AM1808_H__
#define __PRU_AM1808_H__

// PRU0/1 Local INTC
#pragma ctable_entry 0 0x00004000
// Timer64P0
#pragma ctable_entry 1 0x01C20000
// I2C0
#pragma ctable_entry 2 0x01C22000
// PRU0/1 Local Data (self)
#pragma ctable_entry 3 0x00000000
// PRU1/0 Local Data (other)
#pragma ctable_entry 4 0x00002000
// MMC/SD
#pragma ctable_entry 5 0x01C40000
// SPI0
#pragma ctable_entry 6 0x01C41000
// UART0
#pragma ctable_entry 7 0x01C42000
// McASP0 DMA
#pragma ctable_entry 8 0x01D02000
// Reserved
#pragma ctable_entry 9 0x01D06000
// Reserved
#pragma ctable_entry 10 0x01D0A000
// UART1
#pragma ctable_entry 11 0x01D0C000
// UART2
#pragma ctable_entry 12 0x01D0D000
// USB0
#pragma ctable_entry 13 0x01E00000
// USB1
#pragma ctable_entry 14 0x01E25000
// UHPI Config
#pragma ctable_entry 15 0x01E10000
// Reserved
#pragma ctable_entry 16 0x01E12000
// I2C1
#pragma ctable_entry 17 0x01E28000
// EPWM0
#pragma ctable_entry 18 0x01F00000
// EPWM1
#pragma ctable_entry 19 0x01F02000
// Reserved
#pragma ctable_entry 20 0x01F04000
// ECAP0
#pragma ctable_entry 21 0x01F06000
// ECAP1
#pragma ctable_entry 22 0x01F07000
// ECAP2
#pragma ctable_entry 23 0x01F08000

/* 24 and 25 are variable */
// 24 = PRU0/1 Local Data 0x00000n00
// 25 = McASP0 Control 0x01D00n00

// Reserved
#pragma ctable_entry 26 0x01D04000
// Reserved
#pragma ctable_entry 27 0x01D08000

/* 28-31 are variable */
// 28 = DSP Megamodule RAM/ROM 0x11nnnn00
// 29 = EMIFA SDRAM 0x40nnnn00
// 30 = Shared RAM 0x80nnnn00
// 31 = mDDR/DDR2 Data 0xC0nnnn00

// Timer0 peripheral (only what we need)
static volatile uint32_t * const TIMER0_TIM34 = (volatile uint32_t *)0x01C20014;

#endif
51 changes: 50 additions & 1 deletion main.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
#include <stdint.h>

#include "am1808.h"

// Shared RAM (control input)
typedef struct shared_ram {
// Declare this as a u32 to force more-efficient codegen
uint32_t pwms;
} shared_ram;
// XXX: The real address in use here is 0x80010000
// There appears to be a compiler bug where ctable entries with the MSB set
Copy link
Member

Choose a reason for hiding this comment

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

Do we have a link for the bug we could add here?

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'm working on filing a bug right this moment, but unfortunately GCC's bug tracker seems to want manual approval for new account creation.

I have produced a minimal testcase though:

#pragma ctable_entry 30 0x00beef00
void good() {
    *(volatile unsigned int *)0x00beef00 = 0xdead;
}

#pragma ctable_entry 31 0x80beef00
void bad() {
    *(volatile unsigned int *)0x80beef00 = 0xf00d;
}

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 just filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121124 to track this issue.

// do not get optimized correctly. We lie to the PRU compiler here, but the
// hardware indeed contains the correct address we *actually* want.
static volatile shared_ram * const SHARED = (volatile shared_ram *)0x7f010000;
#pragma ctable_entry 30 0x7f010000

// LED definitions
// LEDx corresponds to DIODEx in the EV3 schematics
// LEDx defined as n ==> pin is PRU1_R30[n]
#define LED0 12
#define LED1 10
#define LED2 13
#define LED3 11

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
register uint32_t __R30 asm ("r30");

static inline void update_pwm(uint8_t val, uint8_t time_now, uint32_t gpio_bit) {
Copy link
Member

@dlech dlech Jul 15, 2025

Choose a reason for hiding this comment

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

Suggested change
static inline void update_pwm(uint8_t val, uint8_t time_now, uint32_t gpio_bit) {
static inline void update_pwm(uint8_t val, uint8_t time_now, uint8_t gpio_bit) {

// We want to force generation of the optimized set/clr opcodes
if (time_now < val) {
asm volatile("set r30, r30, %0"::"I"(gpio_bit));
} else {
asm volatile("clr r30, r30, %0"::"I"(gpio_bit));
}
}

void main() {
__halt();
uint32_t pwms = 0;
while (1) {
// 24 MHz / 256 ==> 93.75 kHz tick rate for this counter
uint8_t time_now = (*TIMER0_TIM34) >> 8;

if (time_now == 0) {
// 24 MHz / 256 / 256 ~= 366 Hz update rate
pwms = SHARED->pwms;
}

update_pwm(pwms >> 0, time_now, LED0);
update_pwm(pwms >> 8, time_now, LED1);
// Intentional swap of 2 and 3
// This puts the pins into the order R, G, R, G
update_pwm(pwms >> 16, time_now, LED3);
update_pwm(pwms >> 24, time_now, LED2);
}
}