RTC Website

section 5.4 companion and outline

This page contains companion resources and an outline for section 5.4 of the book An Introduction to Real-Time Computing for Mechanical Engineers, and it therefore lacks most of section 5.4’s contents. While some sections of the book are fully available on this site, many are not. Please consider purchasing a copy from the MIT Press.

Interrupts and their handling in real-time computing

Programming the target computer for DI interrupts

TX

Lab 5 demonstrates the application of an external digital interrupt. The main() function will simply print on the display the number of seconds that have elapsed since the start of the program. Simultaneously, the ISR, running in a separate thread, will respond to each asynchronous external signal by printing the word “interrupt_.” Properly executed, the count will continue undisturbed, with the word “interrupt_” interspersed randomly.

In preparation for the lab, this section describes the steps necessary to ready the target computer to respond to an interrupt from a digital input (DI). The myRIO handles the DI with its field-programmable gate array (FPGA). The FPGA must be configured to designate specific inputs as IRQ lines.

The myRIO C library header files DIIRQ.h and IRQConfigure.h provide constants, data types, and utility functions for programming DI interrupts. The utility functions are described here and the specific implementations are described in the lab exercise.

Before the code of the ISR can respond to an interrupt, two programming tasks must be performed. First, the digital interrupt must be registered. That is, the specific DI must be identified and its operational parameters configured. The second task is to create a new thread in which the ISR will run when an interrupt occurs. In our lab exercise, the main() function, executing in its own thread, will perform both of these tasks.

The two threads (main and interrupt) communicate through a globally defined thread resource structure declared in the main thread as follows:

typedef struct {
  NiFpga_IrqContext irqContext; // IRQ context reserved
  NiFpga_Bool irqThreadRdy;     // IRQ thread ready flag
  uint8_t irqNumber;            // IRQ number value
} ThreadResource;

For example, main() will use the “IRQ thread ready flag” both to enable the ISR and to signal the interrupt thread to terminate.

Task I.—Register the DI0 Interrupt Request

The myRIO C library provides a function to reserve the interrupt from the FPGA and configure the DI and the IRQ. Its prototype is

int32_t Irq_RegisterDiIrq(MyRio_IrqDi *irqChannel,
                          NiFpga_IrqContext *irqContext,
                          uint8_t irqNumber,
                          uint32_t count,
                          Irq_Dio_Type type);

Its return value is 0 for success, and its five input arguments are

  1. irqChannel: A pointer to a structure containing the registers and settings for the IRQ I/O to modify; defined in DIIRQ.h as

    typedef struct{
      uint32_t dioCount;           // count register
      uint32_t dioIrqNumber;       // number register
      uint32_t dioIrqEnable;       // enable register
      uint32_t dioIrqRisingEdge;   // rising edge-trig register
      uint32_t dioIrqFallingEdge;  // falling edge-trig register
      Irq_Channel dioChannel;      // physical DIO channel number
    } MyRio_IrqDi;

    This structure identifies the digital channel and how it is to be configured.

  2. irqContext: A pointer to a context variable identifying the interrupt to be reserved. It is the first component of the previously given thread resources structure.

  3. irqNumber: The IRQ number (1–8) distinguishes this from other interrupts.

  4. count: The number of times that an interrupt condition is met to trigger the interrupt.

  5. type: The trigger type that is used to increment the count.

Task II.—Create the interrupt thread

The ISR will run in a pthread (subsection 5.3.1). The function pthread_create(), called from main(), creates a new thread and configures it to service the DI interrupt. Its prototype is

int pthread_create(pthread_t *thread,
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *),
                   void *arg);

where the four input arguments are

  1. thread: A pointer to a thread identifier.
  2. attr: A pointer to thread attributes. Use NULL to apply the default attributes.
  3. start_routine: The name of the function to start in the new thread.
  4. arg: The thread resource.

The fourth argument is a pointer to the same thread resource structure defined above and used in the second argument of Irq_RegisterDiIrq(). Just prior to creating the thread, main() will set the IRQ thread ready flag to enable the ISR to process interrupts.

Once the interrupt thread is created and enabled, the main thread (in our case, running the function main()) can carry out its tasks while the interrupt thread runs independently.

Terminating the ISR thread and unregistering the interrupt

When the main thread has completed its tasks, it can signal the interrupt thread to terminate by resetting the interrupt ready flag:

irqThread0.irqThreadRdy = NiFpga_False;

Then the main thread should join the ISR thread to wait for its completion:

pthread_join(irqThread0, NULL);

Finally, the interrupt must be unregistered:

status = Irq_UnregisterDiIrq(&irqDI0,
                             irqThread0.irqContext,
                             irqThread0.irqNumber);

All that remains is to write the ISR.

Writing a digital input interrupt service routine

The function started in the interrupt thread created by the pthread_create() is generically called start_routine(), but it should be given a specific name for the digital interrupt ISR, such as DI_ISR(). Its role is to respond to the interrupt. This thread will execute until signaled to stop by main().

The ISR corresponds to the third argument of pthread_create(), so it must conform with that prototype. For instance, a valid ISR function is

void* DI_ISR(void *thread_resource) {
  // service the interrupt
}

The argument of the starting routine, void* thread_resource, should be cast to a pointer to the ThreadResource defined in the main thread. Remember that it is by this means that the two threads communicate.

Assuming that more than one interrupt is to be processed, the ISR should enter a loop, waiting for each instance of the registered IRQ to occur. That loop should terminate when signaled by the interrupt ready flag. The Irq_Wait() function supplied by the myRIO C library can be invoked to pause the ISR thread until it is activated by its IRQ. The prototype is

void Irq_Wait(NiFpga_IrqContext irqContext,
              NiFpga_Irq irqNumber,
              uint32_t *irqAssert,
              NiFpga_Bool *continueWaiting);

The first, second, and fourth arguments correspond to the members of the ThreadResource irqThread0 created in the main thread. For example, it might be programmed as1

Irq_Wait(thread_resource->irqContext,
         thread_resource->irqNumber,
         &irqAssert,
         (NiFpga_Bool*) &(thread_resource->irqThreadRdy));

The variable of the third argument, the integer irqAssert, indicates which interrupt has occurred. If an IRQ is asserted during the Irq_Wait(), its bit will be assigned to &irqAssert. The irqNumber indicates the position in irqAssert of the bit corresponding to the specific interrupt. This allows us to test for an assertion and acknowledge it with a bit mask. For example,

if (irqAssert & (1 << thread_resource->irqNumber)) {
  // service the interrupt here
  Irq_Acknowledge(irqAssert);    // acknowledge the IRQ(s)
}

The code necessary to respond to the IRQ interrupt should be placed inside the if-statement here.

Once the loop has terminated, we can close the ISR pthread and return a pointer, which can be NULL, as follows:

pthread_exit(NULL);
return NULL;

In lab 5, we will implement an ISR to process an indefinite number of DI interrupts.

Online resources for Section 5.4

No online resources.


  1. The -> or “arrow” operator in the expression s->m is used to access the value of the member m of a structure pointer s. See Kernighan1988.↩︎