AVR-like ISR()

BBenj

Member
Hello

I began to "play" with my Teensy 3.0 board today, directly with FlexTimers...

I found a solution to have a AVR-like ISR() macro which doesn't appears to be implemented in Teensyduino for now. Actually it's the AVR ISR macro itself... :p
I just pasted the macro from avr-libc to mk20dx128.h (just before the isr functions prototypes):

Code:
#ifdef __cplusplus
#  define ISR(vector, ...)            \
    extern "C" void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
#else
#  define ISR(vector, ...)            \
    void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
#endif

Use the ISR() macro with the isr functions names (defined in mk20dx128.h, and just below for convenience), for example:

Code:
ISR(ftm0_isr)
{
  FTM0_SC &= ~FTM_SC_TOF;
  digitalWrite(13, state ^= HIGH);
}

Tested successfully with ftm0 and ftm1. :cool:

Have fun with Teensy ;)


***
And just a tip for anyone using the FlexTimers, you need to write 0 in FTM0_SC before writing to FTM0_MOD, otherwise FTM0_MOD will not be updated (stay at 0xBFFF for me). I spent some time to find that...


***
Here is the list of Teensy 3.0 ISRs:
Code:
	nmi_isr,					//  2 ARM: Non-maskable Interrupt (NMI)
	hard_fault_isr,					//  3 ARM: Hard Fault
	memmanage_fault_isr,				//  4 ARM: MemManage Fault
	bus_fault_isr,					//  5 ARM: Bus Fault
	usage_fault_isr,				//  6 ARM: Usage Fault
	fault_isr,					//  7 --
	fault_isr,					//  8 --
	fault_isr,					//  9 --
	fault_isr,					// 10 --
	svcall_isr,					// 11 ARM: Supervisor call (SVCall)
	debugmonitor_isr,				// 12 ARM: Debug Monitor
	fault_isr,					// 13 --
	pendablesrvreq_isr,				// 14 ARM: Pendable req serv(PendableSrvReq)
	systick_isr,					// 15 ARM: System tick timer (SysTick)
	dma_ch0_isr,					// 16 DMA channel 0 transfer complete
	dma_ch1_isr,					// 17 DMA channel 1 transfer complete
	dma_ch2_isr,					// 18 DMA channel 2 transfer complete
	dma_ch3_isr,					// 19 DMA channel 3 transfer complete
	dma_error_isr,					// 20 DMA error interrupt channel
	unused_isr,					// 21 DMA --
	flash_cmd_isr,					// 22 Flash Memory Command complete
	flash_error_isr,				// 23 Flash Read collision
	low_voltage_isr,				// 24 Low-voltage detect/warning
	wakeup_isr,					// 25 Low Leakage Wakeup
	watchdog_isr,					// 26 Both EWM and WDOG interrupt
	i2c0_isr,					// 27 I2C0
	spi0_isr,					// 28 SPI0
	i2s0_tx_isr,					// 29 I2S0 Transmit
	i2s0_rx_isr,					// 30 I2S0 Receive
	uart0_lon_isr,					// 31 UART0 CEA709.1-B (LON) status
	uart0_status_isr,				// 32 UART0 status
	uart0_error_isr,				// 33 UART0 error
	uart1_status_isr,				// 34 UART1 status
	uart1_error_isr,				// 35 UART1 error
	uart2_status_isr,				// 36 UART2 status
	uart2_error_isr,				// 37 UART2 error
	adc0_isr,					// 38 ADC0
	cmp0_isr,					// 39 CMP0
	cmp1_isr,					// 40 CMP1
	ftm0_isr,					// 41 FTM0
	ftm1_isr,					// 42 FTM1
	cmt_isr,					// 43 CMT
	rtc_alarm_isr,					// 44 RTC Alarm interrupt
	rtc_seconds_isr,				// 45 RTC Seconds interrupt
	pit0_isr,					// 46 PIT Channel 0
	pit1_isr,					// 47 PIT Channel 1
	pit2_isr,					// 48 PIT Channel 2
	pit3_isr,					// 49 PIT Channel 3
	pdb_isr,					// 50 PDB Programmable Delay Block
	usb_isr,					// 51 USB OTG
	usb_charge_isr,					// 52 USB Charger Detect
	tsi0_isr,					// 53 TSI0
	mcg_isr,					// 54 MCG
	lptmr_isr,					// 55 Low Power Timer
	porta_isr,					// 56 Pin detect (Port A)
	portb_isr,					// 57 Pin detect (Port B)
	portc_isr,					// 58 Pin detect (Port C)
	portd_isr,					// 59 Pin detect (Port D)
	porte_isr,					// 60 Pin detect (Port E)
	software_isr,					// 61 Software interrupt
 
Hello

I began to "play" with my Teensy 3.0 board today, directly with FlexTimers...

I found a solution to have a AVR-like ISR() macro which doesn't appears to be implemented in Teensyduino for now. Actually it's the AVR ISR macro itself... :p
I just pasted the macro from avr-libc to mk20dx128.h (just before the isr functions prototypes):
...

If I put that into main.cpp and try to compile it with a Makefile, I get warnings that the attributes are ignored:
Code:
main.cpp:44:1: warning: 'signal' attribute directive ignored [-Wattributes]
main.cpp:44:1: warning: '__INTR_ATTRS' attribute directive ignored [-Wattributes]

If I compile your ISR(ftm0_vect) code inside of Arduino, I don't see the warnings, but I do if I turn on Arduino/Preferences/Show verbose output during compilation, then they do.

From http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html it looks like __attribute signal is AVR only, and __attribute ((interrupt)) or __attribute((isr)) might work.

This gives no warnings, whether it is in the mk20dx128.h file, in a sketch, or in the main.cpp file:

Code:
#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
#  define __INTR_ATTRS used, externally_visible
#else /* GCC < 4.1 */
#  define __INTR_ATTRS used
#endif

#ifndef ISR
# ifdef __cplusplus
#  define ISR(vector, ...)            \
    extern "C" void vector (void) __attribute__ ((interrupt,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
# else
#  define ISR(vector, ...)            \
    void vector (void) __attribute__ ((interrupt,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)
# endif
#endif

But I'm not sure about this. How do I tell how the code is managing cli()/sei() or what? From the GNU document it looks like AVRs cli() on interrupt, __attribute ((interrupt )) would cli, but the __attribute ((signal)) would not. It is unclear to me how __attribute ((interrupt)) acts on an ARM? Does cli before and sei after?

What does a good ISR look like? With or without the ISR macro?
 
Last edited:
How do I tell how the code is managing cli()/sei() or what? From the GNU document it looks like AVRs cli() on interrupt, __attribute ((interrupt )) would cli, but the __attribute ((signal)) would not. It is unclear to me how __attribute ((interrupt)) acts on an ARM? Does cli before and sei after?

What does a good ISR look like? With or without the ISR macro?

I'm pretty sure all that stuff makes absolutely no difference. On ARM, that interrupt stuff is managed by hardware.

It may be hard to believe if you're used to AVR, but truly, all you do on Teensy 3.0 is write an ordinary function using the right name and it automatically works.

On AVR, interrupt functions are special. The compiler generates a special epilogue and prologue that saves and restores any registers. The prologue has the special RETI instruction. Those extra options to ISR(name, options) cause it to generate that epilogue and prologue code differently, possibly enabling interrupts while the interrupt function runs.

ARM does far more in hardware. In fact, ARM does so much that interrupt functions need no special code at all. There's simply no need for an ISR() macro. Just an ordinary function is used. The only thing that makes it an interrupt is the address is stored in the vector table. Teensy's core library defines those names as weak symbols, so all you have to do is write a function with the right name and it automatically becomes an interrupt function, because the linker puts your function's address into that table.

Very smart people at ARM put a lot of thought and good design into the Cortex interrupt system. The ARM Cortex processor handles interrupts with a lot of special work, and the Nested Interrupt Vector Controller (NVIC) controls which interrupts can run.

When an interrupt happens, the processor automatically saves the registers onto the stack. It also fetches the interrupt vector address, and then begins fetching code from that address. The ARM core has separate buses to simultaneously access the RAM and Flash, so the code is fetched while the registers are being saved. The saved return address includes a special value which is recognized by the ordinary return instruction, so the compiler can generate ordinary code and it will automatically return properly to whatever was interrupted. Because the hardware handles saving and restoring registers, it does a crafty "tail chain" optimization if another interrupt is pending, where it jumps pretty much from the end of one interrupt to the beginning of the next without needlessly restoring and resaving the registers.

You can globally disable and enable interrupts on ARM. Teensy implements the AVR names cli(), sei() and Arduino names noInterrupts() and interrupts() which do this. But unlike AVR, the state of interrupt enable/disable is not the only thing controls when your interrupt can run. The global interrupt state is not automatically disabled when your interrupt code runs, because that's not how the NVIC manages things.....

The NVIC implements interrupt priority levels, from 0 to 255 (however, only the upper 4 bits are implemented for 16 different levels, so 0-15 are the same, 16-31 are the same, and so on). The lower the number, the higher the priority. By default, all interrupts are priority 0, which means no other interrupt is allowed until it returns. The NVIC keeps track of the current priority level as interrupts are run and when they return. So it doesn't matter if you use sei() inside your interrupt routine (or an ISR macro which puts a SEI instruction early in the function epilogue). The NVIC doesn't allow another interrupt of the same or lower priority until your function returns. If you want to use this feature to allow some interrupts to interrupt others, instead of fiddling with state at run-time, you just assign each interrupt a priority ahead of time. The NVIC and ARM hardware automatically manages everything.

So the result is you can just use the names defined in that table as ordinary functions, without a special ISR macro, because the ARM Cortex processor doesn't need any of that special stuff that's done on AVR.

I had originally thought about trying to incorporate some sort of emulation of AVR interrupts, with the exact names defined for Arduino Uno's chip. But that turned out to be impractical for many reasons. Instead, I'm focusing my efforts to simply port those libraries to native code. Many of them are already ported. I have a list of others I intend to port, based on the IntervalTimer library (which will be included in Teensyduino's core library soon) rather than directly embedded interrupt code.

I have no plans to ever include an ISR() macro in Teensyduino, because it isn't necessary for ARM code and properly emulating every aspect of the AVR hardware just isn't very practical. However, I could do more to document all this stuff. Hopefully this message is a good start?
 
Last edited:
i am trying to make DMX code that i used on a maple mini (ARM) work on the teensy3.

every time data comes in on the serial/usart pin this interrupt gets called and data is written to an array.
hwo would i do this now on a teensy3, since there is no ISR macro ?

Code:
extern "C" {
  void __irq_usart1(void) {
    unsigned char status = USART1_BASE->SR;
    data = USART1_BASE->DR;

    //onboard LED at D33 get toggled when timer is called
    //gpio_toggle_bit(LED_PORT, LED_BIT);

    switch (dmx_state)
    {
    case DMX_IDLE:
      if (status & 1<<USART_SR_FE_BIT)
      {
        dmx_addr = 0;
        dmx_state = DMX_BREAK;
       // update = 1;
        //delayMicroseconds(8);
      }
      break;

    case DMX_BREAK:
      if (data == 0)
      {
        dmx_state = DMX_START;
      }
      else
      {
        dmx_state = DMX_IDLE;
      }
      break;

    case DMX_START:
      dmx_addr++;
      /*
      if(printTestStage == 1){

        dmx_test_data[dmx_addr] = data;
//        SerialUSB.print(dmx_addr);
//        SerialUSB.print(" , ");
//        SerialUSB.println(data);
      }
      */
      if (dmx_addr == dmx_start_addr)
      {
        chan_cnt = 0;
        dmx_data[chan_cnt++] = data; //constrain(data,0,maxDMXvalue); //data;
        dmx_state = DMX_RUN;
      }
      break;

    case DMX_RUN:
      //for (int wasteTime =0; wasteTime <2; wasteTime++) {}
      dmx_data[chan_cnt++] = data; //constrain(data,0,maxDMXvalue); //data;
      if (chan_cnt >= DMX_NUM_CHANNELS)
      {
       // printTestStage = 2;
        dmx_state = DMX_IDLE;
      }
      break;

    default:
      dmx_state = DMX_IDLE;
      break;
    }
  }
}
 
Back
Top