mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-31 10:25:57 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			213 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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()
 |