• Is it possible to extend setWatch to include timer interrupts?

    The scenario:
    One counter is being used to count external pulses and is being gated by a second timer in one time pulse mode (OTP). The program starts the one time pulse. If setWatch were able to detect the finished interrupt from the OTP then the program would know that the measured count is available to read and process.

  • ...sacrificing 2 pins... one output based on the timer event connected to the other one with input and setWatch... but it would be nice setWatch can determine whether the argument is a pin or a timer...

  • Oooh... extending set watch to other interrupt sources would really open new doors...

  • I think you know this, but setWatch doesn't actually execute code on the interrupt, it just queues something to say the interrupt has happened.

    As @allObjects says, if you can get it to output to a pin, you could loop back and use setWatch that way - but I'm not sure you can.

    We could however modify Espruino to add some functions that put an event (USER1, USER2, etc?) into the queue when they are called, and could get them to call E.on('USER1', ...) when they were found. The addresses of those functions could then be poked into the Interrupt Vector Table, so that they were called when something happened...

    I kind of wonder how many people would use the functionality though ;)

  • Lots of ideas here.
    In dealing with interrupts over the years on varying platforms, I've run into something I call the nagging interrupt. The interrupt occurs and if you don't clear it before doing the return from interrupt it will continue to occur.
    When you set a watch on a pin, does a change on the pin create an interrupt? In the routine that queues the watch do you clear the interrupt bit for the pin?

    Here is an outline subject to everyone's revisions;
    SetWatch(User,function(),{IVectoraddress,IRegisteraddress,bitnum,bitval 0 or 1})
    This would save the old address at the IVectoraddress in the interrupt table,
    then insert the Watch action address into the table.

    The watch action and queue code:
    When the interrupt occurs the IResgisteraddress is used along with the bitnum and bitval to clear the interrupt. The event is queued along with the function to call.

    When ClearWatch is called the old address would be restored to the interrupt table

    As far as the external wire method between the OTpulse timer and the counting timer in gate mode, I think that works as far as the timers are concerned but I will have to see if the setWatch still works in that setup. The OTpulse pin is setup as an output. The gated timer gate would be an input. I'm a couple of steps from being able to try that in the timer thread.

  • The setWatch() accepts an optional options object that feel like what you are looking for: the repeat property of the options controls whether it is a one single shot or multiples. I would not go as far as @Gordon outlined. I would only give the timers names (may be that's what @Gordon means with user) and then let the known pattern take care of things... Like we have ports / pins named, why can we not have timers named? ...T1, T2, T3,... and then use, for example, setWatch(callbackFunction,T3,{repeat: true}); to invoke callbackFunction when T3 overflows in counting mode...

  • When you set a watch on a pin, does a change on the pin create an interrupt? In the routine that queues the watch do you clear the interrupt bit for the pin?

    Yes to both...

    Good point about clearing the interrupt flag.

    Personally I'd probably leave setWatch to be just for pins, and would have a new function for IRQs (after all, things like 'edge' and 'debounce' make little sense for IRQs).

    To be honest it all starts to get quite complicated. There are around 30 possible interrupts I think - I wonder whether it makes sense to just add built-in handlers for all the common ones which automatically clear the relevant flags.

    Then you could just do E.on('IRQ15', function() { ... })

    Just to add, if you're so inclined I think you can hack things together with Espruino as-is:

    • Use process.memory() to find the end of the stack, where there will be some RAM you can use
    • Use the Assembler to write some ARM assembly that clears the relevant flag and then toggles an IO pin
    • Manually copy that assembled code to the end of the stack as well
    • Now 'poke' the address of that code into the vector table
    • Connect two IO pins together and do a setWatch

  • Thanks for all the good ideas.

    It would be nice to be able to use the internal wires between the timers instead of an external wire. This is done by setting up one timer as a master with output TRGO and the second timer as a slave with input on the appropriate line ITR0..ITR3. The problem is telling the Javascript code when the timer process is finished so that the Javascript can peek at the results.
    From the interpreter perspective with a finite amount of memory, adding code to cover all possible conditions results in lower memory limits for user code that doesn’t use all the features. It makes sense to have the user add the code to handle the interrupt.
    I was looking at the inline assembler page.
    http://www.espruino.com/Assembler#line=195,196
    For an interrupt service routine (ISR):
    Disable interrupts
    Clear the interrupt flag
    Enable interrupts
    Queue something to let Javascript know
    Return or is it return from interrupt (as seen in some processors)
    So this is placed in spare memory as you outlined and the address written to the interrupt table.
    With the code for one such ISR as a pattern, a short function could be written
    Function codeISR(InterruptNum,registerAddr,bitnum,value)
    The function would then take the example ISR and insert the parameters and console.log the code to be used for the new ISR. These could be pasted into the user program that needs such the ISR function.

    As far as to which Javascript verb to use do E.on('IRQ15', function() { ... })
    or setWatch or some other verb I’ll leave that up to you.

    Do you have a link to obtain a copy of the assembler?
    What is the opcode for disable and enable interrupt?
    Does the ARM use a special return from interrupt? If so what is the opcode?
    How to queue the Javascript?

  • Do you have a link to obtain a copy of the assembler?

    You best bet is to upload it, then hit up-arrow to see a copy of what was uploaded. Eg.

    var adder = E.asm("int(int)",
    "movs    r1, #3",
    "adds    r0, r0, r1", // add two 32 bit values
    "bx  lr"); // return
    

    Gives var adder = E.nativeCall(1, "int(int)", atob("AyFAGHBH"))

    and you can just use atob("AyFAGHBH"):

    var data = atob("AyFAGHBH");
    var addr = process.memory().stackEndAddress-data.length;
    poke8(addr, data);
    

    What is the opcode for disable and enable interrupt?

    You don't actually have to disable interrupts - I believe the ARM does this for you (the only thing that will interrupt you is an interrupt of higher priority, which you should probably allow anyway).

    Does the ARM use a special return from interrupt? If so what is the opcode?

    It's just a simple bx lr I believe (a normal return) - the ARM instruction set is quite nice :)

    How to queue the Javascript?

    I don't think that is possible for now...

    You'd have to toggle the state of a pin, feed it back, and then use setWatch. There's info on toggling pins states under the ACCESSING IO heading on the assembler page

  • Thanks that helps. On the queue problem here's what I've been looking at based on the E.on('IRQ15', function() { ... }) that you've been suggesting.

    //an object
    function IRQ(a){
      this.A=a;
    }//end LogObj
    //create an instance
    var a=new IRQ(3);
    //display the object
    console.log(a);
    //create a listener for the object
    a.on("IRQ1",function(d){
    //ISR code goes here
    //then display the timer results
      console.log("IRQ1");
      console.log(d);
    });
    
    //Simulate the interrupt
    a.emit("IRQ1","hello");
    
    IRQ.prototype.setup = function() {
    // setup asm code in memory
    // put address in interrupt table
    };
    
    >echo(0);
    { "A": 3 }
    =undefined
    IRQ1
    hello
    
    

    So either place the call to a.emit in the assembly code or call the assembly code from the a.on() function and place the corresponding address in the interrupt table.
    How does one obtain the address of a function such as a.emit to call?

  • Calling a.emit from Assembler is something that just isn't possible at the moment - hence why you need to do the pin joining and setWatch thing.

    One issue is that JS functions don't exist as something you can just 'jump' to from Assembler, but also the JS interpreter isn't designed to be reentrant... Even allocating JS vars (for instance the string 'hello') can be a pain.

  • Thanks @Gordon. I was just thinking about calling interpreted text like it is machine code really doesn't make sense. Perhaps the best approach other than the pin joining is to have the ISR set a flag and use a setinterval() to check the flag and do the emit.

    On a different note, after typing in the asm code and doing the upload you said,
    *You best bet is to upload it, then hit up-arrow to see a copy of what was uploaded. *
    Nothing happens, no text gets returned?

  • Up arrow in the left hand side,after uploading on the right...

  • Perhaps the best approach other than the pin joining is to have the ISR set a flag and use a setinterval() to check the flag and do the emit.

    Ahh, but if you're doing that, it's almost as easy to just check the interrupt flag and reset it right from setInterval, without the interrupt?

  • Thanks @Wilberforce the up arrow in left pane more than once did the trick.

    @Gordon in a previous post suggested this example:

    var adder = E.asm("int(int)",
    "movs r1, #3",
    "adds r0, r0, r1", // add two 32 bit values
    "bx lr"); // return
    
    

    Gives var adder = E.nativeCall(1, "int(int)", atob("AyFAGHBH"))
    and you can just use atob("AyFAGHBH"):

    The "AyFAGHBH" is the 64 bit encoded version of the code.
    The atob function converts it to a flat string with escaped characters.
    The btoa function creates a 64 bit encoded string.

    Some confusion about where to poke the string.
    @Gordon suggested before the stack end address

    var data = atob("AyFAGHBH");
    var addr = process.memory().stackEndAddress-data.length;
    poke8(addr
    
    

    The assembler page suggests after the stack end address.
    http://www.espruino.com/Assembler

    var ASM_BASE=process.memory().stackEndAddress;
    var ASM_BASE1=ASM_BASE+1/*thumb*/;
    [0x4a02,0xf44f,0x4360,0x6013,0x6053,0x4770,0x0810,0x4001].forEach(function(v) { poke16((ASM_BASE+=2)-2,v); }); 
    var pulse = E.nativeCall(ASM_BASE1, "void()")
    
    

    So I’ve tried it both ways and both crash.

    var data = atob("AyFAGHBH");
    //var addr = process.memory().stackEndAddress-data.length;
    var addr = process.memory().stackEndAddress+1;
    for(var i=0;i<data.length;i++){
    poke8(addr+i,data.charCodeAt(i));
    console.log((addr+i).toString(16)+","+peek8(addr+i).toString(16));
    }
    
    var adder = E.nativeCall(1, "int(int)", atob("AyFAGHBH"));
    //var adder= E.nativeCall(addr, "int(int)");
    
    console.log(adder(2));
    
    

    Which displays

    >echo(0);
    20009b3d,3
    20009b3e,21
    20009b3f,40
    20009b40,18
    20009b41,70
    20009b42,47
    5
    =undefined
    >
    
    
  • Found it
    put code at addr, call at addr+1 for thumb code.

    var data = atob("AyFAGHBH");
    //var addr = process.memory().stackEndAddress-data.length;
    var addr = process.memory().stackEndAddress;
    for(var i=0;i<data.length;i++){
    poke8(addr+i,data.charCodeAt(i));
    console.log((addr+i).toString(16)+","+peek8(addr+i).toString(16));
    }
    
    addr++;
    //var adder = E.nativeCall(1, "int(int)", atob("AyFAGHBH"));
    var adder= E.nativeCall(addr, "int(int)");
    
    console.log(adder(2));
    
    

    Outputs

    >echo(0);
    20009b3c,3
    20009b3d,21
    20009b3e,40
    20009b3f,18
    20009b40,70
    20009b41,47
    5
    =undefined
    > 
    
  • Here is a link to help with Thumb code
    Introduction to ARM Thumb
    https://www.cs.princeton.edu/courses/archive/fall12/cos375/ARMthumb.pdf

  • One last try.
    I was looking at the Espruino compiler page:
    http://www.espruino.com/Compilation

    If a javascript function is compiled, is it possible to call it from assembled code?

  • Yes you can!
    A simple compiled JavaScript function:

    function foo() {
      "compiled";
      console.log("hello world");
      return 0;
    }
    
    foo();
    
    /* Using up arrow several times in left pane one finds the code below
    var foo=E.nativeCall(1,'JsVar()',atob('Len/QR1IGUt4RJhHBK5G+AQNB0YaSEzy/WN4RJhHFEuARphHBUYTTEBGoEcVSShGeUQAIkz2eTOYRwAhASMqRo3oSAALRk3yDVaARrBHBkZARqBHKEagRzhGoEcwRqBHBUsAIJhHBLC96PCBsbACAHU9AQChqwIAhS8BAHwAAAB2AAAAaAAAAGhlbGxvIHdvcmxkAGNvbnNvbGUAbG9nAA=='));
    */
    

    Run the program and use the up arrow in the left pane a couple of times to get the native call string.
    Using that string one can load it as thumb code in spare memory and call it.

    fooHelloThmbcode.js

    //used foohello.js to get the string
    /* Using up arrow several times in left pane one finds the code below
    var foo=E.nativeCall(1,'JsVar()',atob('Len/QR1IGUt4RJhHBK5G+AQNB0YaSEzy/WN4RJhHFEuARphHBUYTTEBGoEcVSShGeUQAIkz2eTOYRwAhASMqRo3oSAALRk3yDVaARrBHBkZARqBHKEagRzhGoEcwRqBHBUsAIJhHBLC96PCBsbACAHU9AQChqwIAhS8BAHwAAAB2AAAAaAAAAGhlbGxvIHdvcmxkAGNvbnNvbGUAbG9nAA=='));
    */
    
    
    var data = atob('Len/QR1IGUt4RJhHBK5G+AQNB0YaSEzy/WN4RJhHFEuARphHBUYTTEBGoEcVSShGeUQAIkz2eTOYRwAhASMqRo3oSAALRk3yDVaARrBHBkZARqBHKEagRzhGoEcwRqBHBUsAIJhHBLC96PCBsbACAHU9AQChqwIAhS8BAHwAAAB2AAAAaAAAAGhlbGxvIHdvcmxkAGNvbnNvbGUAbG9nAA==');
    
    
    //var addr = process.memory().stackEndAddress-data.length;
    var addr = process.memory().stackEndAddress;
    for(var i=0;i<data.length;i++){
    poke8(addr+i,data.charCodeAt(i));
    console.log((addr+i).toString(16)+","+peek8(addr+i).toString(16));
    }
    
    addr++;
    
    var foo= E.nativeCall(addr, 'JsVar()');
    
    foo();
    foo();
    

    and the output

    20009bda,67
    20009bdb,0
    hello world
    =undefined
    >echo(1)
    =undefined
    >foo();
    hello world
    =0
    >foo();
    hello world
    =0
    >foo();
    hello world
    =0
    >foo();
    hello world
    =0
    > 
    

    2 Attachments

  • Great! Yes, sorry, I forgot about that.

    On ARM, the bottom bit of the address determines whether you're jumping to ARM 16 bit 'thumb' assembler, or normal non-thumb 32 bit code. Unfortunately the Cortex Ms used for Espruino don't support normal ARM instructions, so you have to set the bottom bit to 1 (while keeping the instructions aligned to 2 byte boundaries)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Is it possible to extend setWatch to include timer interrupts?

Posted by Avatar for ClearMemory041063 @ClearMemory041063

Actions