|
1 | 1 | name: Host validation |
2 | 2 |
|
3 | | -# Clone-local by design: no board access, compiler toolchains, or mutable |
4 | | -# third_party/fetch.sh network checkouts are required by this workflow. |
| 3 | +# Hardware-free by design: no board access or cross compiler is required. |
| 4 | +# Host CMSIS parity uses only immutable, full-SHA source checkouts. |
5 | 5 |
|
6 | 6 | on: |
7 | 7 | push: |
|
70 | 70 | run: >- |
71 | 71 | python -W error scripts/validate_accuracy.py |
72 | 72 | results/summary.json |
| 73 | +
|
| 74 | + - name: Build and run host CMSIS-NN parity |
| 75 | + shell: bash |
| 76 | + run: | |
| 77 | + set -euo pipefail |
| 78 | + host_root="$RUNNER_TEMP/tigris-host-validation" |
| 79 | + compiler_revision="$(python -c \ |
| 80 | + 'import json; print(json.load(open("core-versions.json"))["compiler"]["commit"])')" |
| 81 | + runtime_revision="$(python -c \ |
| 82 | + 'import json; print(json.load(open("core-versions.json"))["runtime"]["commit"])')" |
| 83 | + cmsis_revision="$(python -c \ |
| 84 | + 'import json; print(json.load(open("cortex-m-deployability/results/summary.json"))["provenance"]["common"]["dependencies"]["CMSIS-NN"])')" |
| 85 | +
|
| 86 | + checkout_revision() { |
| 87 | + local repository="$1" destination="$2" revision="$3" |
| 88 | + git init "$destination" |
| 89 | + git -C "$destination" remote add origin "$repository" |
| 90 | + git -C "$destination" fetch --depth=1 origin "$revision" |
| 91 | + git -C "$destination" checkout --detach FETCH_HEAD |
| 92 | + test "$(git -C "$destination" rev-parse HEAD)" = "$revision" |
| 93 | + } |
| 94 | + checkout_revision https://github.com/raws-labs/tigris.git \ |
| 95 | + "$host_root/compiler" "$compiler_revision" |
| 96 | + checkout_revision https://github.com/raws-labs/tigris-runtime.git \ |
| 97 | + "$host_root/runtime" "$runtime_revision" |
| 98 | + checkout_revision https://github.com/ARM-software/CMSIS-NN.git \ |
| 99 | + "$host_root/CMSIS-NN" "$cmsis_revision" |
| 100 | +
|
| 101 | + python -m pip install --no-deps "$host_root/compiler" |
| 102 | + HOST_ROOT="$host_root" python - <<'PY' |
| 103 | + import os |
| 104 | + from pathlib import Path |
| 105 | +
|
| 106 | + import numpy as np |
| 107 | + import onnx |
| 108 | + from onnx import TensorProto, helper, numpy_helper |
| 109 | +
|
| 110 | + root = Path(os.environ["HOST_ROOT"]) |
| 111 | + scalar_f32 = lambda name, value: numpy_helper.from_array( |
| 112 | + np.array([value], dtype=np.float32), name) |
| 113 | + scalar_i8 = lambda name: numpy_helper.from_array( |
| 114 | + np.array([0], dtype=np.int8), name) |
| 115 | + inputs = helper.make_tensor_value_info( |
| 116 | + "input", TensorProto.FLOAT, [1, 1, 4, 4]) |
| 117 | + outputs = helper.make_tensor_value_info( |
| 118 | + "output", TensorProto.FLOAT, [1, 1, 4, 4]) |
| 119 | + initializers = [ |
| 120 | + scalar_f32("input_scale", 0.25), scalar_i8("input_zp"), |
| 121 | + numpy_helper.from_array( |
| 122 | + np.array([[[[2]]]], dtype=np.int8), "weight"), |
| 123 | + scalar_f32("weight_scale", 0.25), scalar_i8("weight_zp"), |
| 124 | + scalar_f32("output_scale", 0.25), scalar_i8("output_zp"), |
| 125 | + ] |
| 126 | + nodes = [ |
| 127 | + helper.make_node( |
| 128 | + "QuantizeLinear", ["input", "input_scale", "input_zp"], |
| 129 | + ["input_q"]), |
| 130 | + helper.make_node( |
| 131 | + "DequantizeLinear", ["input_q", "input_scale", "input_zp"], |
| 132 | + ["input_dq"]), |
| 133 | + helper.make_node( |
| 134 | + "DequantizeLinear", ["weight", "weight_scale", "weight_zp"], |
| 135 | + ["weight_dq"]), |
| 136 | + helper.make_node("Conv", ["input_dq", "weight_dq"], ["raw"]), |
| 137 | + helper.make_node( |
| 138 | + "QuantizeLinear", ["raw", "output_scale", "output_zp"], |
| 139 | + ["output_q"]), |
| 140 | + helper.make_node( |
| 141 | + "DequantizeLinear", ["output_q", "output_scale", "output_zp"], |
| 142 | + ["output"]), |
| 143 | + ] |
| 144 | + model = helper.make_model( |
| 145 | + helper.make_graph( |
| 146 | + nodes, "host_cmsis_conv", [inputs], [outputs], initializers), |
| 147 | + opset_imports=[helper.make_opsetid("", 13)], |
| 148 | + ) |
| 149 | + model.ir_version = 8 |
| 150 | + onnx.checker.check_model(model) |
| 151 | + onnx.save(model, root / "host_cmsis_conv.onnx") |
| 152 | + PY |
| 153 | + tigris compile "$host_root/host_cmsis_conv.onnx" \ |
| 154 | + --mem 4K --output "$host_root/host_cmsis_conv.tgrs" |
| 155 | + cmake -S cortex-m-deployability -B "$host_root/build" \ |
| 156 | + -DCMAKE_BUILD_TYPE=Release \ |
| 157 | + -DTIGRIS_HOST_VALIDATION=ON \ |
| 158 | + -DTIGRIS_RUNTIME_ROOT="$host_root/runtime" \ |
| 159 | + -DCMSIS_NN_DIR="$host_root/CMSIS-NN" |
| 160 | + cmake --build "$host_root/build" \ |
| 161 | + --target host_cmsis_validate --parallel 2 |
| 162 | + "$host_root/build/host_cmsis_validate" \ |
| 163 | + "$host_root/host_cmsis_conv.tgrs" |
0 commit comments