Skip to content

Commit 2de7b7c

Browse files
committed
PR comments and Python 3.6 support
1 parent 3ed00ed commit 2de7b7c

File tree

17 files changed

+85
-82
lines changed

17 files changed

+85
-82
lines changed

apps/microtvm/arduino/example_project/src/model.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ tvm_workspace_t app_workspace;
3030

3131
// Blink code for debugging purposes
3232
void TVMPlatformAbort(tvm_crt_error_t error) {
33-
TVMLogf("TVMPlatformAbort: %08x\n", error);
33+
TVMLogf("TVMPlatformAbort: 0x%08x\n", error);
3434
for (;;) {
3535
#ifdef LED_BUILTIN
3636
digitalWrite(LED_BUILTIN, HIGH);

apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919

2020
/*!
21-
* \file tvm/runtime/crt/host/crt_config.h
2221
* \brief CRT configuration for the host-linked CRT.
2322
*/
2423
#ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_

apps/microtvm/arduino/host_driven/src/model_support.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
// Blink code for debugging purposes
2424
void TVMPlatformAbort(tvm_crt_error_t error) {
25-
TVMLogf("TVMPlatformAbort: %08x\n", error);
25+
TVMLogf("TVMPlatformAbort: 0x%08x\n", error);
2626
for (;;)
2727
;
2828
}

apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919

2020
/*!
21-
* \file tvm/runtime/crt/host/crt_config.h
2221
* \brief CRT configuration for the host-linked CRT.
2322
*/
2423
#ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_

apps/microtvm/arduino/template_project/microtvm_api_server.py

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,23 @@ def server_info_query(self, tvm_version):
133133
)
134134

135135
def _copy_project_files(self, api_server_dir, project_dir, project_type):
136+
"""Copies the files for project_type into project_dir.
137+
138+
Notes
139+
-----
140+
template_dir is NOT a project type, and that directory is never copied
141+
in this function. template_dir only holds this file and its unit tests,
142+
so this file is copied separately in generate_project.
143+
144+
"""
136145
project_types_folder = api_server_dir.parents[0]
137-
shutil.copytree(
138-
project_types_folder / project_type / "src", project_dir / "src", dirs_exist_ok=True
139-
)
146+
for item in (project_types_folder / project_type / "src").iterdir():
147+
dest = project_dir / "src" / item.name
148+
if item.is_dir():
149+
shutil.copytree(item, dest)
150+
else:
151+
shutil.copy2(item, dest)
152+
140153
# Arduino requires the .ino file have the same filename as its containing folder
141154
shutil.copy2(
142155
project_types_folder / project_type / "project.ino",
@@ -146,7 +159,6 @@ def _copy_project_files(self, api_server_dir, project_dir, project_type):
146159
CRT_COPY_ITEMS = ("include", "src")
147160

148161
def _copy_standalone_crt(self, source_dir, standalone_crt_dir):
149-
# Copy over the standalone_crt directory
150162
output_crt_dir = source_dir / "standalone_crt"
151163
for item in self.CRT_COPY_ITEMS:
152164
src_path = os.path.join(standalone_crt_dir, item)
@@ -200,9 +212,9 @@ def _template_model_header(self, source_dir, metadata):
200212
with open(source_dir / "model.h", "r") as f:
201213
model_h_template = Template(f.read())
202214

203-
# The structure of the "memory" key depends on the style -
204-
# only style="full-model" works with AOT, so we'll check that
205-
assert metadata["style"] == "full-model"
215+
assert (
216+
metadata["style"] == "full-model"
217+
), "when generating AOT, expect only full-model Model Library Format"
206218

207219
template_values = {
208220
"workspace_size_bytes": metadata["memory"]["functions"]["main"][0][
@@ -225,20 +237,20 @@ def _change_cpp_file_extensions(self, source_dir):
225237
for filename in source_dir.rglob(f"*.inc"):
226238
filename.rename(filename.with_suffix(".h"))
227239

228-
"""Arduino only supports includes relative to the top-level project, so this
229-
finds each time we #include a file and changes the path to be relative to the
230-
top-level project.ino file. For example, the line:
231-
232-
#include <tvm/runtime/crt/platform.h>
233-
234-
Might be changed to (depending on the source file's location):
240+
def _convert_includes(self, project_dir, source_dir):
241+
"""Changes all #include statements in project_dir to be relevant to their
242+
containing file's location.
235243
236-
#include "../../../../include/tvm/runtime/crt/platform.h"
244+
Arduino only supports includes relative to a file's location, so this
245+
function finds each time we #include a file and changes the path to
246+
be relative to the file location. Does not do this for standard C
247+
libraries. Also changes angle brackets syntax to double quotes syntax.
237248
238-
We also need to leave standard library includes as-is.
239-
"""
249+
See Also
250+
-----
251+
https://www.arduino.cc/reference/en/language/structure/further-syntax/include/
240252
241-
def _convert_includes(self, project_dir, source_dir):
253+
"""
242254
for ext in ("c", "h", "cpp"):
243255
for filename in source_dir.rglob(f"*.{ext}"):
244256
with filename.open() as file:
@@ -263,27 +275,19 @@ def _convert_includes(self, project_dir, source_dir):
263275
# be added in the future.
264276
POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/", "src/standalone_crt/crt_config/"]
265277

266-
"""Takes a single #include path, and returns the new location
267-
it should point to (as described above). For example, one of the
268-
includes for "src/standalone_crt/src/runtime/crt/common/ndarray.c" is:
269-
270-
#include <tvm/runtime/crt/platform.h>
271-
272-
For that line, _convert_includes might call _find_modified_include_path
273-
with the arguments:
274-
275-
project_dir = "/path/to/project/dir"
276-
file_path = "/path/to/project/dir/src/standalone_crt/src/runtime/crt/common/ndarray.c"
277-
include_path = "tvm/runtime/crt/platform.h"
278-
279-
Given these arguments, _find_modified_include_path should return:
280-
281-
"../../../../../../src/standalone_crt/include/tvm/runtime/crt/platform.h"
282-
283-
See unit test in ./tests/test_arduino_microtvm_api_server.py
284-
"""
285-
286278
def _find_modified_include_path(self, project_dir, file_path, include_path):
279+
"""Takes a single #include path, and returns the location it should point to.
280+
281+
Examples
282+
--------
283+
>>> _find_modified_include_path(
284+
... "/path/to/project/dir"
285+
... "/path/to/project/dir/src/standalone_crt/src/runtime/crt/common/ndarray.c"
286+
... "tvm/runtime/crt/platform.h"
287+
... )
288+
"../../../../../../src/standalone_crt/include/tvm/runtime/crt/platform.h"
289+
290+
"""
287291
if include_path.endswith(".inc"):
288292
include_path = re.sub(r"\.[a-z]+$", ".h", include_path)
289293

@@ -314,8 +318,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec
314318
source_dir = project_dir / "src"
315319
source_dir.mkdir()
316320

317-
# Copies files from the template folder to project_dir. model.h is copied here,
318-
# but will also need to be templated later.
321+
# Copies files from the template folder to project_dir
319322
shutil.copy2(API_SERVER_DIR / "microtvm_api_server.py", project_dir)
320323
self._copy_project_files(API_SERVER_DIR, project_dir, options["project_type"])
321324

@@ -359,17 +362,24 @@ def build(self, options):
359362
# Specify project to compile
360363
subprocess.run(compile_cmd)
361364

362-
"""We run the command `arduino-cli board list`, which produces
363-
outputs of the form:
364-
365-
Port Type Board Name FQBN Core
366-
/dev/ttyS4 Serial Port Unknown
367-
/dev/ttyUSB0 Serial Port (USB) Spresense SPRESENSE:spresense:spresense SPRESENSE:spresense
368-
"""
369-
370365
BOARD_LIST_HEADERS = ("Port", "Type", "Board Name", "FQBN", "Core")
371366

372367
def _parse_boards_tabular_str(self, tabular_str):
368+
"""Parses the tabular output from `arduino-cli board list` into a 2D array
369+
370+
Examples
371+
--------
372+
>>> list(_parse_boards_tabular_str(bytes(
373+
... "Port Type Board Name FQBN Core \n"
374+
... "/dev/ttyS4 Serial Port Unknown \n"
375+
... "/dev/ttyUSB0 Serial Port (USB) Spresense SPRESENSE:spresense:spresense SPRESENSE:spresense\n"
376+
... "\n",
377+
... "utf-8")))
378+
[['/dev/ttys4', 'Serial Port', 'Unknown', '', ''], ['/dev/ttyUSB0', 'Serial Port (USB)',
379+
'Spresense', 'SPRESENSE:spresense:spresense', 'SPRESENSE:spresense']]
380+
381+
"""
382+
373383
str_rows = tabular_str.split("\n")[:-2]
374384
header = str_rows[0]
375385
indices = [header.index(h) for h in self.BOARD_LIST_HEADERS] + [len(header)]
@@ -387,7 +397,7 @@ def _parse_boards_tabular_str(self, tabular_str):
387397

388398
def _auto_detect_port(self, options):
389399
list_cmd = [options["arduino_cli_cmd"], "board", "list"]
390-
list_cmd_output = subprocess.run(list_cmd, capture_output=True).stdout.decode("utf-8")
400+
list_cmd_output = subprocess.run(list_cmd, stdout=subprocess.PIPE).stdout.decode("utf-8")
391401

392402
desired_fqbn = self._get_fqbn(options)
393403
for line in self._parse_boards_tabular_str(list_cmd_output):

apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
import subprocess
1819
import sys
1920
from pathlib import Path
2021
from unittest import mock
@@ -64,7 +65,7 @@ def test_find_modified_include_path(self, mock_pathlib_path):
6465

6566
BOARD_CONNECTED_OUTPUT = bytes(
6667
"Port Type Board Name FQBN Core \n"
67-
"/dev/ttyACM1 Serial Port (USB) Wrong Arduino arduino:mbed_nano:nano33 arduino:mbed_nano\n"
68+
"/dev/ttyACM1 Serial Port (USB) Wrong Arduino arduino:mbed_nano:nano33 arduino:mbed_nano\n"
6869
"/dev/ttyACM0 Serial Port (USB) Arduino Nano 33 BLE arduino:mbed_nano:nano33ble arduino:mbed_nano\n"
6970
"/dev/ttyS4 Serial Port Unknown \n"
7071
"\n",
@@ -77,37 +78,31 @@ def test_find_modified_include_path(self, mock_pathlib_path):
7778
"utf-8",
7879
)
7980

80-
@mock.patch("subprocess.check_output")
81-
def test_auto_detect_port(self, mock_subprocess_check_output):
81+
@mock.patch("subprocess.run")
82+
def test_auto_detect_port(self, mock_subprocess_run):
8283
process_mock = mock.Mock()
8384
handler = microtvm_api_server.Handler()
8485

8586
# Test it returns the correct port when a board is connected
86-
mock_subprocess_check_output.return_value = self.BOARD_CONNECTED_OUTPUT
87+
mock_subprocess_run.return_value.stdout = self.BOARD_CONNECTED_OUTPUT
8788
detected_port = handler._auto_detect_port(self.DEFAULT_OPTIONS)
8889
assert detected_port == "/dev/ttyACM0"
8990

9091
# Test it raises an exception when no board is connected
91-
mock_subprocess_check_output.return_value = self.BOARD_DISCONNECTED_OUTPUT
92+
mock_subprocess_run.return_value.stdout = self.BOARD_DISCONNECTED_OUTPUT
9293
with pytest.raises(microtvm_api_server.BoardAutodetectFailed):
9394
handler._auto_detect_port(self.DEFAULT_OPTIONS)
9495

95-
@mock.patch("subprocess.check_call")
96-
def test_flash(self, mock_subprocess_check_call):
96+
@mock.patch("subprocess.run")
97+
def test_flash(self, mock_subprocess_run):
9798
handler = microtvm_api_server.Handler()
9899
handler._port = "/dev/ttyACM0"
99100

100-
# Test no exception thrown when code 0 returned
101-
mock_subprocess_check_call.return_value = 0
101+
# Test no exception thrown when command works
102102
handler.flash(self.DEFAULT_OPTIONS)
103-
mock_subprocess_check_call.assert_called_once()
103+
mock_subprocess_run.assert_called_once()
104104

105-
# Test InvalidPortException raised when port incorrect
106-
mock_subprocess_check_call.return_value = 2
107-
with pytest.raises(microtvm_api_server.InvalidPortException):
108-
handler.flash(self.DEFAULT_OPTIONS)
109-
110-
# Test SketchUploadException raised for other issues
111-
mock_subprocess_check_call.return_value = 1
112-
with pytest.raises(microtvm_api_server.SketchUploadException):
105+
# Test exception raised when `arduino-cli upload` returns error code
106+
mock_subprocess_run.side_effect = subprocess.CalledProcessError(2, [])
107+
with pytest.raises(subprocess.CalledProcessError):
113108
handler.flash(self.DEFAULT_OPTIONS)

tests/micro/arduino/test_arduino_rpc_server.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,17 @@ def test_onnx(platform, arduino_cli_cmd, tvm_debug, workspace_dir):
179179

180180
# Load test images.
181181
this_dir = pathlib.Path(__file__).parent
182-
testdata_dir = this_dir.parent / "testdata"
183-
digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28))
182+
mnist_testdata = this_dir.parent / "testdata" / "mnist"
183+
digit_2 = Image.open(mnist_testdata / "digit-2.jpg").resize((28, 28))
184184
digit_2 = np.asarray(digit_2).astype("float32")
185185
digit_2 = np.expand_dims(digit_2, axis=0)
186186

187-
digit_9 = Image.open(testdata_dir / "digit-9.jpg").resize((28, 28))
187+
digit_9 = Image.open(mnist_testdata / "digit-9.jpg").resize((28, 28))
188188
digit_9 = np.asarray(digit_9).astype("float32")
189189
digit_9 = np.expand_dims(digit_9, axis=0)
190190

191191
# Load ONNX model and convert to Relay.
192-
onnx_model = onnx.load(testdata_dir / "mnist-8.onnx")
192+
onnx_model = onnx.load(mnist_testdata / "mnist-8.onnx")
193193
shape = {"Input3": (1, 1, 28, 28)}
194194
relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True)
195195
relay_mod = relay.transform.DynamicToStatic()(relay_mod)

tests/micro/arduino/test_arduino_workflow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def project(platform, arduino_cli_cmd, tvm_debug, workspace_dir):
7070
model, arduino_board = conftest.PLATFORMS[platform]
7171
build_config = {"debug": tvm_debug}
7272

73-
with open(this_dir.parent / "testdata" / "yes_no.tflite", "rb") as f:
73+
with open(this_dir.parent / "testdata" / "kws" / "yes_no.tflite", "rb") as f:
7474
tflite_model_buf = f.read()
7575

7676
# TFLite.Model.Model has changed to TFLite.Model from 1.14 to 2.1
@@ -141,15 +141,15 @@ def test_import_rerouting(project_dir, project):
141141
@pytest.fixture(scope="module")
142142
def modified_project(project_dir, project):
143143
this_dir = pathlib.Path(__file__).parent
144-
micro_testdata_dir = this_dir.parent / "testdata"
144+
kws_testdata_dir = this_dir.parent / "testdata" / "kws"
145145
arduino_testdata_dir = this_dir / "testdata"
146146

147147
shutil.copy2(arduino_testdata_dir / "project.ino", project_dir / "project.ino")
148148

149149
project_data_dir = project_dir / "src" / "data"
150150
project_data_dir.mkdir()
151151
for sample in ["yes.c", "no.c", "silence.c", "unknown.c"]:
152-
shutil.copy2(micro_testdata_dir / sample, project_data_dir / sample)
152+
shutil.copy2(kws_testdata_dir / sample, project_data_dir / sample)
153153

154154
return project
155155

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)