| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # Script to interact with simulavr by simulating a serial port. | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2018-02-04 11:57:22 -05:00
										 |  |  | # Copyright (C) 2015-2018  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  | import sys, optparse, time, os, pty, fcntl, termios, errno | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | import pysimulavr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SERIALBITS = 10 # 8N1 = 1 start, 8 data, 1 stop | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  | SIMULAVR_FREQ = 10**9 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Class to read serial data from AVR serial transmit pin. | 
					
						
							|  |  |  | class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |     def __init__(self, baud, terminal): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         pysimulavr.Pin.__init__(self) | 
					
						
							|  |  |  |         pysimulavr.PySimulationMember.__init__(self) | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |         self.terminal = terminal | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.sc = pysimulavr.SystemClock.Instance() | 
					
						
							| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  |         self.delay = SIMULAVR_FREQ // baud | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.current = 0 | 
					
						
							|  |  |  |         self.pos = -1 | 
					
						
							|  |  |  |     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: | 
					
						
							| 
									
										
										
										
											2020-09-16 12:05:26 -04:00
										 |  |  |             data = bytearray([(self.current >> 1) & 0xff]) | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |             self.terminal.write(data) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             self.pos = -1 | 
					
						
							|  |  |  |             self.current = 0 | 
					
						
							|  |  |  |             return -1 | 
					
						
							|  |  |  |         return self.delay | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Class to send serial data to AVR serial receive pin. | 
					
						
							|  |  |  | class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |     def __init__(self, baud, terminal): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         pysimulavr.Pin.__init__(self) | 
					
						
							|  |  |  |         pysimulavr.PySimulationMember.__init__(self) | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |         self.terminal = terminal | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.SetPin('H') | 
					
						
							|  |  |  |         self.sc = pysimulavr.SystemClock.Instance() | 
					
						
							| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  |         self.delay = SIMULAVR_FREQ // baud | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.current = 0 | 
					
						
							|  |  |  |         self.pos = 0 | 
					
						
							| 
									
										
										
										
											2020-09-16 12:05:26 -04:00
										 |  |  |         self.queue = bytearray() | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |         self.sc.Add(self) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def DoStep(self, trueHwStep): | 
					
						
							|  |  |  |         if not self.pos: | 
					
						
							|  |  |  |             if not self.queue: | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |                 data = self.terminal.read() | 
					
						
							|  |  |  |                 if not data: | 
					
						
							|  |  |  |                     return self.delay * 100 | 
					
						
							| 
									
										
										
										
											2020-09-16 12:05:26 -04:00
										 |  |  |                 self.queue.extend(data) | 
					
						
							|  |  |  |             self.current = (self.queue.pop(0) << 1) | 0x200 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  | # Pace the simulation scaled to real time | 
					
						
							|  |  |  | class Pacing(pysimulavr.PySimulationMember): | 
					
						
							|  |  |  |     def __init__(self, rate): | 
					
						
							|  |  |  |         pysimulavr.PySimulationMember.__init__(self) | 
					
						
							|  |  |  |         self.sc = pysimulavr.SystemClock.Instance() | 
					
						
							|  |  |  |         self.pacing_rate = 1. / (rate * SIMULAVR_FREQ) | 
					
						
							| 
									
										
										
										
											2018-02-04 11:57:22 -05:00
										 |  |  |         self.next_check_clock = 0 | 
					
						
							|  |  |  |         self.rel_time = time.time() | 
					
						
							|  |  |  |         self.best_offset = 0. | 
					
						
							| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  |         self.delay = SIMULAVR_FREQ // 10000 | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  |         self.sc.Add(self) | 
					
						
							|  |  |  |     def DoStep(self, trueHwStep): | 
					
						
							|  |  |  |         curtime = time.time() | 
					
						
							|  |  |  |         clock = self.sc.GetCurrentTime() | 
					
						
							| 
									
										
										
										
											2018-02-04 11:57:22 -05:00
										 |  |  |         offset = clock * self.pacing_rate - (curtime - self.rel_time) | 
					
						
							|  |  |  |         self.best_offset = max(self.best_offset, offset) | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  |         if offset > 0.000050: | 
					
						
							| 
									
										
										
										
											2018-02-04 11:57:22 -05:00
										 |  |  |             time.sleep(offset - 0.000040) | 
					
						
							|  |  |  |         if clock >= self.next_check_clock: | 
					
						
							|  |  |  |             self.rel_time -= min(self.best_offset, 0.) | 
					
						
							|  |  |  |             self.next_check_clock = clock + self.delay * 500 | 
					
						
							|  |  |  |             self.best_offset = -999999999. | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  |         return self.delay | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  | # 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) | 
					
						
							| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  |         except os.error as e: | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |             if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK): | 
					
						
							|  |  |  |                 pysimulavr.SystemClock.Instance().stop() | 
					
						
							|  |  |  |         return "" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # 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) | 
					
						
							| 
									
										
										
										
											2020-05-05 14:57:42 -07:00
										 |  |  |     tcattr = termios.tcgetattr(mfd) | 
					
						
							|  |  |  |     tcattr[0] &= ~( | 
					
						
							|  |  |  |         termios.IGNBRK | termios.BRKINT | termios.PARMRK | termios.ISTRIP | | 
					
						
							|  |  |  |         termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IXON) | 
					
						
							|  |  |  |     tcattr[1] &= ~termios.OPOST | 
					
						
							|  |  |  |     tcattr[3] &= ~( | 
					
						
							|  |  |  |         termios.ECHO | termios.ECHONL | termios.ICANON | termios.ISIG | | 
					
						
							|  |  |  |         termios.IEXTEN) | 
					
						
							|  |  |  |     tcattr[2] &= ~(termios.CSIZE | termios.PARENB) | 
					
						
							|  |  |  |     tcattr[2] |= termios.CS8 | 
					
						
							|  |  |  |     tcattr[6][termios.VMIN] = 0 | 
					
						
							|  |  |  |     tcattr[6][termios.VTIME] = 0 | 
					
						
							|  |  |  |     termios.tcsetattr(mfd, termios.TCSAFLUSH, tcattr) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     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") | 
					
						
							| 
									
										
										
										
											2021-09-16 20:09:33 -04:00
										 |  |  |     opts.add_option("-s", "--speed", type="int", dest="speed", default=16000000, | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                     help="machine speed") | 
					
						
							| 
									
										
										
										
											2019-02-27 13:16:22 -05:00
										 |  |  |     opts.add_option("-r", "--rate", type="float", dest="pacing_rate", | 
					
						
							|  |  |  |                     default=0., help="real-time pacing rate") | 
					
						
							| 
									
										
										
										
											2021-09-16 20:09:33 -04:00
										 |  |  |     opts.add_option("-b", "--baud", type="int", dest="baud", default=250000, | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                     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) | 
					
						
							| 
									
										
										
										
											2020-03-20 18:55:21 +01:00
										 |  |  |     dev.SetClockFreq(SIMULAVR_FREQ // speed) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     sc.Add(dev) | 
					
						
							| 
									
										
										
										
											2017-03-26 20:45:19 -04:00
										 |  |  |     pysimulavr.cvar.sysConHandler.SetUseExit(False) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     trace.load_options() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-22 20:20:42 -04:00
										 |  |  |     # Do optional real-time pacing | 
					
						
							|  |  |  |     if options.pacing_rate: | 
					
						
							|  |  |  |         pacing = Pacing(options.pacing_rate) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |     # Setup terminal | 
					
						
							|  |  |  |     io = TerminalIO() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Setup rx pin | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |     rxpin = SerialRxPin(baud, io) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     net = pysimulavr.Net() | 
					
						
							|  |  |  |     net.Add(rxpin) | 
					
						
							|  |  |  |     net.Add(dev.GetPin("D1")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Setup tx pin | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |     txpin = SerialTxPin(baud, io) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     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: | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |         io.run(fd) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         trace.start() | 
					
						
							| 
									
										
										
										
											2017-03-22 20:02:13 -04:00
										 |  |  |         sc.RunTimeRange(0x7fff0000ffff0000) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         trace.finish() | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         os.unlink(ptyname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |