Abzac / python-on-a-chip

Automatically exported from code.google.com/p/python-on-a-chip
Other
0 stars 0 forks source link

Fix frozen threads during ipm #192

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
So, I just tried to run this code within ipm::

ipm> import mbed, sys
ipm> def blink():
....   while True:
....     mbed.set_led(1,1)
....     sys.wait(500)
....     mbed.set_led(1,0)
....     sys.wait(500)
.... 
ipm> sys.runInThread(blink)

And it didn't work as I'd expect.  I was hoping the led would blink away while 
I continued playing in ipm. But instead, the led toggled only when I entered a 
new command at the prompt and hit enter.

The reason for this is because when ipm is running, it calls a blocking native 
function to fetch new input from the host over the serial port.  So the VM does 
not continue running background threads during ipm.

Original issue reported on code.google.com by dwhall...@gmail.com on 8 Mar 2011 at 4:39

GoogleCodeExporter commented 9 years ago

Original comment by dwhall...@gmail.com on 8 Mar 2011 at 4:40

GoogleCodeExporter commented 9 years ago
Here is an easy fix to get this working (on mbed):

Add a function plat_checkSerial() to plat.cpp and the prototype in 
plat_interface.h

uint8_t
plat_checkSerial(void)
{
    return (uint8_t) serial.readable();
}

Add a function to check for serial data in ipm.py:

#
# check to see if a byte is ready from the platform's default I/O
# Returns 0 if no byte 1 if byte is waiting.
#
def _chks():
    """__NATIVE__
    uint8_t b;
    pPmObj_t pb;
    PmReturn_t retval = PM_RET_OK;

    /* If wrong number of args, raise TypeError */
    if (NATIVE_GET_NUM_ARGS() != 0)
    {
        PM_RAISE(retval, PM_RET_EX_TYPE);
        return retval;
    }

    b = plat_checkSerial();
    PM_RETURN_IF_ERROR(retval);

    retval = int_new((int32_t)b, &pb);
    NATIVE_SET_TOS(pb);
    return retval;
    """
    pass

Change the while loop in ipm.py to check for serial data before trying to 
download a code object:

    while 1:
    # Check to see if the serial port has data
    if _chks():
        # Read code image, make a code object from it
        # and evaluate the code object.
        # #180: One-liner turned into 3 so that objects get bound to roots
        s = _getImg()
        co = Co(s)
        rv = eval(co, g)
        x04()

Original comment by j...@missioncognition.net on 8 May 2011 at 8:46

GoogleCodeExporter commented 9 years ago
Sadly the fix I posted does not seem to work reliably.  The threads and ipm 
eventually hang. I guess that either checks are needed before every serial read 
or bytes are being dropped and its not being handled gracefully.  So that would 
mean that either _getImg() needs to be implemented in Python to allow task 
switching, _getImg() needs to be passed a working buffer and return a bool 
indicating a complete transfer, serial reading has to cooperate with the python 
interpreter when blocked, or the transfer protocol needs beefing up.  

Original comment by j...@missioncognition.net on 15 May 2011 at 9:06

GoogleCodeExporter commented 9 years ago
I suspect we'll have to use fcntl()  with the O_NONBLOCK parameter on the posix 
platform to get a non-blocking getchar() and create a 
plat_getByteNonBlocking().  Then the caller of _getImg() will have to be 
rewritten a little to handle the return-with-no-image situation.

For microcontroller platforms, it should be easy enough to read a control 
register flag to detect the presence of a byte in the UART data register (or 
the number of bytes in a buffer, if the I/O is buffered) in order to make 
plat_getByteNonBlocking().

FYI: for v10, I'm planning on passing lightly-wrapped marshalled data between 
host and target.  Marshalled code images from the host to the target; and a 
marshal of whatever the return object is from the target to the host.  The 
"lightly wrapped" is probably a header with size info and a tail with an error 
detection value.

Original comment by dwhall...@gmail.com on 16 May 2011 at 3:26

GoogleCodeExporter commented 9 years ago
Sounds like a good approach.  

One of the things I'm hoping is that it will be possible to use ipm as a 
monitor console for a user program once this gets working.  In the days before 
JTAG was common and you had to use an ICE for any debugging (very expensive) it 
was not uncommon for the projects I worked on to have a serial console 
programmed with a pretty minimal inspection/monitor command set.  Usually you 
could get a dump of various key system variables and set some control 
parameters while the system was running, and sometimes even do some rudimentary 
debugging. Everything had to be pre-determined and programmed in.  With IPM you 
could just work on the python console and access things directly assuming an 
appropriate mechanism could be created to keep things thread-safe.  

Since you are working on changing the serial protocol let me suggest that you 
consider setting it up in such a way as to let the user program gain access to 
the console serial port as well.  Maybe the user program could register a 
message handler that is called if the message type was not designated as a 
python code transfer to ipm.  That way monitor features that don't lend 
themselves well to the python console could bypass it or even work in parallel. 
 And perhaps more importantly you don't have to disable ipm in order to use 
that serial port for other things (so long as you use the same message 
formatting).  For example a GUI (or another terminal) could show 
telemetry/status values updated in real time while you still interact on the 
python console.

Original comment by j...@missioncognition.net on 23 May 2011 at 7:33