How Forth handles hardware interuptions
Opened this issue · 19 comments
Hi everybody, I have a noob question. I'm working on embedded systems written in C. I'm too young to have worked with Forth in professional projects, I hardly know how to write Forth scripts but I know how to make my own Forth interpreter.
When Forth is embedded inside a micro-controller, how do you manage hardware interruptions routines (ISR) ? For example: reading the ADC value and filter the value or getting the I2C message ? In C code I'm accustomed to have an ISR table with the correct pointers on your ISR functions. When an interruption occurs your routine is called.
How in Forth this is managed ? Do you write the code of the routine directly in asm ? Or do you create a task for polling the register (ugly way) ? Or has it a way to do it directly with Forth words.
Thanks
Everything Paul said are lessons from an authority.
I had to implement a four channel optical counter as an ISR on a Forth system that was running on a 68HC11. For that machine, I opted to use ASM code to minimize taking too much time as it had to run every 4mS on a timer interrupt.
I also used a old Forth system for DOS that provided the words INT: ;INT which allowed you to create hi-level Forth words that had the appropriate code to enter and exit as an ISR, when called by an interrupt. (this extra code was Assembler)
You could then install the address of the INT: word into the system's interrupt table and it would run seamlessly. This to me seems like a nice Forth solution... extend the compiler to make the job simple.
STM8 eForth takes this approach:
- interrupt routines must run a context-switch word that stores the essential machine state
- character I/O words must not be used (no 2nd hold, pat)
- the interrupt routine must run a context-restore word
- the XT of the interrupt routine should be copied to the µCs interrupt vector
After the context switch, the interrupt routine uses a small private data stack which doesn't need to be balanced.
In STM8 eForth there is also a preemptive background task which can use character I/O, and there is a console idle task (which should be careful with character I/O).
@Lecrapouille I still consider myself a Forth newbie, but I hold it as a fact that whenever Forthers meet they feel compelled to not only discuss the problem but also the advantage of "their Forth", or their aptness to compare any Forth. I love it.
The discussion above discus Forth implementations that use an Inner Interpreter (ITC, DTC...) for switching to the ISR. Here it's generally simple (but as @MitchBradley points out sometimes not). The Inner Interpreter (NEXT
) injects your ISR into the execution flow, and that's it. Your worst case latency is basically the time it takes to execute the longest primitive word (e.g. /
).
Some other Forth (STC) compile to machine code, and don't have an inner interpreter proper. There, one usually relies on the ISR capabilities of a µC (just like in C), and one has to perform a context switch (the µCs interrupt feature often does the bulk of it).
All the implementation approaches certainly have their pros and cons, and that's why it's fun ;-)
@Mecrisp Matthias, this makes me curious - how do you deal with the data stack pointer?
Thanks for all your answers that helped me understand better and sorry for not having replied sooner !
@TG9541 yes it does not exist a single Forth standard language but a several Forth: one for each implementor. This is something cool for competitions for innovation but gives hard job for beginners :)
@GarthWilson nice article I'm too novice to understand everything.
-
2/ whatever the language can be ISR code shall be short to let the system be reactive. This is all the difficulty of writing reactive embedded code :/
-
1/ yes This is something I would missed. Code like
' isr >r
was something I was expected to read even with carefulness noticed by MitchBradley. Preserving the neutrality of the RStack like making Forth definitions where the number of>R
and<R
words shall be balanced (else the inner interpreter will get crash).
May I digress the conversion a little but that makes me remind something that I learned but not so much documented for beginners: when you make a word definition you also have to let the data stack neutral. What does "neutral" really mean ? Ok for not touching at the stack depth ! What about the content of the data stack? for example the following code:
: foo [ 3 ] ;
is refused (because of changing the depth). This one accepted by pforth but not by gforth:
: foo [ literal 3 ] ;
(ok this is a bad example because literal is added during compilation and forbidden at interpretation but could be an alias for pushing 3). And what about this one:
1 2
: foo [ swap ] ;
only accepted by pforth: the data stack will be 2 1. So I guess that for ISR similar security has to done.
@MitchBradley saving the IP: I never liked Forth for letting the developer storing datum inside the R stack. I know this is so crucial like defining words DO LOOP or nice hacks by injecting CFA, as well as for helping swapping data stack elements by saving memory with a 2nd data stack and that was something important in 70's but nowadays why not having a 3thd (auxiliary) stack and making the return stack fully private. This looks safer (I may be wrong but it seems that what 4th does).
@GarthWilson thanks for your answer! I infer that the assumption that a controller register always contains a valid data stack pointer is most often true :-)