|  | #!/usr/bin/env python3 | 
|  |  | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | import subprocess | 
|  | import tempfile | 
|  | import argparse | 
|  | import os | 
|  | import string | 
|  | import sys | 
|  |  | 
|  | quartus_cpf_template = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?> | 
|  | <cof> | 
|  | <output_filename>${OUTPUT_FILENAME}</output_filename> | 
|  | <n_pages>1</n_pages> | 
|  | <width>1</width> | 
|  | <mode>14</mode> | 
|  | <sof_data> | 
|  | <user_name>Page_0</user_name> | 
|  | <page_flags>1</page_flags> | 
|  | <bit0> | 
|  | <sof_filename>${SOF_FILENAME}<compress_bitstream>1</compress_bitstream></sof_filename> | 
|  | </bit0> | 
|  | </sof_data> | 
|  | <version>10</version> | 
|  | <create_cvp_file>0</create_cvp_file> | 
|  | <create_hps_iocsr>0</create_hps_iocsr> | 
|  | <auto_create_rpd>0</auto_create_rpd> | 
|  | <rpd_little_endian>1</rpd_little_endian> | 
|  | <options> | 
|  | <map_file>1</map_file> | 
|  | </options> | 
|  | <MAX10_device_options> | 
|  | <por>0</por> | 
|  | <io_pullup>1</io_pullup> | 
|  | <config_from_cfm0_only>0</config_from_cfm0_only> | 
|  | <isp_source>0</isp_source> | 
|  | <verify_protect>0</verify_protect> | 
|  | <epof>0</epof> | 
|  | <ufm_source>2</ufm_source> | 
|  | <ufm_filepath>${KERNEL_FILENAME}</ufm_filepath> | 
|  | </MAX10_device_options> | 
|  | <advanced_options> | 
|  | <ignore_epcs_id_check>2</ignore_epcs_id_check> | 
|  | <ignore_condone_check>2</ignore_condone_check> | 
|  | <plc_adjustment>0</plc_adjustment> | 
|  | <post_chain_bitstream_pad_bytes>-1</post_chain_bitstream_pad_bytes> | 
|  | <post_device_bitstream_pad_bytes>-1</post_device_bitstream_pad_bytes> | 
|  | <bitslice_pre_padding>1</bitslice_pre_padding> | 
|  | </advanced_options> | 
|  | </cof> | 
|  | """ | 
|  |  | 
|  | # XXX Do we care about FileRevision, DefaultMfr, PartName? Do they need | 
|  | # to be parameters? So far seems to work across 2 different boards, leave | 
|  | # this alone for now. | 
|  | quartus_pgm_template = """/* Quartus Prime Version 16.0.0 Build 211 04/27/2016 SJ Lite Edition */ | 
|  | JedecChain; | 
|  | FileRevision(JESD32A); | 
|  | DefaultMfr(6E); | 
|  |  | 
|  | P ActionCode(Cfg) | 
|  | Device PartName(10M50DAF484ES) Path("${POF_DIR}/") File("${POF_FILE}") MfrSpec(OpMask(1)); | 
|  |  | 
|  | ChainEnd; | 
|  |  | 
|  | AlteraBegin; | 
|  | ChainType(JTAG); | 
|  | AlteraEnd;""" | 
|  |  | 
|  |  | 
|  | def create_pof(input_sof, kernel_hex): | 
|  | """given an input CPU .sof file and a kernel binary, return a file-like | 
|  | object containing .pof data suitable for flashing onto the device""" | 
|  |  | 
|  | t = string.Template(quartus_cpf_template) | 
|  | output_pof = tempfile.NamedTemporaryFile(suffix=".pof") | 
|  |  | 
|  | input_sof = os.path.abspath(input_sof) | 
|  | kernel_hex = os.path.abspath(kernel_hex) | 
|  |  | 
|  | # These tools are very stupid and freak out if the desired filename | 
|  | # extensions are used. The kernel image must have extension .hex | 
|  |  | 
|  | with tempfile.NamedTemporaryFile(suffix=".cof") as temp_xml: | 
|  |  | 
|  | xml = t.substitute(SOF_FILENAME=input_sof, | 
|  | OUTPUT_FILENAME=output_pof.name, | 
|  | KERNEL_FILENAME=kernel_hex) | 
|  |  | 
|  | temp_xml.write(bytes(xml, 'UTF-8')) | 
|  | temp_xml.flush() | 
|  |  | 
|  | cmd = ["quartus_cpf", "-c", temp_xml.name] | 
|  | try: | 
|  | subprocess.check_output(cmd) | 
|  | except subprocess.CalledProcessError as cpe: | 
|  | sys.exit(cpe.output.decode("utf-8") + | 
|  | "\nFailed to create POF file") | 
|  |  | 
|  | return output_pof | 
|  |  | 
|  |  | 
|  | def flash_kernel(device_id, input_sof, kernel_hex): | 
|  | pof_file = create_pof(input_sof, kernel_hex) | 
|  |  | 
|  | with tempfile.NamedTemporaryFile(suffix=".cdf") as temp_cdf: | 
|  | dname, fname = os.path.split(pof_file.name) | 
|  | t = string.Template(quartus_pgm_template) | 
|  | cdf = t.substitute(POF_DIR=dname, POF_FILE=fname) | 
|  | temp_cdf.write(bytes(cdf, 'UTF-8')) | 
|  | temp_cdf.flush() | 
|  | cmd = ["quartus_pgm", "-c", device_id, temp_cdf.name] | 
|  | try: | 
|  | subprocess.check_output(cmd) | 
|  | except subprocess.CalledProcessError as cpe: | 
|  | sys.exit(cpe.output.decode("utf-8") + | 
|  | "\nFailed to flash image") | 
|  | pof_file.close() | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser(description="Flash zephyr onto Altera boards") | 
|  | parser.add_argument("-s", "--sof", | 
|  | help=".sof file with Nios II CPU configuration") | 
|  | parser.add_argument("-k", "--kernel", | 
|  | help="Zephyr kernel image to place into UFM in Intel HEX format") | 
|  | parser.add_argument("-d", "--device", | 
|  | help="Remote device identifier / cable name. Default is " | 
|  | "USB-BlasterII. Run jtagconfig -n if unsure.", | 
|  | default="USB-BlasterII") | 
|  |  | 
|  | args = parser.parse_args() | 
|  |  | 
|  | flash_kernel(args.device, args.sof, args.kernel) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |