Last active
December 13, 2019 19:30
-
-
Save agrif/ef4a7806c524572d15899f433ca3bed1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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