Interrupts


Interrupts of various types play a role in implementing the PiTrex software efficiently, and with reliable Linux operation (taken from an email by Kevin Koster):

VIA Interrupts

The 6522 in the Vectrex has an interrupt output that can be triggered
by events including the expiry of the scale timer that controls the
length of lines drawn on the Vectrex screen. I designed the PiTrex
to be able to respond to this by turning off the beam itself after
receiving this signal, thereby allowing the Pi to do other things
because it doesn't have to respond instantly. Malban eventually
convinced me that this wouldn't work because the Vectrex's analogue
circuitry controlled by the 6522 is slow and the PiTrex would react
before the beam position had actually stopped moving. I continued
exploring convoluted solutions to this, but have mostly forgotten what
these were now, and never got any to work.

BCM2835 Timer, EDR, etc. Interrupts:

These are hardware interrupts that the Pi's CPU can respond to. With
the PiTrex's own interrupt solution not working because it didn't wait
for enough time that the Vectrex circuitry was ready, this is a similar
solution in software. The Pi's own interrupt system can be triggered
either by the VIA Interrupt corresponding to expiry of its scale timer,
or one of the Pi's internal timers running in parallel with (or
possibly instead of) the 6522's scale timer. So instead of polling to
see if a line has been drawn to the end, the Pi can again do other
things until the interrupt causes it to go into an ISR that waits for
the delay of the Vectrex's analogue circuitry and then turns off the beam.

Also, during read/write operations in pitrexio-gpio, it can use an
interrupt triggered by the RDY input from the PiTrex to allow other
processing to take place during read/write operations to VIA registers
(which are done at the Vectrex's clock rate, so still correspond to a
significant number of Pi CPU cycles at 1GHz).

One issue with this is that I'm yet to find particularly good
documentation for BCM2835 hardware interrupt usage. Though there's
probably enough to get things working with some effort.

Another issue is that with Linux, interrupts can't be caught directly
by user-space programs, and by the time they've filtered down from the
kernel to user-space software, a significant and variable delay has
been added. So we really want to do this bare-metal or with a Linux
kernel driver (if I've got the right impression about how much more
quickly a kernel driver can react to interrupts - still to be
researched further).

Linux system interrupts:

I haven't got any experience with Linux internals, so I'm a bit vague
on the exact mechanics of this.

These are software interrupts that break execution of our code so that
the Pi can go off to do some supposably more important Linux business.
We're at the mercy of them because we've (I've) failed to get the
PiTrex hardware to react to VIA interrupts itself, so Linux can suspend
our code at the wrong point and cause it to miss whichever
signal/timer-overflow is used to indicate the end of a line.

Hopefully with a kernel driver, we can boss about the kernel and keep
it from doing anything while our code is polling to see when a draw
operation completes. But better would be if we can use BCM2835
interrupts in the Linux driver, and set the interrupt priority such
that running the ISR for handling end-of-line is a process that's
unable to be suspended. That way the Pi can be running useful code
during a draw operation, and also not getting in the way of Linux's
kernel processes for any longer than we need to.


So at the moment the Pi's CPU is tied up a lot of the time (mainly
during line draw operations, but also during each read/write to the VIA)
with polling inputs or (optionally) internal timers. The first two
interrupt types (the hardware interrupts) offer ways to have the Pi's
CPU spend this time executing more code (eg. an emulator) instead (this
can be especially efficient now that Chris has a buffer system in the
vector drawing library). The third (the software interrupts) sabotages
the polling method when used in Linux, and is best overruled by a Linux
kernel driver that also uses BCM2835 interrupts.

So when talking of the Linux kernel driver as a solution to the Linux
system interrupts, I think also of BCM2835 interrupts as part of a
preferable implementation of that. One that would also permit better
performance, hopefully to counter the added load of the other Linux
kernel processes that cause Chris to doubt that the Pi would be fast
enough to run Linux as well as all of the emulators that we want.

Linux Interrupt Details and Links


This outlink is the Git commit that added the BCM2835 timer support to the Linux kernel. It notes that timer 3 is used by Linux to generate the system interrupt (tick). The timer operates as a one-shot. When it expires, "bcm2835_time_set_next_event" is called to increment the counter compare register value to the time required for the next system interrupt. The counter itself is free-running, so the compare register value has to jump ahead of it after each interrupt.

A kernel function called "sched_clock", which retruns the time in nanoseconds since the system was started, is described in the kernel documentation outlink. This could be used within a kernel driver to check whether an interrupt is going to happen before completion of a drawing operation.

"/proc/timer_list" provides a user-space source of the same nsec value from start-up on line three of the output. At the bottom "Tick Device:" describes the hardware timer configuration, and "next_event:" shows what time in nsec the next interrupt is set for. There is old documentation outlink for this interface. The fields for the tick device after the first three lines are described in the clockchips outlink.h header - search code for "clock_event_device - clock event device descriptor", only a few of the structure members seem to get printed.

The interval between interrupts corresponds to the sheduler clock "tick" frequency. This is set at kernel compile-time. RealtimePi seems to have it set to 100Hz, or 10ms between tick interrupts. This is a traditional value, but other modern distros may use a higher frequency.

To check the kernel's tick frequency in RPi OS and similar distros (though we can probably rely on 100Hz most of the time):

sudo modprobe configs
zcat /proc/config.gz | grep CONFIG_HZ=


In case the scheduler isn't actually slow enough to cause significant glitches, but they are caused by the process being suspended in order for another task to run, then the kernel's scheduling system has facilities for setting real-time priorities for a process using the sched_setscheduler function from sched.h. See the man page for sched_setscheduler outlink and sched outlink. These are enhanced when running a kernel with the real-time patch.

Using sched_setscheduler to set process scheduling mode to FIFO and maximum priority doesn't seem to help even when running realtimePi, so the tick interrupt probably is itself the main cause of the glitches.

Update:

Tests so far have been unsuccessful at avoiding glitches by checking the time before the next tick interrupt. In fact three of the four system timer interrupts are used by Linux to do something, but even when avoiding all three there's still a normal regularity of glitches. It's probably another hardware interrupt causing most of the trouble, maybe sound or video related (can't work out how to find/kill the sound and video processes so that related kernel modules can be unloaded - not obvious in the output of "ps -A").

Reading/writing the interrupt enable/disable registers causes a segmentation fault, so they're probably mapped to the kernel. That cuts off that method of disabling interrupts besides the timer tick.

Unless a way can be figured out to unload more kernel modules (and even then it's not certain), this won't work as an quick alternative to writing a kernel driver module.

Another Update:

An adressing mistake in the test code was the reason that disabling interrupts didn't work on the first attempt. With this fixed, ZBlast using SVGA-Vectrex was able to be run in Linux without glitches!

Linux kernel driver

An initial attempt to implement a Linux kernel driver was suspended because it was was taking too long. There is unfinished code outlink and a list of pros and cons to the approach. It's still beleived that the basic principle should be sound, but writing Linux kernel drivers is just hard.

Detecting Linux System Interrupts

Although disabling interrupts while processing the vector pipeline when running on Linux has proven to be a reliable method of preventing display glitches, it has impacted the reliability of Bluetooth peripherals. Currently the necessary buffer size for Bluetooth audio in order to prevent drop-outs is large enough to cause a noticable delay. Bluetooth controllers are also prone to drop-outs when Bluetooth audio is also in use.

Therefore it is desireable to permit Linux system interrupts, yet detect and account for them by inserting extra zeroing operations in the pipeline display process when they delay execution for long enough as to cause drift in the Vectrex beam position. This detection is done by comparing the expected time to perform individual reads, writes, and delays for the Vectrex, with the time recorded by the ARM system timer. Whenever an excessive delay due to a system interrupt is detected, an extra zeroing operation is performed before beginning the next operation in the vector pipeline.

To avoid bright spots at the end of lines when the pipeline display program is interrupted during a draw operation and can't turn off the beam at the end of the line, A program running on the spare VPU core of the Raspberry Pi Zero GPU is given control of this function. It is unaffected by interrupts, so will always turn the beam off at an accurate pre-set delay after the expiry of the T1 timer in the Vectrex.

More details about this are described on the GPU page and Interrupt_Detection_Detail.