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.
This commit is contained in:
Federico Cerutti
2019-12-08 14:11:05 +01:00
parent 1a41b8a321
commit 247a0ffd55
8 changed files with 67 additions and 36 deletions
+24 -20
View File
@@ -8,26 +8,6 @@
#ifndef CODEC_H_
#define CODEC_H_
#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include "../Common.h"
#include "../Configuration.h"
#include "../Settings.h"
#include "ISO14443-2A.h"
#include "Reader14443-2A.h"
#include "SniffISO14443-2A.h"
#include "ISO15693.h"
/* Timing definitions for ISO14443A */
#define ISO14443A_SUBCARRIER_DIVIDER 16
#define ISO14443A_BIT_GRID_CYCLES 128
#define ISO14443A_BIT_RATE_CYCLES 128
#define ISO14443A_FRAME_DELAY_PREV1 1236
#define ISO14443A_FRAME_DELAY_PREV0 1172
#define ISO14443A_RX_PENDING_TIMEOUT 4 // ms
/* Peripheral definitions */
#define CODEC_DEMOD_POWER_PORT PORTB
#define CODEC_DEMOD_POWER_MASK PIN0_bm
@@ -87,6 +67,27 @@
#define CODEC_TIMER_TIMESTAMPS_CCA_VECT TCD1_CCA_vect
#define CODEC_TIMER_TIMESTAMPS_CCB_VECT TCD1_CCB_vect
#ifndef __ASSEMBLER__
#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include "../Common.h"
#include "../Configuration.h"
#include "../Settings.h"
#include "ISO14443-2A.h"
#include "Reader14443-2A.h"
#include "SniffISO14443-2A.h"
#include "ISO15693.h"
/* Timing definitions for ISO14443A */
#define ISO14443A_SUBCARRIER_DIVIDER 16
#define ISO14443A_BIT_GRID_CYCLES 128
#define ISO14443A_BIT_RATE_CYCLES 128
#define ISO14443A_FRAME_DELAY_PREV1 1236
#define ISO14443A_FRAME_DELAY_PREV0 1172
#define ISO14443A_RX_PENDING_TIMEOUT 4 // ms
#define CODEC_BUFFER_SIZE 256
@@ -285,4 +286,7 @@ bool CodecIsReaderToBeRestarted(void);
void CodecThresholdSet(uint16_t th);
uint16_t CodecThresholdIncrement(void);
void CodecThresholdReset(void);
#endif /* __ASSEMBLER__ */
#endif /* CODEC_H_ */
+1 -6
View File
@@ -79,13 +79,8 @@ static void StartDemod(void) {
CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0;
}
ISR(CODEC_DEMOD_IN_INT0_VECT) {
isr_func_CODEC_DEMOD_IN_INT0_VECT();
}
// ISR(CODEC_DEMOD_IN_INT0_VECT)
// Find first pause and start sampling
void isr_ISO14443_2A_TCD0_CCC_vect(void) {
ISR_SHARED isr_ISO14443_2A_TCD0_CCC_vect(void) {
/* This is the first edge of the first modulation-pause after StartDemod.
* Now we have time to start
* demodulating beginning from one bit-width after this edge. */
+2 -3
View File
@@ -89,7 +89,7 @@ static volatile uint16_t ReadCommandFromReader = 0;
* and unregistered writing the INT0MASK to 0
*/
// ISR(CODEC_DEMOD_IN_INT0_VECT)
void isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT(void) {
ISR_SHARED isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT(void) {
/* Start sample timer CODEC_TIMER_SAMPLING (TCD0).
* Set Counter Channel C (CCC) with relevant bitmask (TC0_CCCIF_bm),
* the period for clock sampling is specified in StartISO15693Demod.
@@ -146,8 +146,7 @@ INLINE void ISO15693_EOC(void) {
*
* It disables its own interrupt when receives an EOF (calling ISO15693_EOC) or when it receives garbage
*/
// ISR(CODEC_TIMER_SAMPLING_CCC_VECT) // Reading data sent from the reader
void isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) {
ISR_SHARED isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) {
/* Shift demod data */
SampleRegister = (SampleRegister << 1) | (!(CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK) ? 0x01 : 0x00);
@@ -170,13 +170,10 @@ INLINE void BufferToSequence(void) {
if (BitCount % 8)
CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8));
}
// Frame Delay Time PCD to PICC ends
ISR(CODEC_TIMER_SAMPLING_CCC_VECT) {
isr_func_TCD0_CCC_vect();
}
// ISR (TCD0_CCC_vect)
void isr_Reader14443_2A_TCD0_CCC_vect(void) {
// Frame Delay Time PCD to PICC ends
ISR_SHARED isr_Reader14443_2A_TCD0_CCC_vect(void) {
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc;
+3
View File
@@ -17,6 +17,9 @@
#define ODD_PARITY(Value) OddParityBit(Value)//(parity_even_bit(Value) ? 0 : 1)
#define ISR_SHARED \
void __attribute__((signal)) // This function type has to be used for all the interrupt handlers that have to be changed at runtime
#define INLINE \
static inline __attribute__((always_inline))
+34
View File
@@ -0,0 +1,34 @@
; This file allows to share ISR at runtime with a low overhead.
; Functions that have to be shared MUST be defined as ISR_SHARED types
; (Defined in Common.h) to instruct GCC to compile them preserving the stack
;
; Created on: 2019-12-06
; Author: ceres-c
;
#include "Codec/Codec.h"
#include <avr/interrupt.h>
; Find first pause and start sampling
.global CODEC_DEMOD_IN_INT0_VECT
CODEC_DEMOD_IN_INT0_VECT:
push r30
push r31
lds r30, isr_func_CODEC_DEMOD_IN_INT0_VECT
lds r31, isr_func_CODEC_DEMOD_IN_INT0_VECT + 1
icall
pop r31
pop r30
reti
; Frame Delay Time PCD to PICC ends
.global CODEC_TIMER_SAMPLING_CCC_VECT
CODEC_TIMER_SAMPLING_CCC_VECT:
push r30
push r31
lds r30, isr_func_TCD0_CCC_vect
lds r31, isr_func_TCD0_CCC_vect + 1
icall
pop r31
pop r30
reti
+1 -1
View File
@@ -97,7 +97,7 @@ F_CPU = 27120000
F_USB = 48000000
TARGET = Chameleon-Mini
OPTIMIZATION = s
SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c
SRC += $(TARGET).c LUFADescriptors.c System.c ISRSharing.S Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c
SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c
SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/SniffISO14443-2A.c Codec/Reader14443-ISR.S
SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c Application/CryptoTDEA.S
-1
View File
@@ -140,4 +140,3 @@ void SystemInterruptInit(void) {
PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
sei();
}