In the last article, I stated “I see no benefit to level-triggered interrupts. … If you believe otherwise, let me know…” and you did.
First, a clarification: Your responses let me know that my use of the term “interrupt module” was the source of some confusion about last month’s article. In it, I used “interrupt module” to mean the module inside a block that watches for events within the block, triggers interrupts on those events, and propagates the interrupts to the CPU. I should have stated this more clearly because some of you read “interrupt module” to mean the module inside the CPU that watches for events from various components in the system. The following figure illustrates these different interrupt modules:
Within the block there are multiple interrupt sources feeding into one interrupt module (IM), which produces one interrupt from the block. Within the ASIC, there are multiple block interrupts feeding into one IM, which produces one interrupt from the ASIC. Within the CPU, there are multiple interrupt sources feeding into the one IM which interrupts program execution when any one of them fires. Because CPUs are often pin-limited, interrupt lines from external ASICs, sensors, and other components must be logically OR’d together, forcing firmware to poll each of those devices to figure out which one generated the interrupt. Even though last month I focused on IMs at the block level, the IM at each of those levels – block, ASIC, and CPU – are basically the same and should all be edge-triggered interrupts. Several readers raised important concerns about edge-triggered interrupts, so I’ll try to address them point-by-point.
One common concern from readers was signal noise. Jay Dowling wrote, “edge triggered interrupts … are highly susceptible to transients on the supply or ESD.” Dan Swiger wrote, “They can cause problems when they’re sourced by a noisy actuator.” Jay and Dan also described techniques to filter out the noise so that it was not a problem for level-triggered interrupts.
It is good design practice to put noise filters/debouncers on any signal – not just interrupts – coming into a chip or CPU. A filtered signal will behave just as well feeding into an edge-triggered IM as a level-triggered IM.
Steve Strobel wrote that he found level-triggered interrupts useful when multiple devices feed into one CPU interrupt. He described that since another device might interrupt while servicing the first, the level-triggered behavior forces the ISR to keep triggering until all devices are serviced. He said level-triggered interrupts avoid race conditions of subsequent devices generating interrupts while the current device is being serviced.
Edge-triggered interrupts can provide the same behavior Steve describes. Firmware can handle multiple devices on the same interrupt line feeding into an edge-triggered IM in the CPU by first acking the interrupt before servicing the devices, and then looping around, if necessary, until all devices have been serviced as illustrated with this pseudo code:
while interrupt pending { ack interrupt for each device { if device has interrupt pending service and ack the device } }
The race condition is avoided by acking before servicing the device and then looping. If a different device generates another interrupt while servicing the first, it will stay in the loop because an interrupt is pending again. In the level-triggered case, in order to avoid re-triggering for the same event, firmware cannot ack the interrupt until after servicing the device.
Jay Dowling also commented, “Additionally, [level-triggered interrupts] can detect an interrupt when coming out of reset or a power down state.” Since the interrupt enable at reset defaults to disable, an IM will not generate an interrupt until the device driver enables interrupts. If it were a level-triggered IM, any asserted interrupt lines will immediately generate an interrupt. In an edge-triggered case, the asserted interrupt lines will not generate an interrupt because the edge has already passed. However, if the device driver wishes to service any pre-existing events, it can do so by reading which interrupt lines are asserted and responding to them. Edge-triggered interrupts offer the advantage in that the device driver can decide whether or not to respond to any pending interrupt. Since we are talking about coming out of reset or power down state, it may be that the device generating the interrupt is still just booting up, does not really have an interrupt to be serviced, and is not able to respond to firmware attempting to service it.
Some readers also expressed a general unease about edge-triggered interrupts along these lines: “I remember one time I had a problem with edge-triggered interrupts but I can’t remember what it was.” It is the same with me; it seems like I’ve had problems in the past with edge-triggered interrupts but I can’t remember why. I think Steve Strobel addresses this concern with his comment, “So my preference for level-triggered interrupts might have been shaped by a marginal hardware design.” That is, we might have preferred level-triggered in some cases because we did not have the option to change the hardware design. But if we could change the hardware design, what would we want? For me, after having studied this a while, I still think that edge-triggered interrupts is preferable in all situations.
Referring to the edge-trigger vs. level-trigger confusion, Shalom Bresticker made the following comment, which I will use as the best practice for this issue.
Until the next trigger-happy issue…