simulavr-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Simulavr-devel] Example python script for serial simulation and tracing


From: Kevin O'Connor
Subject: [Simulavr-devel] Example python script for serial simulation and tracing
Date: Sun, 24 May 2015 10:52:00 -0400
User-agent: Mutt/1.5.23 (2014-03-12)

Hi,

I put together a python script that works with simulavr to simulate a
serial port that one can use to interact with the simulated machine's
UART.  The script also supports creating of VCD trace files for
analyzing pin changes and other events.

This is useful for AVR programs that communication over a serial port
with a host system.  For example, if one normally uses a host program
to communicate with a real AVR device - such as:

$ cu -l /dev/ttyS0 -s 9600

then a simulation could be done with something like:

$ avrserial.py program.elf -b 9600 -p /tmp/pseudoserial &
$ cu -l /tmp/pseudoserial -s 9600

The avrserial.py script creates a pseudo-tty device that most serial
terminal programs can use instead of a real serial device.  The
created pseudo-serial is forwarded to the AVR simulation.

It took some time to get the python parameters for this correct, so I
figured I would post the script in case others find it useful.  I
posted an earlier version of the script last year:
http://lists.gnu.org/archive/html/simulavr-devel/2014-08/msg00000.html

The script supports a number of options - run "avrserial.py -h" to see
them.  The script uses the simulavr python library, so simulavr must
be compiled with python support and the python module must be in the
python path.  I ususally run the script with something like:

$ PYTHONPATH=/path/to/simulavr/src ./avrserial.py -h

Please CC me on any replies.
-Kevin


=========================== avrserial.py ===========================

#!/usr/bin/env python
# Script to interact with simulavr by simulating a serial port.
#
# Copyright (C) 2015  Kevin O'Connor <address@hidden>
#
# This file may be distributed under the terms of the GNU GPLv3 license.

import sys, optparse, os, pty, select, fcntl, termios, traceback
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):
    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 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 emulting 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)

    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)
            res = select.select([fd], [], [], 0)
            if res[0]:
                d = os.read(fd, 1024)
                txpin.pushChars(d)
        trace.finish()
    finally:
        os.unlink(ptyname)

if __name__ == '__main__':
    main()



reply via email to

[Prev in Thread] Current Thread [Next in Thread]