Files
Federico Cerutti 247a0ffd55 Faster ISR sharing in ASM
While in the process of writing a new codec I stumbled upon the long
standing issue of ISR sharing in AVR MCUs.
Actually this is accomplished with an ISR written in C, which simply
calls another plain C function referenced via a function pointer
updated at runtime.
This approach is slow because GCC can't optimize the ISR, since it
does not know which registers will be used, so it defaults to push/pop
all of them.

My solution keeps the concept of pointers to functions, but greatly
improves speed by reducing the ISR itself to the bare minimum to call
another function, which will be compiled by GCC as a signal.
This means all interrupt optimizations will be put in place by GCC,
while ISRs can be still written in plain C code, but the overhead is
now much smaller. It has proven to reduce by 20 the number of
instructions for every ISR, mainly pushes/pops, which cuts the clock
cycle count down by 30 cycles (1 cycle for every push, 2 for pop).
In time units, this means 1.1 uSec are saved for every ISR invocation
and every shared ISR now takes only 13 clock cycles more than the
"bare" one.

A new ISR_SHARED function type has been defined in Common.h to hide
away from the programmer GCC attributes. All new shared interrupt
handling routines should be defined of this type to prevent
stack and registers corruption.

Minor changes were made to Codec.h to allow including it in .S files.
2019-12-08 14:11:05 +01:00

143 lines
3.5 KiB
C

/*
* System.c
*
* Created on: 10.02.2013
* Author: skuser
*/
#include "System.h"
#include "LED.h"
#include <avr/interrupt.h>
#ifndef WDT_PER_500CLK_gc
#define WDT_PER_500CLK_gc WDT_PER_512CLK_gc
#endif
ISR(BADISR_vect) {
//LED_PORT.OUTSET = LED_RED;
while (1);
}
ISR(RTC_OVF_vect) {
SYSTEM_TICK_REGISTER += SYSTEM_TICK_PERIOD;
}
void SystemInit(void) {
if (RST.STATUS & RST_WDRF_bm) {
/* On Watchdog reset clear WDRF bit, disable watchdog
* and jump into bootloader */
RST.STATUS = RST_WDRF_bm;
CCP = CCP_IOREG_gc;
WDT.CTRL = WDT_CEN_bm;
asm volatile("jmp %0"::"i"(BOOT_SECTION_START + 0x1FC));
}
/* XTAL x 2 as SysCLK */
OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
OSC.CTRL |= OSC_XOSCEN_bm;
while (!(OSC.STATUS & OSC_XOSCRDY_bm))
;
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | (2 << OSC_PLLFAC_gp);
OSC.CTRL |= OSC_PLLEN_bm;
while (!(OSC.STATUS & OSC_PLLRDY_bm))
;
/* Set PLL as main clock */
CCP = CCP_IOREG_gc;
CLK.CTRL = CLK_SCLKSEL_PLL_gc;
SYSTEM_TICK_REGISTER = 0;
/* Enable RTC with roughly 1kHz clock for system tick
* and to wake up while sleeping. */
CLK.RTCCTRL = CLK_RTCSRC_ULP_gc | CLK_RTCEN_bm;
RTC.PER = SYSTEM_TICK_PERIOD - 1;
RTC.COMP = SYSTEM_TICK_PERIOD - 1;
RTC.CTRL = RTC_PRESCALER_DIV1_gc;
RTC.INTCTRL = RTC_OVFINTLVL_LO_gc;
/* Enable EEPROM data memory mapping */
//NVM.CTRLB |= NVM_EEMAPEN_bm;
/* Enable DMA */
DMA.CTRL = DMA_ENABLE_bm | DMA_DBUFMODE_DISABLED_gc | DMA_PRIMODE_RR0123_gc;
}
void SystemReset(void) {
CCP = CCP_IOREG_gc;
RST.CTRL = RST_SWRST_bm;
}
void SystemEnterBootloader(void) {
/* Use Watchdog timer to reset into bootloader. */
CCP = CCP_IOREG_gc;
WDT.CTRL = WDT_PER_500CLK_gc | WDT_ENABLE_bm | WDT_CEN_bm;
}
void SystemStartUSBClock(void) {
//SystemSleepDisable();
#if 0
/* 48MHz USB Clock using 12MHz XTAL */
OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
OSC.CTRL |= OSC_XOSCEN_bm;
while (!(OSC.STATUS & OSC_XOSCRDY_bm))
;
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | (4 << OSC_PLLFAC_gp);
OSC.CTRL |= OSC_PLLEN_bm;
while (!(OSC.STATUS & OSC_PLLRDY_bm))
;
#else
/* Use internal HS RC for USB */
OSC.CTRL |= OSC_RC32MEN_bm;
while (!(OSC.STATUS & OSC_RC32MRDY_bm))
;
/* Load RC32 CAL values for 48MHz use */
NVM.CMD = NVM_CMD_READ_CALIB_ROW_gc;
DFLLRC32M.CALA = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, USBRCOSCA));
DFLLRC32M.CALB = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, USBRCOSC));
NVM.CMD = NVM_CMD_NO_OPERATION_gc;
/* For USBSOF operation, whe need to use COMP = 48MHz/1kHz = 0xBB80 */
DFLLRC32M.COMP1 = 0x80;
DFLLRC32M.COMP2 = 0xBB;
/* Select USBSOF as DFLL source */
OSC.DFLLCTRL &= ~OSC_RC32MCREF_gm;
OSC.DFLLCTRL |= OSC_RC32MCREF_USBSOF_gc;
/* Enable DFLL */
DFLLRC32M.CTRL |= DFLL_ENABLE_bm;
#endif
}
void SystemStopUSBClock(void) {
//SystemSleepEnable();
#if 0
/* Disable USB Clock to minimize power consumption */
CLK.USBCTRL &= ~CLK_USBSEN_bm;
OSC.CTRL &= ~OSC_PLLEN_bm;
OSC.CTRL &= ~OSC_XOSCEN_bm;
#else
/* Disable USB Clock to minimize power consumption */
CLK.USBCTRL &= ~CLK_USBSEN_bm;
DFLLRC32M.CTRL &= ~DFLL_ENABLE_bm;
OSC.CTRL &= ~OSC_RC32MEN_bm;
#endif
}
void SystemInterruptInit(void) {
/* Enable all interrupt levels */
PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
sei();
}