SAME70 interrupt latency etc

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote a short test program which starts a IRQ handler on each falling edge on a GPIO input pin.

After struggling with some parameters in the ioport and pio ASF functions used to configure the IRQ handler and GPIO pin I finally got as best result a 4µs delay between input pin signal change and IRQ handler start. I am using a scope for measurement.

This is quite a long time for a 300MHz embedded MCU which is optimized for fast interrupt handling I think. I know that there is some code executed in front of the handler due to the PIO handler and FPU support but, well.... I'm stuck here.

// "/INT" pin
#define PIN_NOT_INT_GPIO      (PIO_PA6_IDX)
#define PIN_NOT_INT_FLAGS     (IOPORT_MODE_PULLUP | IOPORT_MODE_GLITCH_FILTER)
#define PIN_NOT_INT_SENSE     (IOPORT_SENSE_FALLING)

#define PIN_NOT_INT_MASK  PIO_PA6
#define PIN_NOT_INT_PIO   PIOA
#define PIN_NOT_INT_ID    ID_PIOA
#define PIN_NOT_INT_ATTR  (PIO_PULLUP | PIO_DEGLITCH | PIO_IT_FALL_EDGE)
#define PIN_NOT_INT_IRQn  PIOA_IRQn


#define PEAK_CURR_IRQ_PRIOR_PIO    0x00		// highest priority


static void PeakCurrentIrq_Handler(uint32_t id, uint32_t mask)
{
	// signal IRQ handler function entry on digital output
	ioport_set_pin_level(PIN_TEST1_GPIO, IOPORT_PIN_LEVEL_HIGH);

	// clear and re-init interrupt
	uint32_t bitmask = pio_get_interrupt_mask(PIN_NOT_INT_PIO);
	pio_disable_interrupt(PIN_NOT_INT_PIO, 0xFFFFFFFF);
	pio_get_interrupt_status(PIN_NOT_INT_PIO);
	NVIC_DisableIRQ((IRQn_Type) PIN_NOT_INT_ID);

	; // do what has to be done

	NVIC_ClearPendingIRQ((IRQn_Type) PIN_NOT_INT_ID);
	NVIC_SetPriority((IRQn_Type) PIN_NOT_INT_ID, PEAK_CURR_IRQ_PRIOR_PIO);
	NVIC_EnableIRQ((IRQn_Type) PIN_NOT_INT_ID);
	pio_enable_interrupt(PIN_NOT_INT_PIO, bitmask);


	// signal IRQ handler function exit on digital output
	ioport_set_pin_level(PIN_TEST1_GPIO, IOPORT_PIN_LEVEL_LOW);

	return;
}


static void enable_peak_irq(void)
{
	pmc_enable_periph_clk(PIN_NOT_INT_ID);
	/* Interrupt on falling edge  */
	pio_handler_set(PIN_NOT_INT_PIO, PIN_NOT_INT_ID, PIN_NOT_INT_MASK, PIN_NOT_INT_ATTR, PeakCurrentIrq_Handler);
	pio_handler_set_priority(PIN_NOT_INT_PIO, (IRQn_Type) PIN_NOT_INT_ID, PEAK_CURR_IRQ_PRIOR_PIO);
}

int main(void)
{
	/* Initialize the SAM system. */
	sysclk_init();
	board_init();

	ioport_set_pin_input_mode(PIN_NOT_INT_GPIO, PIN_NOT_INT_FLAGS, PIN_NOT_INT_SENSE);

	// enable peak current detection
	enable_peak_irq();

	while(1)
	{
		// pull IRQ input pin low to run IRQ handler
		ioport_set_pin_level(PIN_D4_GPIO, IOPORT_PIN_LEVEL_LOW);

		delay_ms(1);
		
		// set IRQ input pin high
		ioport_set_pin_level(PIN_D4_GPIO, IOPORT_PIN_LEVEL_HIGH);

		delay_ms(1);
	}
}

PIN_D4_GPIO is physically connected to the IRQ input pin PIN_NOT_INT_GPIO and is used to trigger the scope.
PIN_TEST1_GPIO is an output used for timing measurement.
Trivial initialization code of these two pins is not shown here in this example.

Has anyone any experience / results / ideas on that topic ?

 

P.S:

PIN_D4_GPIO is physically connected to the IRQ input pin PIN_NOT_INT_GPIO and is used to trigger the scope.
PIN_TEST1_GPIO is an output used for timing measurement.
Trivial initialization code of these two pins is not shown here in this example.

 

SAME newbie

Last Edited: Sun. Jan 22, 2017 - 08:01 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I cannot see in your initialization Code where the CPU is actually set to 300 MHz.

After reset the CPU runs much slower from ist Default clock Settings.

 

You have to call sysclk_init() and have the correct Parameters in conf_clock.h.

 

Regards,

Martin Cibulski

 

From the datasheet:

28.5.1 Embedded 4/8/12 MHz RC Oscillator

After reset, the 4/8/12 MHz RC oscillator is enabled with the 4 MHz frequency selected. This oscillator is selected

as the source of MAINCK. MAINCK is the default clock selected to start the System.

 

The beginning of my conf_clock.h file:

 

#ifndef CONF_CLOCK_H_INCLUDED

#define CONF_CLOCK_H_INCLUDED

 

// ===== System Clock (MCK) Source Options

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_SLCK_RC

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_SLCK_XTAL

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_SLCK_BYPASS

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_MAINCK_4M_RC

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_MAINCK_8M_RC

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_MAINCK_12M_RC

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_MAINCK_XTAL

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_MAINCK_BYPASS

#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLLACK

//#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_UPLLCK

// ===== Processor Clock (HCLK) Prescaler Options (Fhclk = Fsys / (SYSCLK_PRES))

#define CONFIG_SYSCLK_PRES SYSCLK_PRES_1

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_2

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_4

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_8

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_16

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_32

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_64

//#define CONFIG_SYSCLK_PRES SYSCLK_PRES_3

// ===== System Clock (MCK) Division Options (Fmck = Fhclk / (SYSCLK_DIV))

#define CONFIG_SYSCLK_DIV 2

// ===== PLL0 (A) Options (Fpll = (Fclk * PLL_mul) / PLL_div)

// Use mul and div effective values here.

#define CONFIG_PLL0_SOURCE PLL_SRC_MAINCK_XTAL

#define CONFIG_PLL0_MUL 25

#define CONFIG_PLL0_DIV 1

Last Edited: Sat. Jan 21, 2017 - 04:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not list sysclk_init() (my code is derived from an ASF example) in my code example (now I added the function call to my code snippet). I have the same values as you have in your config.

 

Running this code here

uint8_t bVersion, bRevision;
GetProductRevisionIdentifier(&bVersion, &bRevision);
printf("ARM revision identifier r%dp%d\n", bVersion, bRevision);

extern chipid_data_t g_chipid_data;

/* Read CHIPID */
chipid_read(CHIPID, &g_chipid_data);

/* Dump CHIPID information */
chipid_print(&g_chipid_data);

extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
printf("CPU running at %lu MHz\n", SystemCoreClock / 1000000UL);

gives me the following information

ARM revision identifier r0p1
Version 0x0.
Embedded Processor      Cortex-M7.
Nonvolatile program memory size 2048K bytes.
Second nonvolatile program memory size  None.
Internal SRAM size      384K bytes.
Architecture identifier ATSAME70 Series.
Nonvolatile program memory type Embedded Flash Memory.
Extended chip ID is     0x2.
CPU running at 300 MHz

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did some additional tests with compiler optimization levels and cache (CONF_BOARD_ENABLE_CACHE)

 

Cache  Opt.Level       "IRQ delay"

off       O0 (none)      4000 ns        100 %

off       O3 (highest)   1750 ns          44 %

on       O0 (none)        750 ns          19 %

on       O3 (highest)     300 ns            7 %

 

Debugging the E70 is a mess if cache and code optimization is enabled, therefore this is default in my projects. I never realized that these two parameters have that much influence. Wow, overwhelming...

 

Maybe I'll do a test without PIO IRQ sharing to get rid of the pio_handler code. As far as I know the SAM4E interrupt latency is about 120ns. That's what I hope to get with the E70.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did a test with my "real world" application which is running FreeRTOS tasks and chained XDMAC/SPI DMAs but everthing with lower priority.

 

Here is what I got

 

Cache  Opt.Level        "IRQ delay"
off       O0 (none)       6200 ns        100 %
off       O3 (highest)    1750 ns         28 %
on        O0 (none)       4200 ns         68 %
on        O3 (highest)    (not running any more...)

 

This is disappointing...

SAME newbie

Last Edited: Sun. Jan 22, 2017 - 12:50 PM