blob: 0629fef9c24880bdd85c0b20cc054b65cac0321e [file] [log] [blame]
Jonathan Strandbergd2afc512021-03-19 10:31:18 +01001#!/usr/bin/env python3
2
3#
Davide Grohmann5508acb2022-08-08 17:12:08 +02004# Copyright (c) 2021-2022 Arm Limited.
Jonathan Strandbergd2afc512021-03-19 10:31:18 +01005#
6# SPDX-License-Identifier: Apache-2.0
7#
8# Licensed under the Apache License, Version 2.0 (the License); you may
9# not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12# www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an AS IS BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20
21import argparse
22import multiprocessing
23import numpy
24import os
25import pathlib
26import re
27import shutil
28import subprocess
29import sys
30
31os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
Kristofer Jonssonffbd8e72021-06-15 17:51:58 +020032from tensorflow.lite.python.interpreter import Interpreter, OpResolverType
Jonathan Strandbergd2afc512021-03-19 10:31:18 +010033
34CORE_PLATFORM_PATH = pathlib.Path(__file__).resolve().parents[1]
35
36def run_cmd(cmd, **kwargs):
37 # str() is called to handle pathlib.Path objects
38 cmd_str = " ".join([str(arg) for arg in cmd])
39 print(f"Running command: {cmd_str}")
40 return subprocess.run(cmd, check=True, **kwargs)
41
Nir Ekhauza58edd82021-10-04 12:21:17 +030042def ta_parse_raw(ta_raw):
43 ta_parsed = [-1, -1]
44 if ta_raw:
45 for v in ta_raw:
46 index = v[0]
47 value = v[1]
48 if (index > 1):
49 raise Exception("Illegal index value - Should be '0' or '1'")
50 (ta_parsed)[index] = value
Davide Grohmann5508acb2022-08-08 17:12:08 +020051
Nir Ekhauza58edd82021-10-04 12:21:17 +030052 return ta_parsed
53
54def build_core_platform(output_folder, target, toolchain, memory_model, memory_arena, pmu,
55 ta_maxr, ta_maxw, ta_maxrw, ta_rlatency, ta_wlatency,
56 ta_pulse_on, ta_pulse_off, ta_bwcap, ta_perfctrl, ta_perfcnt,
57 ta_mode, ta_histbin, ta_histcnt):
Jonathan Strandbergd2afc512021-03-19 10:31:18 +010058 build_folder = output_folder/"model"/"build"
Nir Ekhauza58edd82021-10-04 12:21:17 +030059 maxr = ta_parse_raw(ta_maxr)
60 maxw = ta_parse_raw(ta_maxw)
61 maxrw = ta_parse_raw(ta_maxrw)
62 rlatency = ta_parse_raw(ta_rlatency)
63 wlatency = ta_parse_raw(ta_wlatency)
64 pulse_on = ta_parse_raw(ta_pulse_on)
65 pulse_off = ta_parse_raw(ta_pulse_off)
66 bwcap = ta_parse_raw(ta_bwcap)
67 perfctrl = ta_parse_raw(ta_perfctrl)
68 perfcnt = ta_parse_raw(ta_perfcnt)
69 mode = ta_parse_raw(ta_mode)
70 histbin = ta_parse_raw(ta_histbin)
71 histcnt = ta_parse_raw(ta_histcnt)
Jonathan Strandbergd2afc512021-03-19 10:31:18 +010072 cmake_cmd = ["cmake",
73 CORE_PLATFORM_PATH/"targets"/target,
74 f"-B{build_folder}",
75 f"-DCMAKE_TOOLCHAIN_FILE={CORE_PLATFORM_PATH/'cmake'/'toolchain'/(toolchain + '.cmake')}",
Nir Ekhauz3c505ca2021-06-06 14:57:50 +030076 f"-DBAREMETAL_PATH={output_folder}",
77 f"-DMEMORY_MODEL={memory_model}",
Nir Ekhauza58edd82021-10-04 12:21:17 +030078 f"-DMEMORY_ARENA={memory_arena}",
79 f"-DETHOSU_TA_MAXR_0={maxr[0]}",
80 f"-DETHOSU_TA_MAXR_1={maxr[1]}",
81 f"-DETHOSU_TA_MAXW_0={maxw[0]}",
82 f"-DETHOSU_TA_MAXW_1={maxw[1]}",
83 f"-DETHOSU_TA_MAXRW_0={maxrw[0]}",
84 f"-DETHOSU_TA_MAXRW_1={maxrw[1]}",
85 f"-DETHOSU_TA_RLATENCY_0={rlatency[0]}",
86 f"-DETHOSU_TA_RLATENCY_1={rlatency[1]}",
87 f"-DETHOSU_TA_WLATENCY_0={wlatency[0]}",
88 f"-DETHOSU_TA_WLATENCY_1={wlatency[1]}",
89 f"-DETHOSU_TA_PULSE_ON_0={pulse_on[0]}",
90 f"-DETHOSU_TA_PULSE_ON_1={pulse_on[1]}",
91 f"-DETHOSU_TA_PULSE_OFF_0={pulse_off[0]}",
92 f"-DETHOSU_TA_PULSE_OFF_1={pulse_off[1]}",
93 f"-DETHOSU_TA_BWCAP_0={bwcap[0]}",
94 f"-DETHOSU_TA_BWCAP_1={bwcap[1]}",
95 f"-DETHOSU_TA_PERFCTRL_0={perfctrl[0]}",
96 f"-DETHOSU_TA_PERFCTRL_1={perfctrl[1]}",
97 f"-DETHOSU_TA_PERFCNT_0={perfcnt[0]}",
98 f"-DETHOSU_TA_PERFCNT_1={perfcnt[1]}",
99 f"-DETHOSU_TA_MODE_0={mode[0]}",
100 f"-DETHOSU_TA_MODE_1={mode[1]}",
101 f"-DETHOSU_TA_HISTBIN_0={histbin[0]}",
102 f"-DETHOSU_TA_HISTBIN_1={histbin[1]}",
103 f"-DETHOSU_TA_HISTCNT_0={histcnt[0]}",
104 f"-DETHOSU_TA_HISTCNT_1={histcnt[1]}"]
105
Nir Ekhauz1af67f72021-09-13 16:56:39 +0300106 if pmu:
107 for i in range(len(pmu)):
108 cmake_cmd += [f"-DETHOSU_PMU_EVENT_{i}={pmu[i]}"]
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100109 run_cmd(cmake_cmd)
110
Kristofer Jonssonffbd8e72021-06-15 17:51:58 +0200111 make_cmd = ["make", "-C", build_folder, f"-j{multiprocessing.cpu_count()}", "baremetal_custom"]
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100112 run_cmd(make_cmd)
113
114def generate_reference_data(output_folder, non_optimized_model_path, input_path, expected_output_path):
Kristofer Jonssonffbd8e72021-06-15 17:51:58 +0200115 interpreter = Interpreter(model_path=str(non_optimized_model_path.resolve()), experimental_op_resolver_type=OpResolverType.BUILTIN_REF)
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100116
117 interpreter.allocate_tensors()
118 input_detail = interpreter.get_input_details()[0]
119 output_detail = interpreter.get_output_details()[0]
120
121 input_data = None
122 if input_path is None:
123 # Randomly generate input data
124 dtype = input_detail["dtype"]
125 if dtype is numpy.float32:
126 rand = numpy.random.default_rng()
127 input_data = rand.random(size=input_detail["shape"], dtype=numpy.float32)
128 else:
129 input_data = numpy.random.randint(low=numpy.iinfo(dtype).min, high=numpy.iinfo(dtype).max, size=input_detail["shape"], dtype=dtype)
130 else:
131 # Load user provided input data
132 input_data = numpy.load(input_path)
133
134 output_data = None
135 if expected_output_path is None:
136 # Run the network with input_data to get reference output
137 interpreter.set_tensor(input_detail["index"], input_data)
138 interpreter.invoke()
139 output_data = interpreter.get_tensor(output_detail["index"])
140 else:
141 # Load user provided output data
142 output_data = numpy.load(expected_output_path)
143
144 network_input_path = output_folder/"ref_input.bin"
145 network_output_path = output_folder/"ref_output.bin"
146
147 with network_input_path.open("wb") as fp:
148 fp.write(input_data.tobytes())
149 with network_output_path.open("wb") as fp:
150 fp.write(output_data.tobytes())
151
152 output_folder = pathlib.Path(output_folder)
153 dump_c_header(network_input_path, output_folder/"input.h", "inputData", "input_data_sec", 4)
154 dump_c_header(network_output_path, output_folder/"output.h", "expectedOutputData", "expected_output_data_sec", 4)
155
156def dump_c_header(input_path, output_path, array_name, section, alignment, extra_data=""):
157 byte_array = []
158 with open(input_path, "rb") as fp:
159 byte_string = fp.read()
160 byte_array = [f"0x{format(byte, '02x')}" for byte in byte_string]
161
162 last = byte_array[-1]
163 byte_array = [byte + "," for byte in byte_array[:-1]] + [last]
164
165 byte_array = [" " + byte if idx % 12 == 0 else byte
166 for idx, byte in enumerate(byte_array)]
167
168 byte_array = [byte + "\n" if (idx + 1) % 12 == 0 else byte + " "
169 for idx, byte in enumerate(byte_array)]
170
171 with open(output_path, "w") as carray:
172 header = f"uint8_t {array_name}[] __attribute__((section(\"{section}\"), aligned({alignment}))) = {{\n"
173 carray.write(extra_data)
174 carray.write(header)
175 carray.write("".join(byte_array))
176 carray.write("\n};\n")
177
178def optimize_network(output_folder, network_path, accelerator_conf):
179 vela_cmd = ["vela",
180 network_path,
181 "--output-dir", output_folder,
182 "--accelerator-config", accelerator_conf]
183 res = run_cmd(vela_cmd)
184 optimized_model_path = output_folder/(network_path.stem + "_vela.tflite")
185 model_name = network_path.stem
Davide Grohmann5508acb2022-08-08 17:12:08 +0200186 dump_c_header(optimized_model_path, output_folder/"model.h", "networkModelData", "network_model_sec", 16, extra_data=f"""
187#include <stddef.h>
188
189const size_t tensorArenaSize = 2000000;
190const char* modelName = \"{model_name}\";
191""")
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100192
193def run_model(output_folder):
194 build_folder = output_folder/"model"/"build"
195 model_cmd = ["ctest", "-V", "-R", "^baremetal_custom$" ]
196 res = run_cmd(model_cmd, cwd=build_folder)
197
198def main():
199 target_mapping = {
200 "corstone-300": "ethos-u55-128"
201 }
202 parser = argparse.ArgumentParser()
203 parser.add_argument("-o", "--output-folder", type=pathlib.Path, default="output", help="Output folder for build and generated files")
204 parser.add_argument("--network-path", type=pathlib.Path, required=True, help="Path to .tflite file")
205 parser.add_argument("--target", choices=target_mapping, default="corstone-300", help=f"Configure target")
206 parser.add_argument("--toolchain", choices=["armclang", "arm-none-eabi-gcc"], default="armclang", help=f"Configure toolchain")
Nir Ekhauz3c505ca2021-06-06 14:57:50 +0300207 parser.add_argument("--memory_model", choices=["sram", "dram"], default="dram", help=f"Configure memory_model")
208 parser.add_argument("--memory_arena", choices=["sram", "dram"], default="sram", help=f"Configure memory_arena")
209 parser.add_argument("--pmu", type=int, action='append', help="PMU Event Counters")
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100210 parser.add_argument("--custom-input", type=pathlib.Path, help="Custom input to network")
211 parser.add_argument("--custom-output", type=pathlib.Path, help="Custom expected output data for network")
Nir Ekhauza58edd82021-10-04 12:21:17 +0300212 parser.add_argument("--ta-maxr", type=int, nargs=2, action='append', help="Max no. of pending reads")
213 parser.add_argument("--ta-maxw", type=int, nargs=2, action='append', help="Max no. of pending writes")
214 parser.add_argument("--ta-maxrw", type=int, nargs=2, action='append', help="Max no. of pending reads+writes")
215 parser.add_argument("--ta-rlatency", type=int, nargs=2, action='append', help="Minimum latency (clock cycles) from AVALID to RVALID")
216 parser.add_argument("--ta-wlatency", type=int, nargs=2, action='append', help="Minimum latency (clock cycles) from WVALID&WLAST to BVALID")
217 parser.add_argument("--ta-pulse_on", type=int, nargs=2, action='append', help="No. of cycles addresses let through (0-65535)")
218 parser.add_argument("--ta-pulse_off", type=int, nargs=2, action='append', help="No. of cycles addresses blocked (0-65535)")
219 parser.add_argument("--ta-bwcap", type=int, nargs=2, action='append', help="Max no. of 64-bit words transfered per pulse cycle 0=infinite")
220 parser.add_argument("--ta-perfctrl", type=int, nargs=2, action='append', help="selecting an event for event counter 0=default")
221 parser.add_argument("--ta-perfcnt", type=int, nargs=2, action='append', help="event counter")
222 parser.add_argument("--ta-mode", type=int, nargs=2, action='append', help="Max no. of pending reads")
223 parser.add_argument("--ta-histbin", type=int, nargs=2, action='append', help="Controlls which histogram bin (0-15) that should be accessed by HISTCNT")
224 parser.add_argument("--ta-histcnt", type=int, nargs=2, action='append', help="Read/write the selected histogram bin")
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100225
226 args = parser.parse_args()
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100227 args.output_folder.mkdir(exist_ok=True)
228
229 try:
230 optimize_network(args.output_folder, args.network_path, target_mapping[args.target])
231 generate_reference_data(args.output_folder, args.network_path, args.custom_input, args.custom_output)
Nir Ekhauza58edd82021-10-04 12:21:17 +0300232 build_core_platform(args.output_folder, args.target, args.toolchain, args.memory_model, args.memory_arena, args.pmu,
233 args.ta_maxr, args.ta_maxw, args.ta_maxrw, args.ta_rlatency, args.ta_wlatency,
234 args.ta_pulse_on, args.ta_pulse_off, args.ta_bwcap, args.ta_perfctrl, args.ta_perfcnt,
235 args.ta_mode, args.ta_histbin, args.ta_histcnt)
Jonathan Strandbergd2afc512021-03-19 10:31:18 +0100236 run_model(args.output_folder)
237 except subprocess.CalledProcessError as err:
238 print(f"Command: '{err.cmd}' failed", file=sys.stderr)
239 return 1
240 return 0
241
242if __name__ == "__main__":
243 sys.exit(main())