Skip to content

Instantly share code, notes, and snippets.

@agrif
Last active December 13, 2019 19:30
Show Gist options
  • Save agrif/ef4a7806c524572d15899f433ca3bed1 to your computer and use it in GitHub Desktop.
Save agrif/ef4a7806c524572d15899f433ca3bed1 to your computer and use it in GitHub Desktop.
from quartus_tcl import QuartusTcl
import tempfile
import os.path
import intelhex # install with: pip install intelhex
quartus = QuartusTcl()
# get hardware, device, and memory instance id
# (you might want to do something smarter here)
hw = quartus.get_hardware_names()[0]
dev = quartus.get_device_names(hardware_name=hw)[0]
idx = 0
# you can also ask for information about editable memories on a device
# but the format is nested lists, which QuartusTcl() doesn't handle automatically
# (this part is not necessary, if it is confusing)
meminfo = quartus.get_editable_mem_instances(hardware_name=hw, device_name=dev)
print('found memories:')
for mi in meminfo:
# calling parse(...) will turn a Tcl list into a Python list...
mi = quartus.parse(mi)
print(mi)
# read memory
with tempfile.TemporaryDirectory() as tmpdir:
tmpfile = os.path.join(tmpdir, 'data.hex')
# dump memory from quartus
quartus.begin_memory_edit(hardware_name=hw, device_name=dev)
quartus.save_content_from_memory_to_file(instance_index=idx, mem_file_path=tmpfile, mem_file_type='hex')
quartus.end_memory_edit()
# load hex file
memory = intelhex.IntelHex(tmpfile)
# modify the memory we've loaded, as an example
memory[0] = 42
# write memory
with tempfile.TemporaryDirectory() as tmpdir:
tmpfile = os.path.join(tmpdir, 'data.hex')
# write hex file
memory.write_hex_file(tmpfile)
# load memory via quartus
quartus.begin_memory_edit(hardware_name=hw, device_name=dev)
quartus.update_content_to_memory_from_file(instance_index=idx, mem_file_path=tmpfile, mem_file_type='hex')
quartus.end_memory_edit()
import subprocess
import functools
import tkinter
class QuartusTcl:
"""A class for managing a Quartus Tcl interpreter as a subprocess."""
def __init__(self, debug=False):
self.debug = debug
# we use python's built-in Tcl interpreter to help us parse Tcl lists
self.tcl = tkinter.Tcl()
# we launch a single instance of the quartus tcl shell, and then
# talk to it line by line
self.process = subprocess.Popen(['quartus_stp', '-s'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
def interact(self, line):
"""Write one line to the Tcl interpreter, and read one line out."""
# write a single line to our subprocess
# wrap it in puts to guarantee at least one newline is output
if self.debug:
print('<<<', line)
self.process.stdin.write(('puts [' + line + ']\n').encode())
self.process.stdin.flush()
# read the output, which will be prefixed with "tcl>"
while True:
outline = self.process.stdout.readline().decode()
if outline.startswith('tcl> '):
prompt, output = outline.split(' ', 1)
output = output.strip()
if output.startswith('ERROR:'):
raise RuntimeError(output)
if self.debug and output:
print('>>>', output)
return output
def parse(self, data):
"""Parse a Tcl-formatted list into a Python list."""
data = data.strip()
# first, make sure the list is canonically formatted
try:
data = '{' + self.tcl.eval('list ' + data) + '}'
except Exception:
raise RuntimeError('Tcl list could not be parsed: ' + repr(data))
# what is the length of the list?
length = int(self.tcl.eval('llength {}'.format(data)))
# iterate through each item, and add it to our python list
parsed = []
for i in range(length):
# get the i'th element...
part = self.tcl.eval('lindex {} {}'.format(data, i))
parsed.append(part)
return parsed
def run(self, cmd, *args):
"""Run a Tcl command, and parse and return the resulting list.
`cmd` can be a format string, which will be filled out with the
remaining arguments. If used this way, the remaining arguments are
quoted in Tcl using {...}. For example:
quartus.run("get_device_names -hardware_name {}", "Foo Bar")
will result in running
get_device_names -hardware_name {Foo Bar}
in the Tcl interpreter subprocess. If you do not want this automatic
quoting, you can use the usual format() method on strings.
"""
# construct the full command by formatting-in our later arguments
# but -- quote them in braces first!
if args:
cmd = cmd.format(*['{' + a + '}' for a in args])
return self.parse(self.interact(cmd))
def run_args(self, cmd, *args, **kwargs):
"""Run a Tcl command with the given arguments and optional arguments.
`cmd` is a bare Tcl command. For example:
quartus.run_args('get_device_names', hardware_name="Foo Bar")
will result in running
get_device_names -hardware_name {Foo Bar}
"""
args = [cmd] + ['{' + str(a) + '}' for a in args]
for k, v in kwargs.items():
args.append('-' + k)
args.append('{' + str(v) + '}')
return self.run(' '.join(args))
def __getattr__(self, attr):
"""A bit of magic: turn unknown method calls on this object into calls
to run_args. For example:
quartus.get_device_names(hardware_name="Foo Bar")
will result in running
get_device_names -hardware_name {Foo Bar}
"""
return functools.partial(self.run_args, attr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment