diff options
Diffstat (limited to 'scripts/avrsim.py')
-rwxr-xr-x | scripts/avrsim.py | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/scripts/avrsim.py b/scripts/avrsim.py new file mode 100755 index 000000000..bbcc20b7f --- /dev/null +++ b/scripts/avrsim.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# Script to interact with simulavr by simulating a serial port. +# +# Copyright (C) 2015 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys, optparse, os, pty, select, fcntl, termios, traceback, errno +import pysimulavr + +SERIALBITS = 10 # 8N1 = 1 start, 8 data, 1 stop + +# Class to read serial data from AVR serial transmit pin. +class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): + def __init__(self, baud): + pysimulavr.Pin.__init__(self) + pysimulavr.PySimulationMember.__init__(self) + self.sc = pysimulavr.SystemClock.Instance() + self.delay = 10**9 / baud + self.current = 0 + self.pos = -1 + self.queue = "" + def SetInState(self, pin): + pysimulavr.Pin.SetInState(self, pin) + self.state = pin.outState + if self.pos < 0 and pin.outState == pin.LOW: + self.pos = 0 + self.sc.Add(self) + def DoStep(self, trueHwStep): + ishigh = self.state == self.HIGH + self.current |= ishigh << self.pos + self.pos += 1 + if self.pos == 1: + return int(self.delay * 1.5) + if self.pos >= SERIALBITS: + self.queue += chr((self.current >> 1) & 0xff) + self.pos = -1 + self.current = 0 + return -1 + return self.delay + def popChars(self): + d = self.queue + self.queue = "" + return d + +# Class to send serial data to AVR serial receive pin. +class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): + MAX_QUEUE = 64 + def __init__(self, baud): + pysimulavr.Pin.__init__(self) + pysimulavr.PySimulationMember.__init__(self) + self.SetPin('H') + self.sc = pysimulavr.SystemClock.Instance() + self.delay = 10**9 / baud + self.current = 0 + self.pos = 0 + self.queue = "" + def DoStep(self, trueHwStep): + if not self.pos: + if not self.queue: + return -1 + self.current = (ord(self.queue[0]) << 1) | 0x200 + self.queue = self.queue[1:] + newstate = 'L' + if self.current & (1 << self.pos): + newstate = 'H' + self.SetPin(newstate) + self.pos += 1 + if self.pos >= SERIALBITS: + self.pos = 0 + 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 +class Tracing: + def __init__(self, filename, signals): + self.filename = filename + self.signals = signals + if not signals: + self.dman = None + return + self.dman = pysimulavr.DumpManager.Instance() + self.dman.SetSingleDeviceApp() + def show_help(self): + ostr = pysimulavr.ostringstream() + self.dman.save(ostr) + sys.stdout.write(ostr.str()) + sys.exit(1) + def load_options(self): + if self.dman is None: + return + if self.signals.strip() == '?': + self.show_help() + sigs = "\n".join(["+ " + s for s in self.signals.split(',')]) + self.dman.addDumpVCD(self.filename, sigs, "ns", False, False) + def start(self): + if self.dman is not None: + self.dman.start() + def finish(self): + if self.dman is not None: + self.dman.stopApplication() + +# Support for creating a pseudo-tty for emulating a serial port +def create_pty(ptyname): + mfd, sfd = pty.openpty() + try: + os.unlink(ptyname) + except os.error: + pass + os.symlink(os.ttyname(sfd), ptyname) + fcntl.fcntl(mfd, fcntl.F_SETFL + , fcntl.fcntl(mfd, fcntl.F_GETFL) | os.O_NONBLOCK) + old = termios.tcgetattr(mfd) + old[3] = old[3] & ~termios.ECHO + termios.tcsetattr(mfd, termios.TCSADRAIN, old) + return mfd + +def main(): + usage = "%prog [options] <program.elf>" + opts = optparse.OptionParser(usage) + opts.add_option("-m", "--machine", type="string", dest="machine", + default="atmega644", help="type of AVR machine to simulate") + opts.add_option("-s", "--speed", type="int", dest="speed", default=8000000, + help="machine speed") + opts.add_option("-b", "--baud", type="int", dest="baud", default=38400, + help="baud rate of the emulated serial port") + opts.add_option("-t", "--trace", type="string", dest="trace", + help="signals to trace (? for help)") + opts.add_option("-p", "--port", type="string", dest="port", + default="/tmp/pseudoserial", + help="pseudo-tty device to create for serial port") + deffile = os.path.splitext(os.path.basename(sys.argv[0]))[0] + ".vcd" + opts.add_option("-f", "--tracefile", type="string", dest="tracefile", + default=deffile, help="filename to write signal trace to") + options, args = opts.parse_args() + if len(args) != 1: + opts.error("Incorrect number of arguments") + elffile = args[0] + proc = options.machine + ptyname = options.port + speed = options.speed + baud = options.baud + + # launch simulator + sc = pysimulavr.SystemClock.Instance() + trace = Tracing(options.tracefile, options.trace) + dev = pysimulavr.AvrFactory.instance().makeDevice(proc) + dev.Load(elffile) + dev.SetClockFreq(10**9 / speed) + sc.Add(dev) + trace.load_options() + + # Setup rx pin + rxpin = SerialRxPin(baud) + net = pysimulavr.Net() + net.Add(rxpin) + net.Add(dev.GetPin("D1")) + + # Setup tx pin + txpin = SerialTxPin(baud) + net2 = pysimulavr.Net() + net2.Add(dev.GetPin("D0")) + net2.Add(txpin) + + # Display start banner + msg = "Starting AVR simulation: machine=%s speed=%d\n" % (proc, speed) + msg += "Serial: port=%s baud=%d\n" % (ptyname, baud) + if options.trace: + msg += "Trace file: %s\n" % (options.tracefile,) + sys.stdout.write(msg) + sys.stdout.flush() + + # Create terminal device + fd = create_pty(ptyname) + + # Run loop + try: + trace.start() + while 1: + 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() + finally: + os.unlink(ptyname) + +if __name__ == '__main__': + main() |