Skip to content

Timer's IRQ example

Alexander edited this page Jun 24, 2014 · 8 revisions

Brief

This example demonstrates the Timer's IRQ handling on EMSK board

Features

  • Demonstrates minimalistic CRT (C Run-Time) start-up code for EM4/EM6 core
  • Demonstrates how to install handlers dynamically
  • Demonstrates how to work with auxiliary registers in ARC GNU
  • Demonstrates how to setup timers
  • Demonstrates basic IRQ handling and configuration
  • Demonstrates how to control EMSK LEDs

Known limitations

  • The example requires ARC 4.8-R4 at least
  • The example works only on EMSK configurations with full register set (reduced register set was not supported by compiler for now)

Files

  • cachectrl.c -- functions for I/D-cache invalidations
  • cachectrl.h -- support header for cachectrl.c
  • crt0.s -- minimalistic CRT (C Run-Time) start-up code for EM4/EM6 core
  • leds.c -- LEDs initialization and control function
  • leds.h -- support header for leds.c
  • link_arcem4.ln -- linker script for EMSK EM4 configuration 0
  • Makefile -- master makefile
  • Makefile.os_specific -- support makefile with some OS (Windows/Linux) specific stuff
  • setvect.c -- functions to set IRQ/Exceptions dynamically
  • setvect.h -- support header for setvect.c
  • test.c -- the test application

Application flow

Initialization

The initialization contains three parts:

  1. LEDs initialization

    First we need to obtain the GPIO base address.

     ulGPIOBaseAddress = __builtin_arc_lr(PERIPHERAL_BASE_ADDRESS) + GPIO_BASE_ADDR;
    

    The ulGPIOBaseAddress contains the GPIO base address.

     LEDs_Init(ulGPIOBaseAddress);
    

    The second, we need to setup GPIO as LEDs. So the LEDs_Init() setups the output direction for GPIO and clear LEDs. Please make sure that 1 in data register means the LED is off (inverted control).

  2. IRQ controller initialization

    First we need disable all interrupts before working with IRQ. Of course most of operations are atomic but this is good practice and allows avoid any possible issues.

     __builtin_arc_clri();
    

    Second, setup IRQ handler dynamic. If the IVT locates in writable memory, the handler may be set dynamic in application flow. Otherwise the handler should be hard-coded in the IVT. Helper function _setvecti() set handler for desired IRQ number.

     _setvecti(16, IRQ_Timer0);
     _setvecti(17, IRQ_Timer1);
    

    Also we configure hardware to store and restore all possible registers automatically.

     __builtin_arc_sr(AUX_IRQ_CTRL_SAVE_ALL, AUX_IRQ_CTRL);
    

    Third, after all configurations (include LEDs and timers), we should enable interrupts and set the level of sensitive. The timers in EMSK 1.0 and 1.1 have levels 0 and 1, so it is enough to set sensitive to 2.

     __builtin_arc_seti(ENABLE_INTERRUPTS | 2);
    
  3. Timers initialization We need to set limit, enable timer's interrupt and reset counter. User can chose the timer by changing the TIMER_NUMBER if the design supports multiple timers.

     __builtin_arc_sr(0x1FFFFF, TIMER0_LIMIT);//set limit
     __builtin_arc_sr(TIMER_CONTROL_IE, TIMER0_CONTROL);//enable interrupt
     __builtin_arc_sr(0x00, TIMER0_COUNT);//reset counter
    

Main loop

Main infinite loop writes current timer counter value (variable gn_TimerCount) to LEDs. Thus LEDs represents 9-bit binary value of counter.

while(1){ LEDs_Write(ulGPIOBaseAddress, ~gn_TimerCount);}

Interrupt handlers

The timer's interrupt handlers are similar and have only one difference. The timer's 0 handler decrements gn_TimerCount. Otherwise timer's 1 handler increments gn_TimerCount. The interrupt handler in opposite regular function uses special attribute in declaration:

void IRQ_Timer0(void) __attribute__ ((interrupt ("ilink")));
void IRQ_Timer1(void) __attribute__ ((interrupt ("ilink")));

Thus compiler insert special prologue (interrupt entry code) and epilog (interrupt exit code).

The interrupt handler for timer should clear pending flag inside handler. Otherwise the handler will be called after exit immediately.

unsigned int t_ctrl = __builtin_arc_lr(TIMER0_CONTROL);
t_ctrl &= ~TIMER_CONTROL_IP;//clear IP bit
__builtin_arc_sr(t_ctrl, TIMER0_CONTROL);

After that handler increments or decrements the gn_TimerCount.

Start-up code

The start-up code contains following major parts:

  1. IVT table

  2. Minimalistic start-up. It initialize stack pointer (SP) and global pointer (GP)

     mov gp, @__SDATA_BEGIN__
     mov sp, @__stack_top 
    

    And jump to the main.

  3. Default handlers for exceptions and interrupts. The handler halts CPU

     _exit_halt:
         ; r0 contains exit code
         flag 0x01 ; halt CPU
         nop
         nop
         nop
         j @_exit_halt
         nop
    
Clone this wiki locally