Has someone tried to add interrupt support to Riscy Pygness?

I just started to play with Riscy Pygness (

formatting link
) on target part of LPCXpresso 1769 board. The system works quite good as interactive environment for experimenting with ARM CORTEX processor, and seems to be an efficient development platform.

The only thing which seems to be lacking is interrupt support. Well, I can write the interrupt service routine in assembly and add it to the kernel, and it will be the most efficient solution. However for interactive experiments it would be much nicer to have possibility to implement ISR as a Forth word. Such functionality is available in the latest version of amforth for ATmega's

formatting link

Has anybody added this functionality to Riscy Pygness? Maybe someone could suggest the best way to implement such functionality? Is it possible to make use of multitasking functionalities provided by the kernel to service interrupts in a reasonable way? What are the traps, which may badly affect the interrupt servicing latency?

-- TIA & Best Regards, Wojtek

Reply to
wzab
Loading thread data ...

I am not familiar with Riscy Pygness, but I can comment generally about interrupt handling.

The problem with writing your interrupt handler in high-level Forth is that the interrupt must first instantiate a Forth environment: stacks, registers, whatever the implementation requires, and do so in such a way as to not affect whatever task was running when the interrupt occurred. This adds some inevitable overhead.

IMO the faster approach is to write the interrupt handler itself in assembler, and make it do a bare minimum: only those things that absolutely have to be done *right now*, and let a high-level word do the less time-critical functions.

For example, if you are doing some data acquisition, your application task can set up the operation and then go into a wait state. When the interrupt occurs, the ISR can read the value, store it somewhere, and set the task to wake up again to do whatever additional processing is necessary.

Of course, I am viewing this from a multitasking perspective.

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

cortex/

I can't say I follow your idea of what an interrupt handler in forth has to do. The stacks are already there. If an interrupt works on top of that there shouldn't be any issues other than possibly overflowing the stacks. If the interrupt routine needs storage between invocations that would be variable memory, no?

That is one of the things I've never understood. Forth is a language based on a virtual machine. That virtual machine is perfectly capable of handling interrupts from what I can see. The only limitation would be if interrupts are allowed during the execution of an assembly language defined word, then that word needs to protect any section of code that should not be interrupted at the Forth level. Otherwise I don't see the issues.

Rick

Reply to
rickman

...

As I said, I'm viewing this from a multitasking perspective: when an interrupt occurs there's no telling which task is running and has its stacks instantiated. If you know there is only one task, and it is idle waiting for the interrupt, you can use its stacks, but what then is the purpose of using interrupts? Why not just poll?

To a limited extent you can get away with pushing something temporarily on whatever is the current data stack, provided you get it off again before exiting the ISR. But that's a far cry from executing high level Forth.

And, of course you want interrupts to occur during executing of assembly language words in an ITC Forth. A lot of your kernel may be written that way. If all your primitives, so identified because those are the ones you want to run fast, have to disable and re-enable interrupts that's just more overhead.

To me, the point of having an interrupt is to be able to respond to an event in the shortest amount of time and with minimum impact on the rest of the running application. On our systems, you can get into an ISR in one or two instruction times, and out in one. Why add further overhead?

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

I presume that there is some sort of 'current task' that points to the task currently executing, and that you can find the current stack from there. Alternatively, use a dedicated interrupt stack.

Why for a limited extent ? And of course you have to pop everything from the stack that you put on.

Reply to
Arlet Ottens

My point is that all of what you describe adds unnecessary overhead. If you follow the model I described above, you only need a few instructions in your ISR (typically 3-5 for a simple "read a value and put it somewhere"), and it would cost more than that just to get into a high-level Forth routine. Much easier to just do the bare minimum at interrupt time and let the task responsible for the interrupting device do the high level processing.

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

If you defer the work to a task, you have the extra overhead of context switches, which are generally more expensive than saving state in the ISR. If you can do all the work in the ISR, or at least do all the work in the majority of cases, this actually costs less overall.

Reply to
Arlet Ottens

Adding native Forth ISRs (Interrupt Service Routine) to a threaded code Forth is an exercise in futility. If you want to write an ISR in Forth you need speed. If you need speed, you need an optimising native code compiler. These are provided by Forth vendors such as MPE and Forth Inc.

Speaking for MPE, I haven't written an ARM or Cortex ISR in anything but high-level Forth for over ten years. The VFX code generator does a very good job.

The downside of such tools is that they are neither Open Source nor free (beer). However, the MPE ARM/Cortex Stamp edition compiler for ARM and Cortex is not expensive GBP 75, EU 87, USD 124 and supports LPC17xx directly. There's a limit of 120kb Flash and

64kb RAM.

Stephen

--
Stephen Pelc, stephenXXX@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads
Reply to
Stephen Pelc

Although I agree with much of what you say in theory, I'm going to disagree with you here in relationship to modern CPUs such as ARM and Cortex.

When you have a really good Forth compiler, the performance enabled by it permits us to write more complex interrupt handlers in high level Forth. The example I always use was a four channel 9600 baud bit-banged serial driver fitted to a vending machine. It ran from a

38400 Hz timer interrupt and had a worst case interrupt duration (measured) of 1.5us on a 60MHz LPC2148. On a Cortex LPC17xx, it would be faster still.

A really good Forth compiler is enabling technology.

Stephen

--
Stephen Pelc, stephenXXX@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads
Reply to
Stephen Pelc

formatting link

Isn't this overly pessimistic? Compared to interrupting an arbitrary program (and provided there is one program space) you need to save two less registers, containing the return stack pointer and the data stack pointer. Instead, use them, which saves initialisation. They must balance out on return. This assumes that the Forth uses its stacks properly, i.e. no data is stored outside of the area "protected" by the data stack pointer. 1) For the rest the usual caveat's for interrupts apply: save all registers that you use. By the way, in some processors (z80) you had no choice, interrupts just use the z80-stack, which Forth uses undoubtedly either as data or return stack.

So what you need to do in the end is store the code start address of a Forth word that is executed upon interrupt in the interrupt table. This is probably a code word that save registers. You also need a code word that restores and does a return from interrupt. The interrupt handler better end with a call to that word.

If the above facilities are not in the Forth already, you can't get around writing assembler. Then you might as well take this approach.

1) You have to take that into account anyway, whenever interrupts are used.

--

--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
Reply to
Albert van der Horst

Thanks for all replies. I have rather small experience with writing Forth programs, but quite good in writing Linux drivers in C. Usually the interrupt is not handled in context of particular task. It simply uses the protected mode stack of task which was runing when interrupt occured. Could it be done in similar way in Forth? When interrupt occurs in the assembly language we simply switch interrupts off (as the hardware source of interrupt may be still active) and set a global flag informing that the interrupt is pending, thets all. Then the inner Forth interpreter after completion of the current word(?) should execute the Forth word responsible for servicing the interrupts. This word would use the stacks of the current, random task. If interrupt is associated with data transfer, then tha data should be read/written to/from a global variable (or could it be a task specific variable, if particular task registers the interrupt routine?). The question is if I can assume, that the latency associated with waiting to the end of execution of current word will be sufficiently small?

If the above approach is correct, then interrupt handling in Riscy Pygness should be associated with:

  1. Adding of simple "universal" interrupt handle and "inInterrupt" flag.
  2. Quite a small modification of nxTab routine - if the inInterrupt flag is set, before loading of the new word: ldrh W, [IPTR], #2 ; read unsigned half-word then bump IPTR by 2 We should store the old IPTR value (on return stack?), load the IPTR with the address of our high level interrupt routine and clear the inInterrupt flag. The high level interrupt routine should inactivate the hardware interrupt source (e.g. read the data from the serial buffer) and enable interupts (if we allow nested interrupts, if not - exiting of high level interrupt routine should reenable interrupts). At the end of high level interrupt routine the previous value of IPTR should be restored from the return stack (so the interrupt servicing word should have the bit 0 ("jump flag") cleared ).

Is the above described implementation reasonable, or have I missed something?

-- TIA & Regards, Wojtek

Reply to
wzab

To digress slightly: You would get better distortion tolerance (symmetrical 33%) using 28800Hz interrupt. The 4*sampling gives asymmetrical (25-50% and 50-75% on the delay after the start pulse.

28800 vs 38400 interrupts per second also reduces overhead.
Reply to
Rocky

Yes, that's essentially how we do it, except that some processors (particularly low-end embedded microcontrollers) don't have a "protected mode".

If it's more than a single machine instruction, the latency is longer than the approach I'm advocating. Obviously, it depends on the word being executed, which could be anywhere from a microsecond to much, much longer. In most real-time applications, you can't afford that level of uncertainty for the time-critical part of an interrupt handler.

Our approach separates the time-critical response from the higher-level processing, which can certainly take place in high-level Forth, at the task level.

Consider the data acquisition situation. An interrupt signals the presence of a value on the A/D. The interrupt code reads the value, stores it in a buffer the application has provided, and sets up the next conversion. It then sets the flag that enables the task responsible for the data to wake up and process the value. This needn't take more than a very few instructions, needs no stacks, and (depending on the processor) may not even need any registers.

The actual logic of processing the data may be anywhere from simple to complex. If the data is coming in faster than the task can process it, it can go into a buffer. That adds management of buffer pointers to the ISR, but that isn't hard, either.

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

In the first place, the interrupt routine needs to save *only* the registers that it uses, which may be as few as none, depending on the processor and what you have to do. And I agree that you need to leave the processor exactly as you found it.

But you're missing the point that to run high-level Forth you need more things than two stack pointers. There are other registers, user variables, and other resources. This makes instantiating a Forth environment costlier than simply branching to a few machine instructions.

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

Not necessarily. In the system I am currently working on, breaking at ramdom many times puts me in the kernel code, in the middle of switching staks between tasks.

Thta's always a cleaner solution.

-- Roberto Waltman

[ Please reply to the group, return address is invalid ]
Reply to
Roberto Waltman

The thing about this discussion that bothers me is talking about interrupts in the abstract. But in my experience how you handle interrupts depends entirely on what is causing the interrupt. There is a huge difference between an interrupt indicating the user pressed a button on a panel versus an interrupt coming in from an Ethernet controller receiving packets at full speed. And there is a huge difference between a RTC interrupt ticking away every second versus driving a matrix of LEDs that you're pulse-width modulating brightness at 1kHz. Sometimes an interrupt comes in from human-speed events and those are so slow you don't care if it takes a few milliseconds here or there. Other times not responding to an interrupt fast enough means you're not going to be transferring data fast enough on a channel or that small perturbations in your timing will be easily detected by human senses.

So I think we need to factor at least three things into this discussion:

  1. How quickly you need to react to an interrupt.
  2. How many cycles your interrupt service routine will take.
  3. The rate of those interrupts.

Yeah, the interrupt mechanism is exactly the same, but the question of overhead starts to matter more.

Personally, I do tend to code my ISRs in a high-level language. But I take a "trust but verify" approach and look at the quality of the code generated. And on critical interrupts, you know I'm going to be instrumenting my code and hooking up a 'scope to measure exactly how much time (and what percentage of time that is from the larger application) is taken. These things matter, and regardless of how one chooses to implement an ISR, you better have a very good understanding of the overheads, resources used, and other limitations.

Reply to
John Passaniti

In my experience, there's usually some component of the response that's time-critical and some that isn't. In the case of a clock tick, that event has to be registered immediately, but there isn't any further processing to occur. In the case of receipt of an interrupt that signals the completion of a process that some task has been waiting for (e.g. a disk read) notifying the task is all that has to be done, and

*all* the subsequent activity is at the task level. My objective is to handle the time-critical steps, whatever they are, *without delay*, which means without scheduling a task or instantiating a context for high-level Forth (and I agree with Stephen that trying to do that for an ITC Forth makes no sense at all). There is no overhead involved in entering an ISR in our systems.

In a multitasking Forth, context switches are occurring, but (at least in the implementations I'm familiar with) they are vastly less costly than in more conventional multi-tasked executives: typically on the order of half-dozen machine instructions to activate a task and fewer to de-activate one.

Cheers, Elizabeth

--
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================
Reply to
Elizabeth D Rather

Well, so this is an approach which is used in interrupt handlers e.g. in Linux - servicing of interrupt is split between the ISR which runs as fast as possible and handles most critical actions related to interrupt (e.g. copying data from hardware register, where they could be overwriten by the next received data, to the buffer), and the deferred interrupt routine ("bottom half", "tasklet", "workqueue" whatever implementation will be used).

What I wanted to achieve was to give my students a way to experiment with LPC1769 based system, including interrupts, without continuous reflashing of CPU. The Riscy Pygness Forth seems to be an ideal solution, however I need a way to handle interrupts completely on the Forth level. Additionally I'd like to hide as few details of interrupt handling as possible. Therefore approach with blocking of interrupts in ISR and passing control to the high level Forth word (which may be defined by user in interactive session) seemed very good. As this will be used mainly for didactical tasks, reasonable decrease of performance may be accepted.

-- Regards, Wojtek

Reply to
wzab

r
e

I have to say I am lost. The description you give above only describes the division between the ISR and the non-ISR code for dealing with an ADC. I don't see anything that relates to the issue of implementing the ISR in Forth.

Such a simple ISR can use the stack of what ever process it interrupted, as you say, as long as it doesn't need to store anything on the stack. I expect that is a given and the ISR would use static variables for such items.

You keep referring to the "overhead" of setting up a Forth environment. I guess I don't have the insight into a complex Forth that would work that way. I am picturing a very simple Forth that has a single task of main line code and interrupts. I don't know what would be required for multitasking other than multiple stacks. I'm not sure of the capabilities of Riscy Pygness.

Do you really think this is such an absolute that ISR in Forth is universally a bad idea?

Rick

Reply to
rickman

ay

.
s

Ah, now I get it. You are thinking in terms of the FULL Forth environment. That is not needed in an ISR. Typically and ISR for an embedded application only needs to do some basic memory access and IO. If you are talking about a UNIX system, then maybe more is required. But for the OP, clearly only a subset is needed that does not require that level of overhead.

Rick

Reply to
rickman

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.