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:
Kevin O'Connor 2017-03-22 20:02:13 -04:00
parent 1fbb36fa87
commit 5f29787dc7
1 changed files with 39 additions and 45 deletions

View File

@ -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)