1#!/usr/bin/env python3
2
3# SPDX-License-Identifier: Apache-2.0
4import subprocess
5import tempfile
6import argparse
7import os
8import string
9import sys
10
11quartus_cpf_template = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
12<cof>
13	<output_filename>${OUTPUT_FILENAME}</output_filename>
14	<n_pages>1</n_pages>
15	<width>1</width>
16	<mode>14</mode>
17	<sof_data>
18		<user_name>Page_0</user_name>
19		<page_flags>1</page_flags>
20		<bit0>
21			<sof_filename>${SOF_FILENAME}<compress_bitstream>1</compress_bitstream></sof_filename>
22		</bit0>
23	</sof_data>
24	<version>10</version>
25	<create_cvp_file>0</create_cvp_file>
26	<create_hps_iocsr>0</create_hps_iocsr>
27	<auto_create_rpd>0</auto_create_rpd>
28	<rpd_little_endian>1</rpd_little_endian>
29	<options>
30		<map_file>1</map_file>
31	</options>
32	<MAX10_device_options>
33		<por>0</por>
34		<io_pullup>1</io_pullup>
35		<config_from_cfm0_only>0</config_from_cfm0_only>
36		<isp_source>0</isp_source>
37		<verify_protect>0</verify_protect>
38		<epof>0</epof>
39		<ufm_source>2</ufm_source>
40		<ufm_filepath>${KERNEL_FILENAME}</ufm_filepath>
41	</MAX10_device_options>
42	<advanced_options>
43		<ignore_epcs_id_check>2</ignore_epcs_id_check>
44		<ignore_condone_check>2</ignore_condone_check>
45		<plc_adjustment>0</plc_adjustment>
46		<post_chain_bitstream_pad_bytes>-1</post_chain_bitstream_pad_bytes>
47		<post_device_bitstream_pad_bytes>-1</post_device_bitstream_pad_bytes>
48		<bitslice_pre_padding>1</bitslice_pre_padding>
49	</advanced_options>
50</cof>
51"""
52
53# XXX Do we care about FileRevision, DefaultMfr, PartName? Do they need
54# to be parameters? So far seems to work across 2 different boards, leave
55# this alone for now.
56quartus_pgm_template = """/* Quartus Prime Version 16.0.0 Build 211 04/27/2016 SJ Lite Edition */
57JedecChain;
58	FileRevision(JESD32A);
59	DefaultMfr(6E);
60
61	P ActionCode(Cfg)
62		Device PartName(10M50DAF484ES) Path("${POF_DIR}/") File("${POF_FILE}") MfrSpec(OpMask(1));
63
64ChainEnd;
65
66AlteraBegin;
67	ChainType(JTAG);
68AlteraEnd;"""
69
70
71def create_pof(input_sof, kernel_hex):
72    """given an input CPU .sof file and a kernel binary, return a file-like
73    object containing .pof data suitable for flashing onto the device"""
74
75    t = string.Template(quartus_cpf_template)
76    output_pof = tempfile.NamedTemporaryFile(suffix=".pof")
77
78    input_sof = os.path.abspath(input_sof)
79    kernel_hex = os.path.abspath(kernel_hex)
80
81    # These tools are very stupid and freak out if the desired filename
82    # extensions are used. The kernel image must have extension .hex
83
84    with tempfile.NamedTemporaryFile(suffix=".cof") as temp_xml:
85
86        xml = t.substitute(SOF_FILENAME=input_sof,
87                           OUTPUT_FILENAME=output_pof.name,
88                           KERNEL_FILENAME=kernel_hex)
89
90        temp_xml.write(bytes(xml, 'UTF-8'))
91        temp_xml.flush()
92
93        cmd = ["quartus_cpf", "-c", temp_xml.name]
94        try:
95            subprocess.check_output(cmd)
96        except subprocess.CalledProcessError as cpe:
97            sys.exit(cpe.output.decode("utf-8") +
98                     "\nFailed to create POF file")
99
100    return output_pof
101
102
103def flash_kernel(device_id, input_sof, kernel_hex):
104    pof_file = create_pof(input_sof, kernel_hex)
105
106    with tempfile.NamedTemporaryFile(suffix=".cdf") as temp_cdf:
107        dname, fname = os.path.split(pof_file.name)
108        t = string.Template(quartus_pgm_template)
109        cdf = t.substitute(POF_DIR=dname, POF_FILE=fname)
110        temp_cdf.write(bytes(cdf, 'UTF-8'))
111        temp_cdf.flush()
112        cmd = ["quartus_pgm", "-c", device_id, temp_cdf.name]
113        try:
114            subprocess.check_output(cmd)
115        except subprocess.CalledProcessError as cpe:
116            sys.exit(cpe.output.decode("utf-8") +
117                     "\nFailed to flash image")
118    pof_file.close()
119
120def main():
121    parser = argparse.ArgumentParser(description="Flash zephyr onto Altera boards", allow_abbrev=False)
122    parser.add_argument("-s", "--sof",
123            help=".sof file with Nios II CPU configuration")
124    parser.add_argument("-k", "--kernel",
125            help="Zephyr kernel image to place into UFM in Intel HEX format")
126    parser.add_argument("-d", "--device",
127            help="Remote device identifier / cable name. Default is "
128                 "USB-BlasterII. Run jtagconfig -n if unsure.",
129            default="USB-BlasterII")
130
131    args = parser.parse_args()
132
133    flash_kernel(args.device, args.sof, args.kernel)
134
135
136if __name__ == "__main__":
137    main()
138