mirror of https://github.com/Desuuuu/klipper.git
avrsim: Do IO directly from simulavr callbacks
Don't start/stop the simulavr simulation loop to do IO - instead implement the IO directly in the serial callback handlers. This improves the speed and consistency of the simulation. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
1fbb36fa87
commit
5f29787dc7
|
@ -5,21 +5,22 @@
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
import sys, optparse, os, pty, select, fcntl, termios, traceback, errno
|
import sys, optparse, os, pty, fcntl, termios, errno
|
||||||
import pysimulavr
|
import pysimulavr
|
||||||
|
|
||||||
SERIALBITS = 10 # 8N1 = 1 start, 8 data, 1 stop
|
SERIALBITS = 10 # 8N1 = 1 start, 8 data, 1 stop
|
||||||
|
SIMULAVR_FREQ = 10**9
|
||||||
|
|
||||||
# Class to read serial data from AVR serial transmit pin.
|
# Class to read serial data from AVR serial transmit pin.
|
||||||
class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
||||||
def __init__(self, baud):
|
def __init__(self, baud, terminal):
|
||||||
pysimulavr.Pin.__init__(self)
|
pysimulavr.Pin.__init__(self)
|
||||||
pysimulavr.PySimulationMember.__init__(self)
|
pysimulavr.PySimulationMember.__init__(self)
|
||||||
|
self.terminal = terminal
|
||||||
self.sc = pysimulavr.SystemClock.Instance()
|
self.sc = pysimulavr.SystemClock.Instance()
|
||||||
self.delay = 10**9 / baud
|
self.delay = SIMULAVR_FREQ / baud
|
||||||
self.current = 0
|
self.current = 0
|
||||||
self.pos = -1
|
self.pos = -1
|
||||||
self.queue = ""
|
|
||||||
def SetInState(self, pin):
|
def SetInState(self, pin):
|
||||||
pysimulavr.Pin.SetInState(self, pin)
|
pysimulavr.Pin.SetInState(self, pin)
|
||||||
self.state = pin.outState
|
self.state = pin.outState
|
||||||
|
@ -33,32 +34,33 @@ class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
||||||
if self.pos == 1:
|
if self.pos == 1:
|
||||||
return int(self.delay * 1.5)
|
return int(self.delay * 1.5)
|
||||||
if self.pos >= SERIALBITS:
|
if self.pos >= SERIALBITS:
|
||||||
self.queue += chr((self.current >> 1) & 0xff)
|
data = chr((self.current >> 1) & 0xff)
|
||||||
|
self.terminal.write(data)
|
||||||
self.pos = -1
|
self.pos = -1
|
||||||
self.current = 0
|
self.current = 0
|
||||||
return -1
|
return -1
|
||||||
return self.delay
|
return self.delay
|
||||||
def popChars(self):
|
|
||||||
d = self.queue
|
|
||||||
self.queue = ""
|
|
||||||
return d
|
|
||||||
|
|
||||||
# Class to send serial data to AVR serial receive pin.
|
# Class to send serial data to AVR serial receive pin.
|
||||||
class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
||||||
MAX_QUEUE = 64
|
def __init__(self, baud, terminal):
|
||||||
def __init__(self, baud):
|
|
||||||
pysimulavr.Pin.__init__(self)
|
pysimulavr.Pin.__init__(self)
|
||||||
pysimulavr.PySimulationMember.__init__(self)
|
pysimulavr.PySimulationMember.__init__(self)
|
||||||
|
self.terminal = terminal
|
||||||
self.SetPin('H')
|
self.SetPin('H')
|
||||||
self.sc = pysimulavr.SystemClock.Instance()
|
self.sc = pysimulavr.SystemClock.Instance()
|
||||||
self.delay = 10**9 / baud
|
self.delay = SIMULAVR_FREQ / baud
|
||||||
self.current = 0
|
self.current = 0
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
self.queue = ""
|
self.queue = ""
|
||||||
|
self.sc.Add(self)
|
||||||
def DoStep(self, trueHwStep):
|
def DoStep(self, trueHwStep):
|
||||||
if not self.pos:
|
if not self.pos:
|
||||||
if not self.queue:
|
if not self.queue:
|
||||||
return -1
|
data = self.terminal.read()
|
||||||
|
if not data:
|
||||||
|
return self.delay * 100
|
||||||
|
self.queue += data
|
||||||
self.current = (ord(self.queue[0]) << 1) | 0x200
|
self.current = (ord(self.queue[0]) << 1) | 0x200
|
||||||
self.queue = self.queue[1:]
|
self.queue = self.queue[1:]
|
||||||
newstate = 'L'
|
newstate = 'L'
|
||||||
|
@ -69,15 +71,6 @@ class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin):
|
||||||
if self.pos >= SERIALBITS:
|
if self.pos >= SERIALBITS:
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
return self.delay
|
return self.delay
|
||||||
def needChars(self):
|
|
||||||
if len(self.queue) > self.MAX_QUEUE / 2:
|
|
||||||
return 0
|
|
||||||
return self.MAX_QUEUE - len(self.queue)
|
|
||||||
def pushChars(self, c):
|
|
||||||
queueEmpty = not self.queue
|
|
||||||
self.queue += c
|
|
||||||
if queueEmpty:
|
|
||||||
self.sc.Add(self)
|
|
||||||
|
|
||||||
# Support for creating VCD trace files
|
# Support for creating VCD trace files
|
||||||
class Tracing:
|
class Tracing:
|
||||||
|
@ -108,6 +101,22 @@ class Tracing:
|
||||||
if self.dman is not None:
|
if self.dman is not None:
|
||||||
self.dman.stopApplication()
|
self.dman.stopApplication()
|
||||||
|
|
||||||
|
# Forward data from a terminal device to the serial port pins
|
||||||
|
class TerminalIO:
|
||||||
|
def __init__(self):
|
||||||
|
self.fd = -1
|
||||||
|
def run(self, fd):
|
||||||
|
self.fd = fd
|
||||||
|
def write(self, data):
|
||||||
|
os.write(self.fd, data)
|
||||||
|
def read(self):
|
||||||
|
try:
|
||||||
|
return os.read(self.fd, 64)
|
||||||
|
except os.error, e:
|
||||||
|
if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK):
|
||||||
|
pysimulavr.SystemClock.Instance().stop()
|
||||||
|
return ""
|
||||||
|
|
||||||
# Support for creating a pseudo-tty for emulating a serial port
|
# Support for creating a pseudo-tty for emulating a serial port
|
||||||
def create_pty(ptyname):
|
def create_pty(ptyname):
|
||||||
mfd, sfd = pty.openpty()
|
mfd, sfd = pty.openpty()
|
||||||
|
@ -154,18 +163,21 @@ def main():
|
||||||
trace = Tracing(options.tracefile, options.trace)
|
trace = Tracing(options.tracefile, options.trace)
|
||||||
dev = pysimulavr.AvrFactory.instance().makeDevice(proc)
|
dev = pysimulavr.AvrFactory.instance().makeDevice(proc)
|
||||||
dev.Load(elffile)
|
dev.Load(elffile)
|
||||||
dev.SetClockFreq(10**9 / speed)
|
dev.SetClockFreq(SIMULAVR_FREQ / speed)
|
||||||
sc.Add(dev)
|
sc.Add(dev)
|
||||||
trace.load_options()
|
trace.load_options()
|
||||||
|
|
||||||
|
# Setup terminal
|
||||||
|
io = TerminalIO()
|
||||||
|
|
||||||
# Setup rx pin
|
# Setup rx pin
|
||||||
rxpin = SerialRxPin(baud)
|
rxpin = SerialRxPin(baud, io)
|
||||||
net = pysimulavr.Net()
|
net = pysimulavr.Net()
|
||||||
net.Add(rxpin)
|
net.Add(rxpin)
|
||||||
net.Add(dev.GetPin("D1"))
|
net.Add(dev.GetPin("D1"))
|
||||||
|
|
||||||
# Setup tx pin
|
# Setup tx pin
|
||||||
txpin = SerialTxPin(baud)
|
txpin = SerialTxPin(baud, io)
|
||||||
net2 = pysimulavr.Net()
|
net2 = pysimulavr.Net()
|
||||||
net2.Add(dev.GetPin("D0"))
|
net2.Add(dev.GetPin("D0"))
|
||||||
net2.Add(txpin)
|
net2.Add(txpin)
|
||||||
|
@ -183,27 +195,9 @@ def main():
|
||||||
|
|
||||||
# Run loop
|
# Run loop
|
||||||
try:
|
try:
|
||||||
|
io.run(fd)
|
||||||
trace.start()
|
trace.start()
|
||||||
while 1:
|
sc.RunTimeRange(0x7fff0000ffff0000)
|
||||||
starttime = sc.GetCurrentTime()
|
|
||||||
r = sc.RunTimeRange(speed/1000)
|
|
||||||
endtime = sc.GetCurrentTime()
|
|
||||||
if starttime == endtime:
|
|
||||||
break
|
|
||||||
d = rxpin.popChars()
|
|
||||||
if d:
|
|
||||||
os.write(fd, d)
|
|
||||||
txsize = txpin.needChars()
|
|
||||||
if txsize:
|
|
||||||
res = select.select([fd], [], [], 0)
|
|
||||||
if res[0]:
|
|
||||||
try:
|
|
||||||
d = os.read(fd, txsize)
|
|
||||||
except os.error, e:
|
|
||||||
if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
txpin.pushChars(d)
|
|
||||||
trace.finish()
|
trace.finish()
|
||||||
finally:
|
finally:
|
||||||
os.unlink(ptyname)
|
os.unlink(ptyname)
|
||||||
|
|
Loading…
Reference in New Issue