I am using MicroPython,
Code: Select all
>>> os.uname()
(sysname='pyboard', nodename='pyboard', release='1.11.0', version='v1.11 on 2019-05-29', machine='PYBv1.1 with STM32F405RG')
Code: Select all
import pyb, micropython
import _thread
micropython.alloc_emergency_exception_buf(100)
class Foo():
def __init__(self):
self.lock = _thread.allocate_lock()
self.bar_ref = self.bar # Allocation occurs here
tim = pyb.Timer(4)
tim.init(freq=1)
tim.callback(self.cb)
def _add(self, item):
# accessed by 'REPL' and via Timer callback thru micropython.schedule()
with self.lock:
i = 0
while i < 10000: # the longer this is the quicker fault occurs
i += 1
def bar(self, _): # internally called by the scheduler via the Timer, this is not ISR context
self._add(23)
def cb(self, t):
# Passing self.bar would cause allocation.
micropython.schedule(self.bar_ref, 0)
def ret(self, method=None, all=False): # externally called by the PC via REPL
print("something {}".format(method))
self._add(method)
return True
foo = Foo()
Code: Select all
from time import sleep
import ampy.pyboard as pyboard
pyb = pyboard.Pyboard("/dev/ttyACM0")
pyb.enter_raw_repl()
data, data_err = pyb.exec_raw("import test_05\n")
print(data, data_err)
for i in range(10000):
data, data_err = pyb.exec_raw("test_05.foo.ret(method='bar', all=False)\n", timeout=10, data_consumer=None)
print(i, data, data_err)
sleep(0.1)
My findings:
1) The use of `with self.lock:` in the function `_add()` triggers the fault. Without the lock, the fault is not triggered. Or maybe it does, but it takes a lot longer.
2) The longer the function `_add()` takes, the easier it is to create the hard fault.
As you can guess from the snippet, in the real code, I have a variable (a list) that is accessed by the Timer function and by the PC (via REPL). In the snippet above, I removed that variable because its not the issue. The snippet doesn't create or access shared variables.
I believe the snippet is following the rules outlined in the link above.
Am I doing something wrong?