mbv: now with functioning timer interrupts!

And a few other nice things.
The bootloader now has an embedded wozmon!
If you know its offset, you can jump to it from the app.
This commit is contained in:
2025-06-10 23:46:43 -07:00
parent fa6ae7b667
commit 3db383d461
17 changed files with 485 additions and 93 deletions

62
mbv/hal/intc.cc Normal file
View File

@@ -0,0 +1,62 @@
#include "intc.h"
#include "pol0.h"
namespace {
struct IntC {
volatile uint32_t ISR;
volatile uint32_t IPR;
volatile uint32_t IER;
volatile uint32_t IAR;
volatile uint32_t SIE;
volatile uint32_t CIE;
volatile uint32_t IVR;
volatile uint32_t MER;
volatile uint32_t IMR;
volatile uint32_t ILR;
// the rest is not enabled
};
IntC* intc = reinterpret_cast<IntC*>(INTC_BASE);
Isr isrs[NIRQ] = {};
}
bool SetIrqEnabled(uint8_t irqn, bool enabled) {
uint32_t mask = 1 << irqn;
uint32_t ier = intc->IER;
bool was_enabled = (ier & (~mask)) > 0;
if (enabled) {
intc->IER = ier | mask;
} else {
intc->IER = ier & (~mask);
}
return was_enabled;
}
void SetIsr(uint8_t irqn, Isr isr) {
isrs[irqn] = isr;
}
void EnableInterrupts() {
intc->MER = 0x3;
}
void InterruptHandler() {
uint32_t ipr = intc->IPR;
for (int i = 0; i < NIRQ; i++) {
uint32_t mask = 1 << i;
if ((ipr & mask) > 0) {
// interrupt pending
if (isrs[i] != nullptr) {
isrs[i]();
}
intc->IAR = mask; // ack
}
}
}

16
mbv/hal/intc.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
using Isr = void(*)(void);
/// Returns: true if the IRQ was previously enabled
bool SetIrqEnabled(uint8_t irqn, bool enabled);
void SetIsr(uint8_t irqn, Isr isr);
// Call this once to enable all HW interrupts
void EnableInterrupts();
// Feed this to the CPU's interrupt handler
void InterruptHandler();

52
mbv/hal/interrupts.cc Normal file
View File

@@ -0,0 +1,52 @@
#include <cstdint>
#include "interrupts.h"
namespace {
constexpr uint32_t kMstatusMieMask = 1 << 3;
constexpr uint32_t kMieExternalInterruptMask = 1 << 11;
constexpr uint32_t kExternalInterruptCause = 0x0b;
constexpr uint32_t kInterruptCauseMask = 0xff;
Isr external_handler = nullptr;
__attribute__((interrupt))
void TrapHandler() {
uint32_t mcause;
uint32_t mip;
asm volatile("csrr %0, mcause" : "=r"(mcause));
asm volatile("csrr %0, mip" : "=r"(mip));
// check for external interrupt
if ((mcause & kInterruptCauseMask) == kExternalInterruptCause) {
if (external_handler != nullptr) {
external_handler();
}
mip &= ~(kMieExternalInterruptMask);
asm volatile("csrw mip, %0" :: "r"(mip));
}
}
}
void SetExternalInterruptHandler(Isr handler) {
external_handler = handler;
}
void EnableExternalInterrupts() {
uint32_t mstatus;
uint32_t mie;
Isr trap = TrapHandler;
asm volatile("csrr %0, mstatus" : "=r"(mstatus));
asm volatile("csrr %0, mie" : "=r"(mie));
asm volatile("csrw mtvec, %0" :: "r"(trap));
mie |= kMieExternalInterruptMask;
asm volatile("csrw mie, %0" :: "r"(mie));
mstatus |= kMstatusMieMask;
asm volatile("csrw mstatus, %0" :: "r"(mstatus));
}

6
mbv/hal/interrupts.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
using Isr = void(*)();
void SetExternalInterruptHandler(Isr handler);
void EnableExternalInterrupts();

18
mbv/hal/pol0.h Normal file
View File

@@ -0,0 +1,18 @@
// Platform definitions for pol0
// LED output
#define GPIO0_BASE (0x40000000)
// /dev/ttyUSB1
#define UART0_BASE (0x40600000)
// Interrupt controller
#define INTC_BASE (0x41200000)
// It's uh.. a timer.
#define TIMER0_BASE (0x41c00000)
// IRQs
#define UART0_IRQN (0)
#define TIMER0_IRQN (1)
#define NIRQ (2)

69
mbv/hal/timer.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
struct TimerControl {
union {
struct {
uint32_t MDT0 : 1;
uint32_t UDT0 : 1;
uint32_t GENT0 : 1;
uint32_t CAPT0 : 1;
uint32_t ARHT0 : 1;
uint32_t LOAD0 : 1;
uint32_t ENIT0 : 1;
uint32_t ENT0 : 1;
uint32_t T0INT : 1;
uint32_t PWMA0 : 1;
uint32_t ENALL : 1;
uint32_t CASC : 1;
uint32_t reserved : 20;
};
uint32_t raw;
};
};
struct Timer {
volatile TimerControl TCSR0;
volatile uint32_t TLR0;
volatile uint32_t TCR0;
uint32_t _reserved;
volatile TimerControl TCSR1;
volatile uint32_t TLR1;
volatile uint32_t TCR1;
void EnableT1() {
TCSR1.ARHT0 = 1;
TCSR1.ENT0 = 1;
}
uint32_t GetT1Ticks() { return TCR1; }
void SetupAsWdt(uint32_t timeout_ticks) {
TLR0 = timeout_ticks;
TCSR0.LOAD0 = 1; // reset counter
TCSR0.UDT0 = 1; // count backwards from the load value
TCSR0.ENIT0 = 1; // enable interrupt
TCSR0.LOAD0 = 0; // allow counter to run
TCSR0.ENT0 = 1; // enable timer
}
void Pet() {
TCSR0.ENT0 = 0;
TCSR0.LOAD0 = 1;
TCSR0.LOAD0 = 0;
TCSR0.ENT0 = 1;
}
void ClearInterrupt() {
TCSR0.T0INT = 0;
}
static Timer* Instance(uint32_t base) {
return reinterpret_cast<Timer*>(base);
}
};