Example: In-System Memory Editing
As a working example, we will use quartustcl together with the mif Python package to read and write editable memories on a connected FPGA.
To work through this example, you will need to install both of these
packages with pip
on the command line.
pip install --upgrade mif quartustcl
Then, in any Python program or session where you use these packages:
import mif
import quartustcl
Finding Your Hardware
The documentation for the Quartus memory editing package
tells us that begin_memory_edit
needs a hardware and device name,
which can be obtained using the Quartus JTAG package. Both of
these packages are available in quartus_stp
, which happens to be the
default Tcl shell for quartustcl.
quartus = quartustcl.QuartusTcl()
The documentation says get_hardware_names
returns a
list. Normally, quartustcl returns only strings. To parse lists, we
need to use parse
. So, calling get_hardware_names
in Python
looks like this:
hwnames = quartus.parse(quartus.get_hardware_names())
hwname = hwnames[0]
Normally, you would want some way to decide which connected hardware to use, but for the example we will just use the first one.
Next, we can use get_device_names
to get the devices connected to a
given piece of hardware. The documentation tells us that the Tcl
syntax for this command is get_device_names -hardware_name <name>
,
and it returns a list. In Python, that looks like this:
devnames = quartus.parse(quartus.get_device_names(hardware_name=hwname))
devname = devnames[0]
Finding Editable Memories
Now we can use get_editable_mem_instances
to find the
editable memories on our chosen device. The documentation for this
command tells us that it returns a list of memory instance
descriptions, which are themselves lists. This “list of lists” can be
parsed at once with the levels
argument to parse
.
memories_raw = quartus.get_editable_mem_instances(
hardware_name=hwname, device_name=devname)
memories = quartus.parse(memories_raw, levels=2)
Now we can search through these memories to find the ID of one we
want. For example, to find the memory named CTRL
:
found_memid = None
for memid, depth, width, rw, type, name in memories:
if name == 'CTRL':
found_memid = memid
if found_memid is None:
raise RuntimeError('could not find memory CTRL')
Note that parse
only parses Tcl lists, and makes no attempt to parse
values into integers or other types. This means memid
and the other
variables are all strings. This will not matter for this example, but
it can for other tasks.
Reading and Writing Memories
Now we can finally start the memory edit:
quartus.begin_memory_edit(hardware_name=hwname, device_name=devname)
We can read the current contents into a MIF file named contents.mif
with save_content_from_memory_to_file
.
quartus.save_content_from_memory_to_file(
instance_index=found_memid,
mem_file_path='contents.mif',
mem_file_type='mif',
)
Using the mif package, we can load that data into a Python array, and modify it.
with open('contents.mif') as f:
data = mif.load(f)
# set least-significant bit of address 0x01 to 1
data[0x01][0] = 1
Now that it’s modified, we can write it out and upload it to the
device using update_content_to_memory_from_file
.
with open('contents.mif', 'w') as f:
mif.dump(data, f)
quartus.update_content_to_memory_from_file(
instance_index=found_memid,
mem_file_path='contents.mif',
mem_file_type='mif',
)
Finally, we end the transaction with end_memory_edit
.
quartus.end_memory_edit()