user traps
user traps
It's not clear if it is possible to assign a procedure to a user-defined trap number.
I tried to do something like:
PROCEDURE MyTrapper;
BEGIN
Out.String("MY TRAPPER"); Out.Ln
END MyTrapper;
...
Traps.ShowRegs(FALSE); (* simplified trap log *)
Traps.Assign(100, MyTrapper);
ASSERT(FALSE, 100);
...
but I just get the standard trap error log:
Trap @080045C4H in Test, Line: 218, Code: 100
my procedure seems not invoked at all
I tried to do something like:
PROCEDURE MyTrapper;
BEGIN
Out.String("MY TRAPPER"); Out.Ln
END MyTrapper;
...
Traps.ShowRegs(FALSE); (* simplified trap log *)
Traps.Assign(100, MyTrapper);
ASSERT(FALSE, 100);
...
but I just get the standard trap error log:
Trap @080045C4H in Test, Line: 218, Code: 100
my procedure seems not invoked at all
-
- Site Admin
- Posts: 525
- Joined: Fri Dec 31, 2010 12:30 pm
- Contact:
Re: user traps
ASSERT generates the ARM SVC instruction which is a software interrupt. The interrupt handler that is invoked is the one that is assigned to the address of the SVC interrupt handler (Traps.SVCVector). In a standard Astrobe installation the interrupt handler is the procedure Traps.SVCTrap. The assertion code is just displayed as a number in the error message.
You could use the assertion code to signify that the trap handler should do something different by modifying the source code of Traps.SVCTrap.
For example (* Warning: none of this has been compiled or tested *) in Traps you could declare an array of procedures:
and a procedure
and modify Traps.SVCTrap to call your function instead of executing the normal trap code:
Don't forget to initialise the array in Traps.Init:
Then in your own code you would write:
Thank you for prompting this idea. I like it and will consider implementing it in the standard Astrobe distribution ...
You could use the assertion code to signify that the trap handler should do something different by modifying the source code of Traps.SVCTrap.
For example (* Warning: none of this has been compiled or tested *) in Traps you could declare an array of procedures:
Code: Select all
VAR
userProcs: ARRAY 155 OF InterruptHandler;
and a procedure
Code: Select all
PROCEDURE AssignSVC*(code: INTEGER; p: InterruptHandler);
BEGIN
IF code >= 100 THEN userProcs[code - 100] := p END
END AssignSVC;
Code: Select all
PROCEDURE SVCTrap;
...
SYSTEM.GET(trapAddr - 4, instr);
code := LSR(instr, 16) MOD 100H;
(* modifications start here: *)
procNo := code - 100;
IF (procNo >= 0) & (userProcs[procNo] # NIL) THEN
userProcs[procNo]
ELSE ...
Code: Select all
FOR i := 0 TO LEN(userProcs) - 1 DO userProcs[i] := NIL END;
Code: Select all
Traps.AssignSVC(100, MyTrapper);
-
- Site Admin
- Posts: 525
- Joined: Fri Dec 31, 2010 12:30 pm
- Contact:
Re: user traps
v7.0 of Astrobe for Cortex-M now supports user-customisable trap handlers. See the attached source code example of its use. The output from the example is:
- Attachments
-
- UserTraps.zip
- User trap handler example
- (669 Bytes) Downloaded 2257 times
Re: user traps
I want to use the SVC call/mechanism not only for error handling, but also executing code that is safe from interference by interrupt service routines (ISR), eg. to avoid race conditions. Thusly executed code must be small and fast for the least possible interference with the incoming interrupts. As Traps.mod is focused on error handling, there's a lot going on before a user trap handler is even called, including saving the regs, and collecting all the error info, which is not ideal for what I have in mind. However, I don't want to lose the error handling in Traps.mod by simply assigning my own handler to the SVC vector, so I adapted Traps.mod as outlined below.
Now I can call
to execute 'myproc' in handler mode with minimal overhead and still get the standard error handling.
Thoughts? Something I have overlooked?
PS: FWIW, my use case is a simple task scheduler using a ticker counter on the 'SysTick' interrupt, the value of which the scheduler loop needs to read out and reset.
PPS: I guess I also could swap out and then back in the SVC handler on the fly, if I wanted to avoid changes to a library module, which I am always reluctant to do for future compatibility; haven't tried that yet.
Code: Select all
CONST
...
SVC_Instruction = 0DF00H;
VAR
...
svcHandler: InterruptHandler;
PROCEDURE SVCall*(h: InterruptHandler);
BEGIN
svcHandler := h;
SYSTEM.EMIT(SVC_Instruction)
END SVCall;
PROCEDURE SVCTrap;
VAR
id: ID;
regNo, addr, procNo: INTEGER;
BEGIN
IF svcHandler # NIL THEN
svcHandler;
svcHandler := NIL
ELSE
SaveRegs();
SYSTEM.GET(SYSTEM.SP + 128, id.addr);
SYSTEM.GET(id.addr - 4, id.instr);
...
END SVCTrap;
PROCEDURE Init*;
VAR
i, addr: INTEGER;
BEGIN
...
svcHandler := NIL
END Init;
Code: Select all
Traps.SVCall(myproc)
Thoughts? Something I have overlooked?
PS: FWIW, my use case is a simple task scheduler using a ticker counter on the 'SysTick' interrupt, the value of which the scheduler loop needs to read out and reset.
PPS: I guess I also could swap out and then back in the SVC handler on the fly, if I wanted to avoid changes to a library module, which I am always reluctant to do for future compatibility; haven't tried that yet.
Re: user traps
Here's a version that works without changing Traps.mod, using the vector swapping technique (relevant excerpt from my Kernel.mod module, leaving out the SysTick business).
Caveat: I haven't done any in depth testing yet, but it works in my test set-up: the tasks get scheduled (SVCall works), and they can ASSERT errors (standard Traps work, ie. I am back to the unchanged Traps.mod here).
Code: Select all
CONST
...
SVC_Instruction = 0DF00H;
SVC_Vector = 02CH;
VAR
...
sysHandler: PROCEDURE;
svcTrapAddr: INTEGER;
PROCEDURE svcHandler;
BEGIN
ASSERT(sysHandler # NIL, 100);
sysHandler
END svcHandler;
PROCEDURE SVCall*(h: PROCEDURE);
VAR
currentHandler: PROCEDURE;
BEGIN
sysHandler := h;
SYSTEM.GET(svcTrapAddr, currentHandler);
Traps.Assign(svcTrapAddr, svcHandler);
SYSTEM.EMIT(SVC_Instruction);
SYSTEM.PUT(svcTrapAddr, currentHandler)
END SVCall;
PROCEDURE initSVC;
VAR
trapAddrBase: INTEGER;
BEGIN
sysHandler := NIL;
SYSTEM.GET(MCU.VTOR, trapAddrBase);
svcTrapAddr := trapAddrBase + SVC_Vector;
END initSVC;
BEGIN
initSVC
END ...
-
- Site Admin
- Posts: 525
- Joined: Fri Dec 31, 2010 12:30 pm
- Contact:
Re: user traps
A couple of things I'm uneasy about that I think you should consider:
1. What are the consequences when the ASSERT in svcHandler fails? It might be sufficient just to skip the call to sysHandler if it is NIL.
2. What happens if sysHandler causes an exception?
1. What are the consequences when the ASSERT in svcHandler fails? It might be sufficient just to skip the call to sysHandler if it is NIL.
2. What happens if sysHandler causes an exception?
Re: user traps
ad 1.) true, that's a bad idea. It would result in a Hard fault. The ASSERT needs to be in SVCall:
ad 2.) as sysHandler runs as exception handler, it will result in a Hard fault. But that's true for UserHandlers set via Traps.mod as well, isn't it, as they also run as exception handlers, being called from SVCTrap. Or do I miss something?
Exactly for this reason, exception/interrupt handlers are tricky to write in general. I get uneasy, if they are longer than a few lines...
Code: Select all
PROCEDURE SVCall*(h: PROCEDURE);
VAR
currentHandler: PROCEDURE;
BEGIN
ASSERT(h # NIL, 100);
sysHandler := h;
...
Exactly for this reason, exception/interrupt handlers are tricky to write in general. I get uneasy, if they are longer than a few lines...
-
- Site Admin
- Posts: 525
- Joined: Fri Dec 31, 2010 12:30 pm
- Contact:
Re: user traps
Yes - you are right. The same thing occurred to me after I had posted my reply!gray wrote:ad 2.) as sysHandler runs as exception handler, it will result in a Hard fault. But that's true for UserHandlers set via Traps.mod as well, isn't it, as they also run as exception handlers, being called from SVCTrap. Or do I miss something?
Back to your original question, you can probably reorganise the Traps code for user handlers so that it skips the code that saves registers, gets the module name etc. when making one of your SVC calls. You could do what ASSERT does and include a trap number with your SVC call so Traps can know whether it's an error trap or otherwise.