├── ml4f ├── Makefile ├── models ├── gestures.tfjsmodel.weights.bin ├── gestures.tfjsmodel.json ├── button.json ├── microbit_acc_gestures.json └── accel.json ├── .clang-format ├── .gitignore ├── src ├── pxtpackage.d.ts ├── ml4f.ts ├── runtime.ts ├── tfi.ts ├── library.ts ├── util.ts ├── float16.ts ├── driver.ts ├── testing.ts └── thumb.ts ├── c ├── compile.sh ├── q.js ├── asm.sh ├── asm.js └── tt.c ├── pxt ├── build-gh-pages.sh ├── index.html ├── tsconfig.json └── extension.ts ├── CODE_OF_CONDUCT.md ├── index.html ├── .github └── workflows │ ├── build.yml │ └── ghpages.yml ├── cli ├── tsconfig.json └── src │ └── cli.ts ├── package.json ├── tsconfig.json ├── .vscode └── launch.json ├── LICENSE ├── sample ├── ml4f.h └── ml4f.c ├── rollup.config.ts ├── SECURITY.md └── README.md /ml4f: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('./built/cli.cjs').mainCli() 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | yarn prepare 3 | 4 | pub: 5 | yarn version --minor 6 | npm publish 7 | -------------------------------------------------------------------------------- /models/gestures.tfjsmodel.weights.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ml4f/HEAD/models/gestures.tfjsmodel.weights.bin -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | ColumnLimit: 100 5 | AllowShortFunctionsOnASingleLine: Inline 6 | SortIncludes: false 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | built/ 4 | tmp 5 | *.o 6 | c/*.s 7 | c/*.elf 8 | c/foo.ts 9 | c/*.diff 10 | c/*.dump 11 | c/*.hex 12 | c/*.bin 13 | tmp 14 | *.tgz 15 | -------------------------------------------------------------------------------- /src/pxtpackage.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace pxt { 2 | interface Map { 3 | [index: string]: T; 4 | } 5 | } 6 | 7 | interface SMap { 8 | [index: string]: T; 9 | } 10 | -------------------------------------------------------------------------------- /src/ml4f.ts: -------------------------------------------------------------------------------- 1 | export * from "./assembler" 2 | export * from "./compiler" 3 | export * from "./library" 4 | export * from "./driver" 5 | export * from "./util" 6 | export * from "./testing" 7 | export * from "./float16" 8 | -------------------------------------------------------------------------------- /c/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x 3 | FL="-mthumb -mcpu=cortex-m4 -std=c99 -mfpu=fpv4-sp-d16 -O3 -mfloat-abi=hard" 4 | arm-none-eabi-gcc $FL -S tt.c 5 | arm-none-eabi-gcc $FL -O3 -c tt.c 6 | arm-none-eabi-gcc $FL tt.o -o tt.elf -lm 7 | -------------------------------------------------------------------------------- /pxt/build-gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | rm -rf built/gh-pages 6 | mkdir -p built/gh-pages 7 | sed -e 's@src="[^"]*/@src="./@g' < pxt/index.html > built/gh-pages/index.html 8 | cp node_modules/@tensorflow/tfjs/dist/tf.es2017.js built/ml4f.js built/pxtml4f.js built/gh-pages/ 9 | touch built/gh-pages/.nojekyll 10 | exit 0 11 | -------------------------------------------------------------------------------- /c/q.js: -------------------------------------------------------------------------------- 1 | const mem = [1, 2, 3, 40] 2 | function softmax(ptr, len) { 3 | let max = mem[ptr] 4 | for (let i = 1; i < len; ++i) 5 | max = Math.max(mem[ptr + i], max) 6 | let sum = 0 7 | for (let i = 0; i < len; ++i) 8 | sum += (mem[ptr + i] = Math.exp(mem[ptr + i] - max)) 9 | for (let i = 0; i < len; ++i) 10 | mem[ptr + i] /= sum 11 | } 12 | 13 | 14 | softmax(0, 4) 15 | console.log(mem) 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /c/asm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x 3 | FL="-masm-syntax-unified -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -O3 -mfloat-abi=hard" 4 | cp ../tmp/built/model.asm foo.s 5 | arm-none-eabi-gcc -W -Wall $FL -c foo.s 6 | arm-none-eabi-objcopy -O binary foo.o foo.bin 7 | node asm 8 | hexdump -C foo.bin > foo.hex 9 | hexdump -C foo2.bin > foo2.hex 10 | diff -u foo.hex foo2.hex > foo.diff 11 | arm-none-eabi-objdump -d foo.o > foo.dump 12 | arm-none-eabi-objdump -d foo2.o > foo2.dump 13 | diff -u foo.dump foo2.dump > foo-dump.diff 14 | -------------------------------------------------------------------------------- /pxt/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ML4F 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ML4F 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /c/asm.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs") 2 | 3 | let mbuf = "const modelBuf = hex\`" 4 | const objbin = fs.readFileSync("foo.bin") 5 | mbuf += objbin.toString("hex").replace(/.{0,128}/g, s => s + "\n") 6 | mbuf += "\`\n" 7 | fs.writeFileSync("foo.ts", mbuf) 8 | //const obj2bin = Buffer.from(fs.readFileSync("foo.s", "utf8").replace(/[^]*BUF: /, ""), "hex") 9 | const obj2bin = fs.readFileSync("../tmp/built/model.bin") 10 | fs.writeFileSync("foo2.bin", obj2bin) 11 | 12 | const elfobj = fs.readFileSync("foo.o") 13 | const idx = elfobj.indexOf(objbin.slice(0, 8)) 14 | if (idx <= 0) throw "blah" 15 | elfobj.set(obj2bin.slice(0,objbin.length), idx) 16 | fs.writeFileSync("foo2.o", elfobj) 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | permissions: 8 | contents: write 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/cache@v3 15 | with: 16 | path: '**/node_modules' 17 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 18 | - run: yarn install 19 | - run: yarn test 20 | - run: npm pack 21 | - name: Release 22 | uses: softprops/action-gh-release@v1 23 | if: startsWith(github.ref, 'refs/tags/') 24 | with: 25 | body: "Install with: npm install -g ml4f-*.tgz" 26 | files: ml4f-*.tgz 27 | -------------------------------------------------------------------------------- /cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": ["es2015", "es2016", "es2017"], 7 | "strict": false, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "preserveConstEnums": true, 14 | "resolveJsonModule": true, 15 | "skipLibCheck": true, 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "noImplicitReturns": true, 19 | "noEmit": true, 20 | "outDir": "../built/cli", 21 | "newLine": "LF", 22 | "typeRoots": [ 23 | "../node_modules/@types" 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": ["es2015", "es2016", "es2017", "DOM"], 7 | "strict": false, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "preserveConstEnums": true, 14 | "resolveJsonModule": true, 15 | "skipLibCheck": true, 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "noImplicitReturns": true, 19 | "outDir": "../built", 20 | "noEmit": true, 21 | "newLine": "LF", 22 | "typeRoots": [ 23 | "../node_modules/@types" 24 | ] 25 | }, 26 | "include": [ "." ] 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ml4f", 3 | "version": "1.10.1", 4 | "description": "", 5 | "scripts": { 6 | "prepare": "node build", 7 | "test": "node ml4f --self-test", 8 | "watch": "node build --watch" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "bin": "./ml4f", 13 | "types": "./built/ml4f.d.ts", 14 | "main": "./built/ml4f.cjs", 15 | "browser": "./built/ml4f.js", 16 | "module": "./built/ml4f.mjs", 17 | "files": [ 18 | "built/ml4f.*js", 19 | "built/pxtml4f.*js", 20 | "built/ml4f.d.ts", 21 | "built/cli.cjs" 22 | ], 23 | "devDependencies": { 24 | "esbuild": "^0.15.14", 25 | "typescript": "^4.3.2" 26 | }, 27 | "dependencies": { 28 | "@tensorflow/tfjs": "^4.10.0", 29 | "@tensorflow/tfjs-vis": "^1.5.1", 30 | "commander": "^6.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": ["es2015", "es2016", "es2017"], 7 | "strict": false, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "preserveConstEnums": true, 14 | "resolveJsonModule": true, 15 | "skipLibCheck": true, 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "noImplicitReturns": true, 19 | "outDir": "built", 20 | "newLine": "LF", 21 | "emitDeclarationOnly": true, 22 | "outFile": "built/ml4f", 23 | "typeRoots": [ 24 | "node_modules/@types" 25 | ] 26 | }, 27 | "include": [ "src" ] 28 | } 29 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}/built/cli.cjs", 15 | "args": ["--debug", "tmp/models/1/test-model.json"], 16 | // "preLaunchTask": "tsc: build - tsconfig.json", 17 | "outFiles": [ 18 | "${workspaceFolder}/built/**/*.js" 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /src/runtime.ts: -------------------------------------------------------------------------------- 1 | import { float16AsUintToFloat } from "./float16" 2 | 3 | export function mkRuntime(mem: Float32Array) { 4 | return { 5 | softmax: (ptr: number, len: number) => { 6 | let max = mem[ptr] 7 | for (let i = 1; i < len; ++i) 8 | max = Math.max(mem[ptr + i], max) 9 | let sum = 0 10 | for (let i = 0; i < len; ++i) 11 | sum += (mem[ptr + i] = Math.exp(mem[ptr + i] - max)) 12 | for (let i = 0; i < len; ++i) 13 | mem[ptr + i] /= sum 14 | }, 15 | f32: (v: number) => { 16 | const arr = new Float32Array(1) 17 | arr[0] = v 18 | return arr[0] 19 | }, 20 | vcvtb_f32_f16: (v: number) => 21 | float16AsUintToFloat(v & 0xffff), 22 | vcvtt_f32_f16: (v: number) => 23 | float16AsUintToFloat((v >> 16) & 0xffff), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: GH-Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | permissions: 11 | contents: write 12 | 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [14.x] 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - uses: actions/cache@v2 26 | with: 27 | path: '**/node_modules' 28 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 29 | - run: yarn install 30 | - run: yarn test 31 | - run: sh pxt/build-gh-pages.sh 32 | - name: Deploy 🚀 33 | uses: JamesIves/github-pages-deploy-action@4.1.4 34 | with: 35 | branch: gh-pages 36 | folder: built/gh-pages 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /sample/ml4f.h: -------------------------------------------------------------------------------- 1 | #ifndef __ML4F_H 2 | #define __ML4F_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define ML4F_TYPE_FLOAT32 1 11 | 12 | #define ML4F_MAGIC0 0x30470f62 13 | #define ML4F_MAGIC1 0x46344c4d // "ML4F" 14 | 15 | // All values are little endian. 16 | // All offsets and sizes are in bytes. 17 | 18 | typedef struct ml4f_header { 19 | uint32_t magic0; 20 | uint32_t magic1; 21 | uint32_t header_size; 22 | uint32_t object_size; 23 | uint32_t weights_offset; 24 | uint32_t test_input_offset; 25 | uint32_t test_output_offset; 26 | uint32_t arena_bytes; 27 | uint32_t input_offset; 28 | uint32_t input_type; // always ML4F_TYPE_FLOAT32 29 | uint32_t output_offset; 30 | uint32_t output_type; // always ML4F_TYPE_FLOAT32 31 | uint32_t reserved[4]; 32 | // Shapes are 0-terminated, and are given in elements (not bytes). 33 | // Input shape is followed by output shape. 34 | uint32_t input_shape[0]; 35 | } ml4f_header_t; 36 | 37 | int ml4f_is_valid_header(const ml4f_header_t *header); 38 | int ml4f_invoke(const ml4f_header_t *model, uint8_t *arena); 39 | int ml4f_test(const ml4f_header_t *model, uint8_t *arena); 40 | const uint32_t *ml4f_input_shape(const ml4f_header_t *model); 41 | const uint32_t *ml4f_output_shape(const ml4f_header_t *model); 42 | uint32_t ml4f_shape_elements(const uint32_t *shape); 43 | uint32_t ml4f_shape_size(const uint32_t *shape, uint32_t type); 44 | int ml4f_argmax(float *data, uint32_t size); 45 | 46 | int ml4f_full_invoke(const ml4f_header_t *model, const float *input, float *output); 47 | int ml4f_full_invoke_argmax(const ml4f_header_t *model, const float *input); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/tfi.ts: -------------------------------------------------------------------------------- 1 | // These types are normally defined in TF.js, but they are not exported for some reason. 2 | 3 | import { DataType } from "@tensorflow/tfjs"; 4 | 5 | export type ActivationIdentifier = 'elu' | 'hardSigmoid' | 'linear' | 'relu' | 'relu6' | 6 | 'selu' | 'sigmoid' | 'softmax' | 'softplus' | 'softsign' | 'tanh'; 7 | 8 | export type ConstraintIdentifier = 'maxNorm' | 'minMaxNorm' | 'nonNeg' | 'unitNorm' | string; 9 | export type RegularizerIdentifier = 'l1l2' | string; 10 | 11 | export declare interface LayerArgs { 12 | dtype?: DataType; 13 | } 14 | 15 | export declare interface DenseLayerArgs extends LayerArgs { 16 | activation?: ActivationIdentifier; 17 | units: number; 18 | useBias?: boolean; 19 | } 20 | 21 | export declare interface BatchNormalizationLayerArgs extends LayerArgs { 22 | axis?: number; 23 | epsilon?: number; 24 | momentum?: number; 25 | center?: boolean; 26 | scale?: boolean; 27 | } 28 | 29 | export type PaddingMode = 'valid' | 'same' | 'causal'; 30 | 31 | export type DataFormat = 'channelsFirst' | 'channelsLast'; 32 | 33 | export declare interface BaseConvLayerArgs extends LayerArgs { 34 | kernelSize: number[]; 35 | strides: number[]; 36 | padding: PaddingMode; 37 | dataFormat: DataFormat; 38 | 39 | dilationRate?: number | [number] | [number, number] | [number, number, number]; 40 | activation?: ActivationIdentifier; 41 | useBias?: boolean; 42 | } 43 | 44 | export declare interface ConvLayerArgs extends BaseConvLayerArgs { 45 | filters: number; 46 | } 47 | 48 | export declare interface DepthwiseConvLayerArgs extends BaseConvLayerArgs { 49 | depthMultiplier: number; 50 | } 51 | 52 | export declare interface Pooling2DLayerArgs extends LayerArgs { 53 | poolSize: [number, number]; 54 | strides: [number, number]; 55 | padding: PaddingMode; 56 | dataFormat: DataFormat; 57 | } 58 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import commonjs from "rollup-plugin-commonjs" 2 | import typescript from "rollup-plugin-typescript2" 3 | import json from "rollup-plugin-json" 4 | import dts from "rollup-plugin-dts" 5 | 6 | function tsbuild(name, src, tsconfig) { 7 | return ({ 8 | external: ["@tensorflow/tfjs"], 9 | input: src, 10 | plugins: [ 11 | json(), 12 | typescript({ 13 | tsconfig, 14 | tsconfigOverride: { 15 | compilerOptions: { 16 | module: "ES2015", 17 | }, 18 | }, 19 | }), 20 | commonjs({ 21 | include: "node_modules/**", 22 | }), 23 | ], 24 | output: [ 25 | { 26 | extend: true, 27 | file: `built/${name}.js`, 28 | format: "umd", 29 | name, 30 | globals: { 31 | "@tensorflow/tfjs": "tf", 32 | }, 33 | sourcemap: true, 34 | }, 35 | { 36 | extend: true, 37 | file: `built/${name}.cjs`, 38 | format: "cjs", 39 | name, 40 | globals: { 41 | "@tensorflow/tfjs": "tf", 42 | }, 43 | sourcemap: true, 44 | }, 45 | ], 46 | onwarn: warning => { 47 | const ignoreWarnings = ["EVAL"] 48 | if (ignoreWarnings.indexOf(warning.code) >= 0) return 49 | 50 | console.warn(warning.code, warning.message) 51 | }, 52 | }) 53 | } 54 | 55 | export default [ 56 | tsbuild("ml4f", "src/main.ts", undefined), 57 | { 58 | input: "./built/main.d.ts", 59 | output: [{ file: "built/ml4f.d.ts", format: "es" }], 60 | plugins: [dts()], 61 | }, 62 | tsbuild("pxtml4f", "pxt/extension.ts", "pxt/tsconfig.json"), 63 | ] 64 | -------------------------------------------------------------------------------- /src/library.ts: -------------------------------------------------------------------------------- 1 | export const asmDeps: SMap = { 2 | 'softmax': ['expf_asm'] 3 | } 4 | 5 | export const asmFns: SMap = { 6 | "expf_asm": ` 7 | // based on https://stackoverflow.com/questions/29381117 8 | expf_asm: 9 | vldr.32 s15, .L10 10 | vcmpe.f32 s0, s15 11 | vmrs APSR_nzcv, FPSCR 12 | bmi .L5 13 | vldr.32 s15, .L10+4 14 | vcmpe.f32 s0, s15 15 | vmrs APSR_nzcv, FPSCR 16 | bgt .L9 17 | vldr.32 s15, .L10+8 18 | vldr.32 s9, .L10+12 19 | vldr.32 s6, .L10+16 20 | vldr.32 s7, .L10+20 21 | vldr.32 s10, .L10+24 22 | vldr.32 s8, .L10+28 23 | vldr.32 s11, .L10+32 24 | vldr.32 s12, .L10+36 25 | vldr.32 s13, .L10+40 26 | vmul.f32 s15, s0, s15 27 | vmov.f32 s14, #1.0 28 | vadd.f32 s15, s15, s9 29 | vsub.f32 s15, s15, s9 30 | vfma.f32 s0, s15, s6 31 | vcvt.s32.f32 s9, s15 32 | vfma.f32 s0, s15, s7 33 | vmov.f32 s15, s10 34 | vfma.f32 s15, s8, s0 35 | vmov r3, s9 // int 36 | vfma.f32 s11, s15, s0 37 | vfma.f32 s12, s11, s0 38 | vfma.f32 s13, s12, s0 39 | vmov.f32 s15, s13 40 | vmov.f32 s13, s14 41 | vfma.f32 s13, s15, s0 42 | vfma.f32 s14, s13, s0 43 | vmov r2, s14 // int 44 | add r3, r2, r3, lsl #23 45 | vmov s0, r3 // int 46 | bx lr 47 | .L9: 48 | vldr.32 s15, .L10+44 49 | vmov.f32 s14, #1.0 50 | vdiv.f32 s0, s14, s15 51 | bx lr 52 | .L5: 53 | vldr.32 s0, .L10+44 54 | bx lr 55 | .L11: 56 | .align 2 57 | .L10: 58 | .word 3265921024 59 | .word 1118699520 60 | .word 1069066811 61 | .word 1262485504 62 | .word 3207688704 63 | .word 3049242254 64 | .word 1007234926 65 | .word 984915968 66 | .word 1026207149 67 | .word 1042983464 68 | .word 1056964603 69 | .word 0 70 | `, 71 | 72 | "softmax": ` 73 | softmax: 74 | cmp r1, #1 75 | push {r3, r4, r5, lr} 76 | vldr.32 s5, [r0] 77 | bls .L13 78 | adds r3, r0, #4 79 | add r2, r0, r1, lsl #2 80 | .L16: 81 | vldmia.32 r3!, {s15} 82 | vcmp.f32 s15, s5 83 | vmrs APSR_nzcv, FPSCR 84 | it gt 85 | vmovgt.f32 s5, s15 86 | cmp r2, r3 87 | bne .L16 88 | .L17: 89 | movs r4, #0 90 | vmov s4, r4 91 | mov r5, r0 92 | .L19: 93 | vldr.32 s0, [r5] 94 | vsub.f32 s0, s0, s5 95 | bl expf_asm 96 | adds r4, #1 97 | cmp r1, r4 98 | vadd.f32 s4, s4, s0 99 | vstmia.32 r5!, {s0} 100 | bhi .L19 101 | movs r3, #0 102 | .L20: 103 | vldr.32 s14, [r0] 104 | vdiv.f32 s15, s14, s4 105 | adds r3, #1 106 | cmp r1, r3 107 | vstmia.32 r0!, {s15} 108 | bhi .L20 109 | pop {r3, r4, r5, pc} 110 | .L13: 111 | cmp r1, #0 112 | bne .L17 113 | pop {r3, r4, r5, pc} 114 | ` 115 | 116 | } -------------------------------------------------------------------------------- /c/tt.c: -------------------------------------------------------------------------------- 1 | // from 2 | // https://stackoverflow.com/questions/29381117/which-exponentiation-algorithms-do-cpu-programming-languages-use 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Like rintf(), but -0.0f -> +0.0f, and |a| must be < 2**22 */ 9 | static inline float quick_and_dirty_rintf(float a) { 10 | float cvt_magic = 0x1.800000p+23f; 11 | return (a + cvt_magic) - cvt_magic; 12 | } 13 | 14 | /* Approximate exp(a) on the interval [log(sqrt(0.5)), log(sqrt(2.0))]. */ 15 | static inline float expf_poly(float a) { 16 | float r; 17 | 18 | r = 0x1.694000p-10f; // 1.37805939e-3 19 | r = fmaf(r, a, 0x1.125edcp-07f); // 8.37312452e-3 20 | r = fmaf(r, a, 0x1.555b5ap-05f); // 4.16695364e-2 21 | r = fmaf(r, a, 0x1.555450p-03f); // 1.66664720e-1 22 | r = fmaf(r, a, 0x1.fffff6p-02f); // 4.99999851e-1 23 | r = fmaf(r, a, 0x1.000000p+00f); // 1.00000000e+0 24 | r = fmaf(r, a, 0x1.000000p+00f); // 1.00000000e+0 25 | return r; 26 | } 27 | 28 | typedef union { 29 | float f; 30 | uint32_t i; 31 | } float_repr_t; 32 | 33 | /* Compute exponential base e. Maximum ulp error = 0.86565 */ 34 | __attribute__((noinline)) 35 | float my_expf(float a) { 36 | float r, t; 37 | float_repr_t x; 38 | int i; 39 | 40 | // this used to be -102.0 - 102.0, which seems to overflow 41 | if (a < -85.0f) 42 | return 0.0f; 43 | if (a > 87.0f) 44 | return 1.0f / 0.0f; // +INF 45 | 46 | t = a * 0x1.715476p+0f; // 1/log(2); 1.442695 47 | t = quick_and_dirty_rintf(t); 48 | i = (int)t; 49 | r = fmaf(t, -0x1.62e400p-01f, a); // log_2_hi; -6.93145752e-1 50 | r = fmaf(t, -0x1.7f7d1cp-20f, r); // log_2_lo; -1.42860677e-6 51 | x.f = expf_poly(r); 52 | // printf("%x %d\n", x.i,i); 53 | x.i += (i << 23); 54 | // x.f = ldexpf(x.f, i); 55 | // printf("%x\n", x.i); 56 | // printf("expf(%f) -> %f vs %f\n", a,x.f,expf(a)); 57 | return x.f; 58 | } 59 | 60 | /* 61 | void foo(float *d, float *a) { 62 | if (a[0] >= a[1]) 63 | d[0] = a[0]; 64 | else 65 | d[0] = a[1]; 66 | } 67 | */ 68 | 69 | void softmax(float *arr, unsigned len) { 70 | float max = arr[0]; 71 | float sum = 0; 72 | for (unsigned i = 1; i < len; ++i) 73 | if (arr[i] > max) 74 | max = arr[i]; 75 | for (unsigned i = 0; i < len; ++i) 76 | sum += (arr[i] = my_expf(arr[i] - max)); 77 | for (unsigned i = 0; i < len; ++i) 78 | arr[i] /= sum; 79 | } 80 | 81 | void _exit() {} 82 | 83 | int main() { 84 | //float arr[]={1,2,3,1090}; 85 | float arr[]={-3,0.5,0.7,0.4}; 86 | softmax(arr,4); 87 | for (unsigned i = 0; i < sizeof(arr)/4; ++i) 88 | printf("%e\n", arr[i]); 89 | 90 | //float f[12]; 91 | //softmax(f, 12); 92 | //foo(f, f); 93 | } 94 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | export function assert(cond: boolean, msg = "Assertion failed") { 2 | if (!cond) { 3 | debugger 4 | throw new Error(msg) 5 | } 6 | } 7 | 8 | export function userError(msg: string): Error { 9 | let e = new Error(msg); 10 | (e).isUserError = true; 11 | throw e 12 | } 13 | 14 | 15 | export function lookup(m: pxt.Map, key: string): T { 16 | if (m.hasOwnProperty(key)) 17 | return m[key] 18 | return null 19 | } 20 | 21 | export function oops(msg = "OOPS"): Error { 22 | debugger 23 | throw new Error(msg) 24 | } 25 | 26 | export function endsWith(str: string, suffix: string) { 27 | if (str.length < suffix.length) return false 28 | if (suffix.length == 0) return true 29 | return str.slice(-suffix.length) == suffix 30 | } 31 | 32 | export function startsWith(str: string, prefix: string) { 33 | if (str.length < prefix.length) return false 34 | if (prefix.length == 0) return true 35 | return str.slice(0, prefix.length) == prefix 36 | } 37 | 38 | export function iterMap(m: pxt.Map, f: (k: string, v: T) => void) { 39 | Object.keys(m).forEach(k => f(k, m[k])) 40 | } 41 | 42 | export function mapMap(m: pxt.Map, f: (k: string, v: T) => S) { 43 | let r: pxt.Map = {} 44 | Object.keys(m).forEach(k => r[k] = f(k, m[k])) 45 | return r 46 | } 47 | 48 | export function pushRange(trg: T[], src: ArrayLike): void { 49 | for (let i = 0; i < src.length; ++i) 50 | trg.push(src[i]) 51 | } 52 | 53 | // TS gets lost in type inference when this is passed an array 54 | export function concatArrayLike(arrays: ArrayLike>): T[] { 55 | return concat(arrays as any) 56 | } 57 | 58 | export function concat(arrays: T[][]): T[] { 59 | let r: T[] = [] 60 | for (let i = 0; i < arrays.length; ++i) { 61 | pushRange(r, arrays[i]) 62 | } 63 | return r 64 | } 65 | 66 | export function range(len: number) { 67 | let r: number[] = [] 68 | for (let i = 0; i < len; ++i) r.push(i) 69 | return r 70 | } 71 | 72 | let seed = 13 * 0x1000193 73 | 74 | export function seedRandom(v: number) { 75 | seed = (v * 0x1000193) >>> 0 76 | } 77 | 78 | export function randomUint32() { 79 | let x = seed; 80 | x ^= x << 13; 81 | x ^= x >>> 17; 82 | x ^= x << 5; 83 | x >>>= 0; 84 | seed = x; 85 | return x; 86 | } 87 | 88 | export function randomInclusive(min: number, max: number) { 89 | return min + randomUint32() % (max - min + 1) 90 | } 91 | 92 | export function randomPermute(arr: T[]) { 93 | for (let i = 0; i < arr.length; ++i) { 94 | let j = randomUint32() % arr.length 95 | let tmp = arr[i] 96 | arr[i] = arr[j] 97 | arr[j] = tmp 98 | } 99 | } 100 | 101 | export function randomPick(arr: T[]): T { 102 | if (arr.length == 0) return null; 103 | return arr[randomUint32() % arr.length]; 104 | } 105 | 106 | export function randomUFloat() { 107 | return randomUint32() / 0x1_0000_0000; 108 | } 109 | 110 | export function randomSFloat() { 111 | return 2 * randomUFloat() - 1; 112 | } 113 | 114 | export function flatClone(v: T): T { 115 | const s = v as any 116 | const d: any = {} 117 | for (const k of Object.keys(s)) { 118 | d[k] = s[k] 119 | } 120 | return d 121 | } -------------------------------------------------------------------------------- /sample/ml4f.c: -------------------------------------------------------------------------------- 1 | #include "ml4f.h" 2 | #include 3 | 4 | int ml4f_is_valid_header(const ml4f_header_t *header) { 5 | if (!header || header->magic0 != ML4F_MAGIC0 || header->magic1 != ML4F_MAGIC1) 6 | return 0; 7 | if (header->input_type != ML4F_TYPE_FLOAT32 || header->output_type != ML4F_TYPE_FLOAT32) 8 | return 0; 9 | return 1; 10 | } 11 | 12 | typedef void (*model_fn_t)(const ml4f_header_t *model, uint8_t *arena); 13 | 14 | int ml4f_invoke(const ml4f_header_t *model, uint8_t *arena) { 15 | if (!ml4f_is_valid_header(model)) 16 | return -1; 17 | // +1 for Thumb mode 18 | model_fn_t fn = (model_fn_t)((const uint8_t *)model + model->header_size + 1); 19 | fn(model, arena); 20 | return 0; 21 | } 22 | 23 | #define EPS 0.00002f 24 | static int is_near(float a, float b) { 25 | float diff = a - b; 26 | if (diff < 0) 27 | diff = -diff; 28 | if (diff < EPS) 29 | return 1; 30 | if (a < 0) 31 | a = -a; 32 | if (b < 0) 33 | b = -b; 34 | if (diff / (a + b) < EPS) 35 | return 1; 36 | return 0; 37 | } 38 | 39 | int ml4f_test(const ml4f_header_t *model, uint8_t *arena) { 40 | if (!ml4f_is_valid_header(model)) 41 | return -1; 42 | 43 | if (!model->test_input_offset || !model->test_output_offset) 44 | return 0; // no tests 45 | 46 | memcpy(arena + model->input_offset, (uint8_t *)model + model->test_input_offset, 47 | ml4f_shape_size(ml4f_input_shape(model), model->input_type)); 48 | 49 | ml4f_invoke(model, arena); 50 | 51 | float *actual = (float *)(arena + model->output_offset); 52 | const float *expected = (const float *)((const uint8_t *)model + model->test_output_offset); 53 | int elts = ml4f_shape_elements(ml4f_output_shape(model)); 54 | for (int i = 0; i < elts; ++i) { 55 | if (!is_near(actual[i], expected[i])) 56 | return -2; 57 | } 58 | 59 | return 1; // tests OK 60 | } 61 | 62 | const uint32_t *ml4f_input_shape(const ml4f_header_t *model) { 63 | return model->input_shape; 64 | } 65 | 66 | const uint32_t *ml4f_output_shape(const ml4f_header_t *model) { 67 | const uint32_t *p = model->input_shape; 68 | while (*p) 69 | p++; 70 | p++; 71 | return p; 72 | } 73 | 74 | uint32_t ml4f_shape_elements(const uint32_t *shape) { 75 | uint32_t r = 1; 76 | while (*shape) 77 | r *= *shape++; 78 | return r; 79 | } 80 | 81 | uint32_t ml4f_shape_size(const uint32_t *shape, uint32_t type) { 82 | if (type != ML4F_TYPE_FLOAT32) 83 | return 0; 84 | return ml4f_shape_elements(shape) << 2; 85 | } 86 | 87 | int ml4f_argmax(float *data, uint32_t size) { 88 | if (size == 0) 89 | return -1; 90 | float max = data[0]; 91 | int maxidx = 0; 92 | for (unsigned i = 0; i < size; ++i) 93 | if (data[i] > max) { 94 | max = data[i]; 95 | maxidx = i; 96 | } 97 | return maxidx; 98 | } 99 | 100 | // This function is just an example - you'll likely have your own tensor formats and memory 101 | // allocation functions 102 | 103 | #include 104 | 105 | int ml4f_full_invoke(const ml4f_header_t *model, const float *input, float *output) { 106 | if (!ml4f_is_valid_header(model)) 107 | return -1; 108 | uint8_t *arena = malloc(model->arena_bytes); 109 | memcpy(arena + model->input_offset, input, 110 | ml4f_shape_size(ml4f_input_shape(model), model->input_type)); 111 | int r = ml4f_invoke(model, arena); 112 | memcpy(output, arena + model->output_offset, 113 | ml4f_shape_size(ml4f_output_shape(model), model->output_type)); 114 | free(arena); 115 | return r; 116 | } 117 | 118 | int ml4f_full_invoke_argmax(const ml4f_header_t *model, const float *input) { 119 | if (!ml4f_is_valid_header(model)) 120 | return -1; 121 | uint8_t *arena = malloc(model->arena_bytes); 122 | memcpy(arena + model->input_offset, input, 123 | ml4f_shape_size(ml4f_input_shape(model), model->input_type)); 124 | int r = ml4f_invoke(model, arena); 125 | if (r == 0) 126 | r = ml4f_argmax((float *)(arena + model->output_offset), 127 | ml4f_shape_size(ml4f_output_shape(model), model->output_type) >> 2); 128 | free(arena); 129 | return r; 130 | } -------------------------------------------------------------------------------- /src/float16.ts: -------------------------------------------------------------------------------- 1 | /// based on: Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf 2 | 3 | const basetable = new Uint16Array(512); 4 | const shifttable = new Uint8Array(512); 5 | 6 | const mantissatable = new Uint32Array(2048); 7 | const offsettable = new Uint16Array(64); 8 | const exponenttable = new Uint32Array(64); 9 | 10 | let inited = false 11 | 12 | function init() { 13 | inited = true 14 | for (let i = 0; i < 256; ++i) { 15 | const e = i - 127; 16 | if (e < -24) { // Very small numbers map to zero 17 | basetable[i | 0x000] = 0x0000; 18 | basetable[i | 0x100] = 0x8000; 19 | shifttable[i | 0x000] = 24; 20 | shifttable[i | 0x100] = 24; 21 | } else if (e < -14) { // Small numbers map to denorms 22 | basetable[i | 0x000] = (0x0400 >> (-e - 14)); 23 | basetable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; 24 | shifttable[i | 0x000] = -e - 1; 25 | shifttable[i | 0x100] = -e - 1; 26 | } else if (e <= 15) { // Normal numbers just lose precision 27 | basetable[i | 0x000] = ((e + 15) << 10); 28 | basetable[i | 0x100] = ((e + 15) << 10) | 0x8000; 29 | shifttable[i | 0x000] = 13; 30 | shifttable[i | 0x100] = 13; 31 | } else if (e < 128) { // Large numbers map to Infinity 32 | basetable[i | 0x000] = 0x7C00; 33 | basetable[i | 0x100] = 0xFC00; 34 | shifttable[i | 0x000] = 24; 35 | shifttable[i | 0x100] = 24; 36 | } else { // Infinity and NaN's stay Infinity and NaN's 37 | basetable[i | 0x000] = 0x7C00; 38 | basetable[i | 0x100] = 0xFC00; 39 | shifttable[i | 0x000] = 13; 40 | shifttable[i | 0x100] = 13; 41 | } 42 | } 43 | 44 | for (let i = 1; i < 2048; ++i) { 45 | if (i < 1024) 46 | mantissatable[i] = convertmantissa(i) 47 | else 48 | mantissatable[i] = 0x38000000 + ((i - 1024) << 13) 49 | } 50 | 51 | exponenttable[32] = 0x80000000 52 | exponenttable[31] = 0x47800000 53 | exponenttable[63] = 0xC7800000 54 | for (let i = 1; i <= 30; ++i) 55 | exponenttable[i] = i << 23 56 | for (let i = 33; i <= 62; ++i) 57 | exponenttable[i] = 0x80000000 + ((i - 32) << 23) 58 | 59 | for (let i = 1; i < offsettable.length; ++i) 60 | offsettable[i] = 1024 61 | offsettable[32] = 0 62 | 63 | function convertmantissa(i: number) { 64 | let m = i << 13; // Zero pad mantissa bits 65 | let e = 0; // Zero exponent 66 | while (!(m & 0x00800000)) { // While not normalized 67 | e -= 0x00800000; // Decrement exponent (1<<23) 68 | m <<= 1; // Shift mantissa 69 | } 70 | m &= ~0x00800000; // Clear leading 1 bit 71 | e += 0x38800000; // Adjust bias ((127-14)<<23) 72 | return (m | e) >>> 0; // Return combined number 73 | } 74 | } 75 | 76 | export function float32ToUInt32(v: number) { 77 | const buf = new Float32Array(1) 78 | buf[0] = v 79 | return new Uint32Array(buf.buffer)[0] 80 | } 81 | 82 | export function float16toUInt16(v: number) { 83 | const f = float32ToUInt32(v) 84 | if (!inited) init() 85 | return basetable[(f >> 23) & 0x1ff] | ((f & 0x007fffff) >> shifttable[(f >> 23) & 0x1ff]) 86 | } 87 | 88 | export function float16AsUintToFloat(h: number) { 89 | if (!inited) init() 90 | const tmp = mantissatable[offsettable[h >> 10] + (h & 0x3ff)] + exponenttable[h >> 10] 91 | const buf = new Uint32Array(1) 92 | buf[0] = tmp 93 | return new Float32Array(buf.buffer)[0] 94 | } 95 | 96 | export function testFloatConv() { 97 | for (let i = 0; i < 30000; ++i) { 98 | test(i) 99 | test(-i) 100 | test(1 / i) 101 | test(-1 / i) 102 | test(1 / (i * 100)) 103 | test(-1 / (i * 100)) 104 | } 105 | 106 | function test(v: number) { 107 | const u = float16toUInt16(v) & 0xffff 108 | const v2 = float16AsUintToFloat(u) 109 | const d = Math.min(10000 * Math.abs(v - v2), Math.abs((v - v2) / v)) 110 | if (d > 0.002) { 111 | throw new Error(`fail: ${v} -> ${u} -> ${v2} (dd=${v - v2} d=${d})`) 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ML4F - Machine Learning model compiler for Cortex-M4F 2 | 3 | ML4F takes a [Keras](https://keras.io/) sequential model as an input and compiles it directly to 4 | ARM Thumb machine code for Cortex-M4F and better (M7, M33 etc.). 5 | The performance (latency) is typically an order of magnitude better than the 6 | [Tensorflow Lite for Microcontrollers](https://www.tensorflow.org/lite/microcontrollers) interpreter 7 | (with `float32` models). 8 | 9 | The input model generally needs to be in [Tensorflow.js](https://www.tensorflow.org/js) format, but the command line tool can 10 | invoke [Python scripts](https://www.tensorflow.org/js/guide/conversion) to convert from `.h5` or `.pb` models. 11 | Once compiled, weights can be stored as `float32` or `float16`. 12 | 13 | The following operators are supported: 14 | * `Conv1D` 15 | * `Conv2D` 16 | * `DepthwiseConv1D` 17 | * `DepthwiseConv2D` 18 | * `MaxPooling1D` 19 | * `MaxPooling2D` 20 | * `AveragePooling1D` 21 | * `AveragePooling2D` 22 | * `Dense` 23 | * `Softmax` 24 | * `Activation` 25 | * `BatchNormalization` 26 | 27 | Plus some no-ops: 28 | * `InputLayer` 29 | * `Dropout` 30 | * `Flatten` 31 | * `Reshape` 32 | 33 | Feel free to report what other operators might be useful (along with example models) via 34 | [GitHub Issues](https://github.com/microsoft/ml4f/issues). 35 | 36 | ## Usage 37 | 38 | ```bash 39 | npm i -g ml4f 40 | ml4f my-model 41 | ``` 42 | 43 | Typical invocation might look like this: 44 | 45 | ```bash 46 | ml4f --basename model-float32 my-model.h5 47 | ml4f --float16 --basename model-float16 built/converted.tfjs 48 | ``` 49 | 50 | First line compiles `my-model.h5` using `float32` weights, with results in `built/model-float32.*`. 51 | The second line compiles with `float16` weights, using temporary file created by the first 52 | line to speed things up (Python TensorFlow is really slow to load). 53 | Results are in `built/model-float16.*`. 54 | 55 | Run `ml4f --help` for more info. 56 | 57 | You can also use it as a library from a browser (in which case it can only take TF.js models). 58 | 59 | ## Evaluating models 60 | 61 | You can pass `--eval test.json` option to evaluated the model on given input data - this will 62 | print confusion matrix and accuracy. 63 | The `test.json` has two fields `x` and `y`. The field `x` contains a batch of input tensors, 64 | and `y` a batch of output tensors, with proper nesting. 65 | For example, for input of shape `2x3` and output of shape `4`: 66 | 67 | ```json 68 | { 69 | "x": [ 70 | [ [ 0.1, 0.2, -0.3 ], [ 0.2, -0.22, 0 ] ], 71 | [ [ -0.1, 0.3, 0.1 ], [ 0.32, 0.2, 1 ] ] 72 | ], 73 | "y": [ 74 | [ 0, 1, 0, 0 ], 75 | [ 1, 0, 0, 0 ] 76 | ] 77 | } 78 | ``` 79 | 80 | If you have data as NumPy arrays, you can use the following snippet to save it as JSON: 81 | 82 | ```python 83 | import json 84 | class NumpyEncoder(json.JSONEncoder): 85 | def default(self, obj): 86 | if isinstance(obj, np.ndarray): 87 | return obj.tolist() 88 | return json.JSONEncoder.default(self, obj) 89 | 90 | with open('test.json', 'w') as outfile: 91 | json.dump({"x": xs_test, "y": ys_test}, outfile, cls=NumpyEncoder) 92 | ``` 93 | 94 | Evaluation stats look like the following: 95 | 96 | ``` 97 | Accuracy: 0.9560 98 | 245 0 1 2 99 | 6 84 4 0 100 | 3 2 73 0 101 | 4 0 0 76 102 | 103 | model: 12.75k; code: 2.46k (19.3%); arena: 4.38k; test 0.00k 104 | total cycles: 225149 (2.680ms at 84MHz) 105 | ``` 106 | 107 | ## Architecture 108 | 109 | The models are loaded using TensorFlow.js library. 110 | Each layer is first compiled separately, and the generated code is run in simulation 111 | (a JavaScript function is generated, where each line corresponds to a single assembly instruction). 112 | The results are compared with running the same layer in TensorFlow.js. 113 | This process can be disabled with `--no-validate` option. 114 | Then layers are composed and the final binary code is generated. 115 | 116 | The binary is position-independent and can be loaded from any word-aligned address in flash or RAM. 117 | Look in `sample/` folder for example invocation from C, 118 | or check out our [MakeCode extension](https://github.com/microsoft/pxt-ml4f). 119 | 120 | 121 | ## Compiling 122 | 123 | ``` 124 | yarn install 125 | yarn watch 126 | # in another window 127 | http-server -c1 128 | ``` 129 | 130 | Then open http://localhost:8080/ 131 | 132 | Also, run `./ml4f` in this folder. 133 | 134 | ## Contributing 135 | 136 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 137 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 138 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 139 | 140 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 141 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 142 | provided by the bot. You will only need to do this once across all repos using our CLA. 143 | 144 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 145 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 146 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 147 | -------------------------------------------------------------------------------- /cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | import * as tf from '@tensorflow/tfjs' 2 | import * as fs from 'fs' 3 | import * as path from 'path' 4 | import * as child_process from 'child_process' 5 | import { program as commander } from "commander" 6 | import { 7 | compileModel, compileModelAndFullValidate, 8 | evalModel, 9 | loadFlatJSONModel, 10 | loadTfjsModelJSON, 11 | Options, runModel, sampleModel, testAllModels, 12 | testFloatConv, 13 | toCSource 14 | } from '../../src/ml4f' 15 | 16 | interface CmdOptions { 17 | debug?: boolean 18 | output?: string 19 | basename?: string 20 | validate?: boolean 21 | testData?: boolean 22 | sampleModel?: string 23 | loadJs?: string 24 | selfTest?: boolean 25 | optimize?: boolean 26 | float16?: boolean 27 | eval?: string 28 | force?: boolean 29 | } 30 | 31 | let options: CmdOptions 32 | 33 | function getCompileOptions(): Options { 34 | return { 35 | optimize: options.optimize, 36 | verbose: options.debug, 37 | includeTest: options.testData, 38 | float16weights: options.float16, 39 | testOutputFromJS: true, 40 | } 41 | } 42 | 43 | function mkdirP(thePath: string) { 44 | if (thePath == "." || !thePath) return; 45 | if (!fs.existsSync(thePath)) { 46 | mkdirP(path.dirname(thePath)) 47 | fs.mkdirSync(thePath) 48 | } 49 | } 50 | 51 | function built(fn: string) { 52 | return path.join(options.output, fn) 53 | } 54 | 55 | function loadJSONModel(modelPath: string) { 56 | const modelBuf = fs.readFileSync(modelPath) 57 | 58 | if (modelBuf[0] != 0x7b) 59 | throw new Error("model not in JSON format") 60 | 61 | const preModel = JSON.parse(modelBuf.toString("utf8")) 62 | 63 | const model0 = loadFlatJSONModel(preModel) 64 | if (model0) return model0 65 | 66 | const modelJSON: tf.io.ModelJSON = preModel 67 | 68 | if (!modelJSON.modelTopology) 69 | throw new Error("model not in tf.js JSON format") 70 | 71 | const model = loadTfjsModelJSON(modelJSON) 72 | 73 | if (modelJSON.weightsManifest != null) { 74 | const dirName = path.dirname(modelPath); 75 | const buffers: Buffer[] = []; 76 | model.weightSpecs = []; 77 | for (const group of modelJSON.weightsManifest) { 78 | for (const weightPath of group.paths) { 79 | buffers.push(fs.readFileSync(path.join(dirName, weightPath))); 80 | } 81 | model.weightSpecs.push(...group.weights); 82 | } 83 | model.weightData = new Uint8Array(Buffer.concat(buffers)).buffer; 84 | } 85 | 86 | return model; 87 | } 88 | 89 | function runCmd(cmd: string, args: string[]) { 90 | const info = `${cmd} ${args.join(" ")}` 91 | console.log(`RUN ${info}`) 92 | const res = child_process.spawnSync(cmd, args, { 93 | stdio: "inherit" 94 | }) 95 | if (res.status != 0) 96 | throw new Error(`non-zero status from ${info}`) 97 | console.log("RUN OK") 98 | } 99 | 100 | function fromPB(modelPath: string) { 101 | const tmpPath = built("converted.h5") 102 | runCmd("python3", [ 103 | "-c", `import tensorflow; m = tensorflow.keras.models.load_model('${path.dirname(modelPath)}'); m.save('${tmpPath}')` 104 | ]) 105 | return loadModel(tmpPath) 106 | } 107 | 108 | function fromH5(modelPath: string) { 109 | const tmpPath = built("converted.tfjs") 110 | runCmd("tensorflowjs_converter", [ 111 | "--input_format", "keras", 112 | "--output_format", "tfjs_layers_model", 113 | modelPath, tmpPath 114 | ]) 115 | return loadJSONModel(path.join(tmpPath, "model.json")) 116 | } 117 | 118 | function checkSubdir(modelPath: string, n: string) { 119 | const saved = path.join(modelPath, n) 120 | if (fs.existsSync(saved)) 121 | return saved 122 | return modelPath 123 | } 124 | 125 | async function loadModel(modelPath: string): Promise { 126 | modelPath = checkSubdir(modelPath, "saved_model.pb") 127 | modelPath = checkSubdir(modelPath, "model.json") 128 | 129 | let modelBuf = fs.readFileSync(modelPath) 130 | 131 | if (modelBuf[0] == 0x08) 132 | return fromPB(modelPath) 133 | 134 | if (modelBuf[0] == 0x89) 135 | return fromH5(modelPath) 136 | 137 | return loadJSONModel(modelPath) 138 | 139 | } 140 | 141 | async function processModelFile(modelFile: string) { 142 | tf.setBackend("cpu") 143 | 144 | mkdirP(options.output) 145 | 146 | let m: tf.LayersModel 147 | if (options.sampleModel) { 148 | m = sampleModel(options.sampleModel) 149 | } else { 150 | const model = await loadModel(modelFile) 151 | if (!model.weightData && !options.force) 152 | throw new Error(`model '${modelFile}' is missing weights`) 153 | m = await tf.loadLayersModel({ load: () => Promise.resolve(model) }) 154 | } 155 | 156 | const opts = getCompileOptions() 157 | const cres = !options.validate ? compileModel(m, opts) : await compileModelAndFullValidate(m, opts) 158 | 159 | write(".asm", cres.thumb) 160 | write(".js", cres.js) 161 | write(".ml4f", cres.machineCode) 162 | write(".c", toCSource(options.basename.replace(/[^\w]/g, "_"), cres.machineCode)) 163 | write("_layerStats.json", JSON.stringify(cres.stats, null, 4)) 164 | 165 | let evalInfo = `\n*** ${built(options.basename + ".ml4f")}\n\n` 166 | 167 | if (options.eval) { 168 | const ev = evalModel(cres, JSON.parse(fs.readFileSync(options.eval, "utf8"))) 169 | evalInfo += ev + "\n" 170 | } 171 | 172 | evalInfo += cres.memInfo + "\n" 173 | evalInfo += cres.timeInfo + "\n" 174 | 175 | write(".txt", evalInfo + "\n") 176 | 177 | console.log("\n" + evalInfo) 178 | 179 | function write(ext: string, buf: string | Uint8Array) { 180 | const fn = built(options.basename + ext) 181 | const binbuf = typeof buf == "string" ? Buffer.from(buf, "utf8") : buf 182 | console.log(`write ${fn} (${binbuf.length} bytes)`) 183 | fs.writeFileSync(fn, binbuf) 184 | } 185 | } 186 | 187 | export async function mainCli() { 188 | // require('@tensorflow/tfjs-node'); 189 | 190 | // shut up warning 191 | (tf.backend() as any).firstUse = false; 192 | 193 | const pkg = require("../../package.json") 194 | commander 195 | .version(pkg.version) 196 | .option("-d, --debug", "enable debugging") 197 | .option("-n, --no-validate", "don't validate resulting model") 198 | .option("-g, --no-optimize", "don't optimize IR") 199 | .option("-h, --float16", "use float16 weights") 200 | .option("-t, --test-data", "include test data in binary model") 201 | .option("-T, --self-test", "run self-test of all included sample models") 202 | .option("-s, --sample-model ", "use an included sample model") 203 | .option("-e, --eval ", "evaluate model (confusion matrix, accuracy) on a given test data") 204 | .option("-o, --output ", "path to store compilation results (default: 'built')") 205 | .option("-b, --basename ", "basename of model files (default: 'model')") 206 | .option("-f, --force", "force compilation even if certain errors are detected") 207 | .option("-j, --load-js ", "load compiled model in JavaScript format") 208 | .arguments("") 209 | .parse(process.argv) 210 | 211 | options = commander as CmdOptions 212 | 213 | if (!options.output) options.output = "built" 214 | if (!options.basename) options.basename = "model" 215 | 216 | if (options.selfTest) { 217 | testFloatConv() 218 | const opts = getCompileOptions() 219 | opts.includeTest = false 220 | await testAllModels(opts) 221 | process.exit(0) 222 | } 223 | 224 | if (options.loadJs) { 225 | if (!options.eval) { 226 | console.error(`--eval is required with --load-js`) 227 | process.exit(1) 228 | } 229 | const js = fs.readFileSync(options.loadJs, "utf-8") 230 | const data = JSON.parse(fs.readFileSync(options.eval, "utf8")) 231 | runModel(js, data) 232 | process.exit(0) 233 | } 234 | 235 | if (!options.sampleModel && commander.args.length != 1) { 236 | console.error("exactly one model argument expected") 237 | process.exit(1) 238 | } 239 | 240 | try { 241 | await processModelFile(commander.args[0]) 242 | } catch (e) { 243 | console.error(e.stack) 244 | } 245 | } 246 | 247 | if (require.main === module) mainCli() 248 | -------------------------------------------------------------------------------- /src/driver.ts: -------------------------------------------------------------------------------- 1 | import * as tf from '@tensorflow/tfjs' 2 | import { ThumbProcessor } from './thumb'; 3 | import * as assembler from './assembler' 4 | import * as U from './util' 5 | import { assignLayerInfos, compileModelCore, CompileResult, LayerStats, partialModels, prefixModels, shapeElts } from './compiler'; 6 | import { Options } from './ir'; 7 | 8 | const epsF32 = 0.00009 9 | const epsF16 = 0.01 10 | 11 | function mkProcessorFile() { 12 | const b = new assembler.File(new ThumbProcessor()) 13 | 14 | b.ei.testAssembler(); // just in case 15 | 16 | b.disablePeepHole = true 17 | 18 | b.lookupExternalLabel = _name => null; 19 | b.normalizeExternalLabel = s => s; 20 | b.throwOnError = true; 21 | 22 | return b 23 | } 24 | 25 | function throwAssemblerErrors(b: assembler.File) { 26 | if (b.errors.length > 0) { 27 | throw new Error(b.errors[0].message) 28 | } 29 | } 30 | 31 | export function assemble(src: string) { 32 | const procFile = mkProcessorFile() 33 | procFile.emit(src); 34 | 35 | throwAssemblerErrors(procFile) 36 | 37 | // 16-byte aligned size 38 | const binary = new Uint8Array(((procFile.buf.length << 1) + 15) & ~15) 39 | for (let i = 0; i < procFile.buf.length; ++i) { 40 | binary[i << 1] = procFile.buf[i] & 0xff 41 | binary[(i << 1) + 1] = (procFile.buf[i] >> 8) & 0xff 42 | } 43 | 44 | return { binary, procFile } 45 | } 46 | 47 | function randomTensor(shape: tf.Shape, mult = 1) { 48 | shape = shape.map(s => s == null ? 1 : s) 49 | const num = shapeElts(shape) 50 | return tf.tidy(() => tf.tensor(U.range(num).map(_ => mult * U.randomSFloat())).reshape(shape)) 51 | } 52 | 53 | function randomPosTensor(shape: tf.Shape, mult = 1) { 54 | shape = shape.map(s => s == null ? 1 : s) 55 | const num = shapeElts(shape) 56 | return tf.tidy(() => tf.tensor(U.range(num).map(_ => mult * U.randomUFloat())).reshape(shape)) 57 | } 58 | 59 | export function setRandomWeights(l: tf.layers.Layer) { 60 | let idx = 0 61 | for (const w of l.weights) { 62 | const mult = 1 63 | if (w.originalName.endsWith("/moving_variance")) 64 | w.write(randomPosTensor(w.shape, mult)) 65 | else 66 | w.write(randomTensor(w.shape, mult)) 67 | idx++ 68 | } 69 | } 70 | 71 | function isNear(a: number, b: number, eps: number) { 72 | const diff = Math.abs(a - b) 73 | if (diff < eps) 74 | return true 75 | if (diff / (Math.abs(a) + Math.abs(b)) < eps) 76 | return true 77 | return false 78 | } 79 | 80 | export function optionsWithTestData(m: tf.LayersModel, opts: Options) { 81 | opts = U.flatClone(opts) 82 | let count = 0 83 | let maxMul = 0 84 | while (true) { 85 | const randomInput = randomTensor(m.inputs[0].shape) 86 | const resTensor = m.predict(randomInput) as tf.Tensor 87 | const res = resTensor.flatten().arraySync() 88 | let sum = 0 89 | let mul = 1 90 | for (const r of res) { 91 | sum += r 92 | mul *= r 93 | } 94 | 95 | const isSoftmax = Math.abs(sum - 1) < 0.1 96 | if (!isSoftmax) { 97 | save() 98 | break 99 | } 100 | 101 | if (mul > maxMul) { 102 | maxMul = mul 103 | save() 104 | } 105 | 106 | if (count++ > (opts.includeTest ? 1000 : 100) || maxMul > 0.1) { 107 | if (!mul) 108 | save() 109 | break 110 | } 111 | 112 | function save() { 113 | opts.testInput = randomInput.flatten().arraySync() 114 | opts.testOutput = res 115 | } 116 | } 117 | return opts 118 | } 119 | 120 | export function compileModel(m: tf.LayersModel, opts: Options) { 121 | const cres = compileModelCore(m, opts) 122 | const ares = assemble(cres.thumb) 123 | cres.machineCode = ares.binary 124 | 125 | let idx = 0 126 | for (const st of cres.stats.layers) { 127 | st.codeBytes = ares.procFile.lookupLabel("end_" + idx) - ares.procFile.lookupLabel("begin_" + idx) 128 | idx++ 129 | } 130 | 131 | const st = getStatsFromBin(cres.machineCode, cres.stats.total) 132 | cres.memInfo = st.info 133 | return cres 134 | } 135 | 136 | export async function compileModelAndFullValidate(m: tf.LayersModel, opts: Options) { 137 | assignLayerInfos(m, opts) 138 | 139 | const optsPart = U.flatClone(opts) 140 | optsPart.includeTest = false 141 | 142 | console.log("Validating partial models...") 143 | for await (const mod of partialModels(m, optsPart)) { 144 | for (const l of mod.layers) 145 | setRandomWeights(l) 146 | compileAndTest(mod, optsPart) 147 | } 148 | 149 | console.log("Validating prefix models...") 150 | for await (const mod of prefixModels(m, optsPart)) { 151 | compileAndTest(mod, optsPart) 152 | } 153 | 154 | console.log("Compiling full model...") 155 | 156 | // also test the top-level one again 157 | return compileAndTest(m, opts) 158 | } 159 | 160 | export function validateCompilation(cres: CompileResult) { 161 | const opts = cres.options 162 | const res = opts.testOutput 163 | const res2 = cres.execute(opts.testInput) 164 | if (cres.options.verbose) 165 | console.log("Test output", res2) 166 | let numerr = 0 167 | for (let i = 0; i < res2.length; ++i) { 168 | if (!isNear(res[i], res2[i], opts.float16weights ? epsF16 : epsF32)) { 169 | console.log(`at ${i} ${res[i]}[exp] - ${res2[i]} = ${res[i] - res2[i]}`) 170 | numerr++ 171 | if (numerr > 5) break 172 | } 173 | } 174 | if (numerr) 175 | throw new Error("mismatch") 176 | } 177 | 178 | export function compileAndTest(m: tf.LayersModel, options: Options) { 179 | let cres: CompileResult 180 | try { 181 | options = optionsWithTestData(m, options) 182 | cres = compileModel(m, options) 183 | validateCompilation(cres) 184 | return cres 185 | } catch (e) { 186 | if (options.info) 187 | console.log(options.info) 188 | if (!cres || !options.verbose) { 189 | options.verbose = true 190 | cres = compileModelCore(m, options) 191 | } 192 | console.log(cres.js) 193 | console.log("Failing model: ", m.name) 194 | throw e 195 | } 196 | } 197 | 198 | function readU32(bin: Uint8Array, off: number) { 199 | return (bin[off] | (bin[off + 1] << 8) | (bin[off + 2] << 16) | (bin[off + 3] << 24)) >>> 0 200 | } 201 | 202 | function readU32s(bin: Uint8Array) { 203 | const res: number[] = [] 204 | for (let i = 0; i < bin.length; i += 4) { 205 | res.push(readU32(bin, i)) 206 | } 207 | return res 208 | } 209 | 210 | export function getStatsFromBin(bin: Uint8Array, stats?: LayerStats) { 211 | let [magic0, magic1, hdSize, totalSize, weightsOff, testInpOff, testOutOff, arenaSize] = readU32s(bin.slice(0, 64)) 212 | if (magic0 != 0x30470f62) 213 | return null 214 | const modelSize = testInpOff || totalSize 215 | const codeSize = weightsOff - hdSize 216 | const codePerc = codeSize * 100 / modelSize 217 | const testSize = totalSize - modelSize 218 | 219 | function sz(n: number) { 220 | return (n / 1024).toFixed(2) + "k" 221 | } 222 | const info = 223 | `model: ${sz(modelSize)}; ` + 224 | `code: ${sz(codeSize)} (${codePerc.toFixed(1)}%); ` + 225 | `arena: ${sz(arenaSize)}; test ${sz(testSize)}` 226 | 227 | if (stats) { 228 | stats.arenaBytes = arenaSize 229 | stats.codeBytes = codeSize 230 | stats.weightBytes = modelSize - codeSize 231 | } 232 | 233 | return { 234 | info, 235 | modelSize, 236 | codeSize, 237 | testSize, 238 | totalSize, 239 | arenaSize 240 | } 241 | } 242 | 243 | export function loadTfjsModelJSON(modelJSON: tf.io.ModelJSON) { 244 | // remove regularizers, as we're not going to train the model, and unknown regularizers 245 | // cause it to fail to load 246 | const cfg = (modelJSON.modelTopology as any)?.model_config?.config 247 | const outLayers: any[] = [] 248 | 249 | let seq_id = 0 250 | 251 | function addLayer(layer: any) { 252 | const layerConfig = layer?.config 253 | if (layerConfig) { 254 | layerConfig.bias_regularizer = null 255 | layerConfig.activity_regularizer = null 256 | layerConfig.bias_constraint = null 257 | } 258 | 259 | if (layer.class_name == "Sequential") { 260 | seq_id++ 261 | for (const l of layer.config.layers) { 262 | if (l.class_name == "InputLayer") 263 | continue 264 | if (l.config.name == "dropout") 265 | l.config.name += "_seq_" + seq_id 266 | addLayer(l) 267 | } 268 | } else { 269 | outLayers.push(layer) 270 | } 271 | } 272 | 273 | if (cfg?.layers) { 274 | cfg.layers.forEach(addLayer) 275 | cfg.layers = outLayers 276 | } 277 | 278 | const model: tf.io.ModelArtifacts = { 279 | modelTopology: modelJSON.modelTopology, 280 | format: modelJSON.format, 281 | generatedBy: modelJSON.generatedBy, 282 | convertedBy: modelJSON.convertedBy, 283 | trainingConfig: modelJSON.trainingConfig, 284 | userDefinedMetadata: modelJSON.userDefinedMetadata 285 | } 286 | 287 | return model 288 | } 289 | 290 | export function loadFlatJSONModel(preModel: any) { 291 | if (!preModel.modelJSON) 292 | return null 293 | 294 | let modelJSON: tf.io.ModelJSON 295 | if (typeof preModel.modelJSON == "string") 296 | modelJSON = JSON.parse(preModel.modelJSON) 297 | else 298 | modelJSON = preModel.modelJSON 299 | 300 | const model = loadTfjsModelJSON(modelJSON) 301 | const arr: number[] = preModel.weights 302 | if (Array.isArray(arr)) { 303 | model.weightData = new Uint32Array(arr).buffer 304 | model.weightSpecs = (modelJSON as any).weightSpecs 305 | } 306 | 307 | return model 308 | } 309 | 310 | export function toCSource(name: string, machineCode: Uint8Array) { 311 | if (machineCode.length & 3) throw new Error() 312 | const u32 = new Uint32Array(machineCode.buffer) 313 | let r = `const unsigned ${name}[${u32.length}] = {\n` 314 | const chunk = 8 315 | for (let off = 0; off < u32.length; off += chunk) { 316 | r += " " 317 | r += Array.from(u32.slice(off, off + chunk)) 318 | .map(n => "0x" + ("00000000" + n.toString(16)).slice(-8) + ", ") 319 | .join("") 320 | r += "\n" 321 | } 322 | r += "};\n" 323 | return r 324 | } 325 | -------------------------------------------------------------------------------- /models/gestures.tfjsmodel.json: -------------------------------------------------------------------------------- 1 | { 2 | "modelTopology": { 3 | "class_name": "Sequential", 4 | "config": { 5 | "name": "sequential_1", 6 | "layers": [ 7 | { 8 | "class_name": "Conv2D", 9 | "config": { 10 | "filters": 16, 11 | "kernel_initializer": { 12 | "class_name": "VarianceScaling", 13 | "config": { 14 | "scale": 1, 15 | "mode": "fan_in", 16 | "distribution": "normal", 17 | "seed": null 18 | } 19 | }, 20 | "kernel_regularizer": null, 21 | "kernel_constraint": null, 22 | "kernel_size": [ 23 | 4, 24 | 3 25 | ], 26 | "strides": [ 27 | 1, 28 | 1 29 | ], 30 | "padding": "valid", 31 | "data_format": "channels_last", 32 | "dilation_rate": [ 33 | 1, 34 | 1 35 | ], 36 | "activation": "relu", 37 | "use_bias": true, 38 | "bias_initializer": { 39 | "class_name": "Zeros", 40 | "config": {} 41 | }, 42 | "bias_regularizer": null, 43 | "activity_regularizer": null, 44 | "bias_constraint": null, 45 | "name": "conv2d_Conv2D1", 46 | "trainable": true, 47 | "batch_input_shape": [ 48 | null, 49 | 50, 50 | 3, 51 | 1 52 | ], 53 | "dtype": "float32" 54 | } 55 | }, 56 | { 57 | "class_name": "MaxPooling2D", 58 | "config": { 59 | "pool_size": [ 60 | 2, 61 | 1 62 | ], 63 | "padding": "valid", 64 | "strides": [ 65 | 2, 66 | 1 67 | ], 68 | "data_format": "channels_last", 69 | "name": "max_pooling2d_MaxPooling2D1", 70 | "trainable": true 71 | } 72 | }, 73 | { 74 | "class_name": "Dropout", 75 | "config": { 76 | "rate": 0.1, 77 | "noise_shape": null, 78 | "seed": null, 79 | "name": "dropout_Dropout1", 80 | "trainable": true 81 | } 82 | }, 83 | { 84 | "class_name": "Conv2D", 85 | "config": { 86 | "filters": 16, 87 | "kernel_initializer": { 88 | "class_name": "VarianceScaling", 89 | "config": { 90 | "scale": 1, 91 | "mode": "fan_in", 92 | "distribution": "normal", 93 | "seed": null 94 | } 95 | }, 96 | "kernel_regularizer": null, 97 | "kernel_constraint": null, 98 | "kernel_size": [ 99 | 2, 100 | 1 101 | ], 102 | "strides": [ 103 | 1, 104 | 1 105 | ], 106 | "padding": "valid", 107 | "data_format": "channels_last", 108 | "dilation_rate": [ 109 | 1, 110 | 1 111 | ], 112 | "activation": "relu", 113 | "use_bias": true, 114 | "bias_initializer": { 115 | "class_name": "Zeros", 116 | "config": {} 117 | }, 118 | "bias_regularizer": null, 119 | "activity_regularizer": null, 120 | "bias_constraint": null, 121 | "name": "conv2d_Conv2D2", 122 | "trainable": true 123 | } 124 | }, 125 | { 126 | "class_name": "Dropout", 127 | "config": { 128 | "rate": 0.1, 129 | "noise_shape": null, 130 | "seed": null, 131 | "name": "dropout_Dropout2", 132 | "trainable": true 133 | } 134 | }, 135 | { 136 | "class_name": "Conv2D", 137 | "config": { 138 | "filters": 16, 139 | "kernel_initializer": { 140 | "class_name": "VarianceScaling", 141 | "config": { 142 | "scale": 1, 143 | "mode": "fan_in", 144 | "distribution": "normal", 145 | "seed": null 146 | } 147 | }, 148 | "kernel_regularizer": null, 149 | "kernel_constraint": null, 150 | "kernel_size": [ 151 | 2, 152 | 1 153 | ], 154 | "strides": [ 155 | 1, 156 | 1 157 | ], 158 | "padding": "valid", 159 | "data_format": "channels_last", 160 | "dilation_rate": [ 161 | 1, 162 | 1 163 | ], 164 | "activation": "relu", 165 | "use_bias": true, 166 | "bias_initializer": { 167 | "class_name": "Zeros", 168 | "config": {} 169 | }, 170 | "bias_regularizer": null, 171 | "activity_regularizer": null, 172 | "bias_constraint": null, 173 | "name": "conv2d_Conv2D3", 174 | "trainable": true 175 | } 176 | }, 177 | { 178 | "class_name": "Dropout", 179 | "config": { 180 | "rate": 0.1, 181 | "noise_shape": null, 182 | "seed": null, 183 | "name": "dropout_Dropout3", 184 | "trainable": true 185 | } 186 | }, 187 | { 188 | "class_name": "Flatten", 189 | "config": { 190 | "name": "flatten_Flatten1", 191 | "trainable": true 192 | } 193 | }, 194 | { 195 | "class_name": "Dense", 196 | "config": { 197 | "units": 4, 198 | "activation": "softmax", 199 | "use_bias": true, 200 | "kernel_initializer": { 201 | "class_name": "VarianceScaling", 202 | "config": { 203 | "scale": 1, 204 | "mode": "fan_in", 205 | "distribution": "normal", 206 | "seed": null 207 | } 208 | }, 209 | "bias_initializer": { 210 | "class_name": "Zeros", 211 | "config": {} 212 | }, 213 | "kernel_regularizer": null, 214 | "bias_regularizer": null, 215 | "activity_regularizer": null, 216 | "kernel_constraint": null, 217 | "bias_constraint": null, 218 | "name": "dense_Dense1", 219 | "trainable": true 220 | } 221 | } 222 | ] 223 | }, 224 | "keras_version": "tfjs-layers 2.3.0", 225 | "backend": "tensor_flow.js" 226 | }, 227 | "format": "layers-model", 228 | "generatedBy": "TensorFlow.js tfjs-layers v2.3.0", 229 | "convertedBy": null, 230 | "weightsManifest": [ 231 | { 232 | "paths": [ 233 | "./gestures.tfjsmodel.weights.bin" 234 | ], 235 | "weights": [ 236 | { 237 | "name": "conv2d_Conv2D1/kernel", 238 | "shape": [ 239 | 4, 240 | 3, 241 | 1, 242 | 16 243 | ], 244 | "dtype": "float32" 245 | }, 246 | { 247 | "name": "conv2d_Conv2D1/bias", 248 | "shape": [ 249 | 16 250 | ], 251 | "dtype": "float32" 252 | }, 253 | { 254 | "name": "conv2d_Conv2D2/kernel", 255 | "shape": [ 256 | 2, 257 | 1, 258 | 16, 259 | 16 260 | ], 261 | "dtype": "float32" 262 | }, 263 | { 264 | "name": "conv2d_Conv2D2/bias", 265 | "shape": [ 266 | 16 267 | ], 268 | "dtype": "float32" 269 | }, 270 | { 271 | "name": "conv2d_Conv2D3/kernel", 272 | "shape": [ 273 | 2, 274 | 1, 275 | 16, 276 | 16 277 | ], 278 | "dtype": "float32" 279 | }, 280 | { 281 | "name": "conv2d_Conv2D3/bias", 282 | "shape": [ 283 | 16 284 | ], 285 | "dtype": "float32" 286 | }, 287 | { 288 | "name": "dense_Dense1/kernel", 289 | "shape": [ 290 | 336, 291 | 4 292 | ], 293 | "dtype": "float32" 294 | }, 295 | { 296 | "name": "dense_Dense1/bias", 297 | "shape": [ 298 | 4 299 | ], 300 | "dtype": "float32" 301 | } 302 | ] 303 | } 304 | ] 305 | } -------------------------------------------------------------------------------- /models/button.json: -------------------------------------------------------------------------------- 1 | {"name":"classifier1","inputShape":[20,1],"inputTypes":["pressure"],"labels":["notpressed","pressed","shortpress"],"modelJSON":"{\"modelTopology\":{\"class_name\":\"Sequential\",\"config\":{\"name\":\"sequential_1\",\"layers\":[{\"class_name\":\"Conv1D\",\"config\":{\"filters\":16,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"kernel_regularizer\":null,\"kernel_constraint\":null,\"kernel_size\":[4],\"strides\":[1],\"padding\":\"valid\",\"dilation_rate\":[1],\"activation\":\"relu\",\"use_bias\":true,\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"bias_regularizer\":null,\"activity_regularizer\":null,\"bias_constraint\":null,\"name\":\"conv1d_Conv1D1\",\"trainable\":true,\"batch_input_shape\":[null,20,1],\"dtype\":\"float32\"}},{\"class_name\":\"MaxPooling1D\",\"config\":{\"pool_size\":[2],\"padding\":\"valid\",\"strides\":[2],\"name\":\"max_pooling1d_MaxPooling1D1\",\"trainable\":true}},{\"class_name\":\"Dropout\",\"config\":{\"rate\":0.1,\"noise_shape\":null,\"seed\":null,\"name\":\"dropout_Dropout1\",\"trainable\":true}},{\"class_name\":\"Conv1D\",\"config\":{\"filters\":16,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"kernel_regularizer\":null,\"kernel_constraint\":null,\"kernel_size\":[2],\"strides\":[1],\"padding\":\"valid\",\"dilation_rate\":[1],\"activation\":\"relu\",\"use_bias\":true,\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"bias_regularizer\":null,\"activity_regularizer\":null,\"bias_constraint\":null,\"name\":\"conv1d_Conv1D2\",\"trainable\":true}},{\"class_name\":\"Dropout\",\"config\":{\"rate\":0.1,\"noise_shape\":null,\"seed\":null,\"name\":\"dropout_Dropout2\",\"trainable\":true}},{\"class_name\":\"Flatten\",\"config\":{\"name\":\"flatten_Flatten1\",\"trainable\":true}},{\"class_name\":\"Dense\",\"config\":{\"units\":3,\"activation\":\"relu\",\"use_bias\":true,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"kernel_regularizer\":null,\"bias_regularizer\":null,\"activity_regularizer\":null,\"kernel_constraint\":null,\"bias_constraint\":null,\"name\":\"dense_Dense1\",\"trainable\":true}}]},\"keras_version\":\"tfjs-layers 3.8.0\",\"backend\":\"tensor_flow.js\"},\"format\":\"layers-model\",\"generatedBy\":\"TensorFlow.js tfjs-layers v3.8.0\",\"convertedBy\":null,\"weightData\":null,\"weightSpecs\":[{\"name\":\"conv1d_Conv1D1/kernel\",\"shape\":[4,1,16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D1/bias\",\"shape\":[16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D2/kernel\",\"shape\":[2,16,16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D2/bias\",\"shape\":[16],\"dtype\":\"float32\"},{\"name\":\"dense_Dense1/kernel\",\"shape\":[112,3],\"dtype\":\"float32\"},{\"name\":\"dense_Dense1/bias\",\"shape\":[3],\"dtype\":\"float32\"}]}","outputShape":3,"status":"trained","trainingAcc":1,"weights":[1007525612,1037226973,3167382906,1008831401,1026873287,3184125802,1027865142,3192716236,3190367323,3181559484,3181419064,1043398745,1040323185,1043040005,1040509500,1025547594,1040980199,1041568981,1041703053,1042175744,1037973606,3190214434,1047395369,1042473324,1036383060,3169257985,3173566051,1025624300,3191146180,3191071587,3196756350,3190156083,1045525747,3194082754,3179820776,3176435052,1050870587,3179191039,1047140727,3110775242,3150357485,3181837357,1040677241,1041518665,3188291277,3184580532,1049299003,1038875330,1049867990,1040341096,1022395939,3174996700,1046901107,3197209045,3191013135,1023323943,1038977441,1043696291,3163087302,3193263306,1031872688,3185879951,3187294984,1029377335,3143403295,3154333011,3167293298,3166005702,1002967041,0,3144797391,3147192479,1009488240,3156842127,1002359476,3162881202,987551443,3134518980,993419100,1002010044,3178302694,1052357111,1030167462,1052123201,1008873160,1045038306,1050222809,3181662449,3172969079,3195381716,1048773092,3174196752,1046875597,3179669055,1042149875,3191202444,1015468715,3173149381,3169380254,1027570138,1032137194,1037332547,3182431228,1041035604,1036889328,1046787369,3157064799,1041791399,1040230809,3188357451,1044587959,1049435210,3171281964,1049744960,3164849893,1042749801,1037182332,3189273658,3198774820,1034257677,1036743806,3182292371,1032253957,1038414802,3193063245,3161641032,3188638047,1050764745,3168173826,1036878430,3181498327,3166265596,3190557727,1014285309,1017964548,1037395902,1052154714,1034563674,3196286975,3191932933,1044402703,1008897779,3185834904,3163418714,1041796545,1003374925,3178899094,1008189779,3195383347,3156822448,3189770186,1046529617,3188996480,3181533491,3181779317,3184748480,3190537184,3189733408,1026727956,1036933135,1039800131,3198862921,3179666100,1041072550,3194325643,3175953368,3182585972,3154347949,1035528637,1023827782,1015193658,1040341986,3177513597,1035342656,1000130025,1015227116,3189011690,3190648702,1045843863,3188557750,3166682163,3136224065,1046206060,3193373600,1033782884,3189278096,1044143316,990311177,3173679112,1045693992,1044043627,3191868932,1044135167,1049657298,3142907754,3196787959,3185127282,3195987079,1047281128,1041756584,3178192956,3195731075,1039638057,3187316325,1039236457,1024524967,1033234415,1037913497,999170781,3184754144,1042364401,3188262415,3195855826,1035400148,3188269418,1001651854,3175782105,3179003534,1050094154,1041544353,1049977669,3156288118,3163000223,3198052613,1041645069,3188980634,3185464559,3167525762,1049243237,3195213148,1024992926,1027605058,1036851202,3190753917,3184553532,3174923286,1026971489,3193333199,3196992674,1034642343,1044020809,1023485317,3187974109,3191585013,3191529121,1011535416,3195702374,3192332287,1032611653,3169590706,1020660015,1048250767,3191134172,3192713247,1045386180,1038371002,3195871892,1033382182,3196496001,1042852672,1030153442,1040429348,1040313282,1032363548,1036532054,3179649294,3178997085,3190394322,3182079106,3192265830,3194674642,3197478307,3166133679,1024193918,1046493953,1027344265,3194387841,1048830195,1040481875,1043099485,1030263840,1018254348,1046792444,3198576730,3191370659,1025164570,3188902272,1040962864,1049008335,974845116,1035945403,3182987128,3188505443,1052020992,1034997288,3191888903,1050267648,3198070086,3158895609,3197226716,3190677443,3189804897,3191482224,3199014159,3165250743,1040336760,3181465810,3186085554,1042370292,3196155306,1051249426,3190842670,3170700210,3188936196,1047827176,1044364396,1036548552,3192995485,1036585198,3172374577,1031909346,3187749285,1041551939,3172396213,3191657801,1048755978,3192371444,3185360335,3195704404,3191242560,1044605790,1033962880,3189135819,3182588103,1048780483,1031867558,3173783887,3188621733,3194535228,1032025330,3194290628,1022467095,3197160143,3188284509,3184732981,3193068268,3182651536,3188527471,1010999931,1043193046,3187787253,1041371596,1043177845,1015990217,3179168577,3175697562,3193873867,1041440812,1034042719,3176585863,1043968213,1009915722,1011859461,3188705822,1041840017,1045705760,3188646246,3183543204,3196115041,3186935998,3179524592,3165855242,3164418334,1033344852,1033748966,1049349429,1033363442,1024616506,1027688470,3188606153,3192310204,3187998170,3180130767,3176397537,3192305249,3168464452,3191431027,3182755202,3181964670,1034156941,1035131226,1030046109,3175564234,3193394810,3195633656,3189996763,1036210661,3179372286,1029038566,3157409807,1044142760,1047058063,1001432826,3159630890,3193163136,1041580554,3197357165,1045228014,1038668572,1031430630,1051197087,3180899420,3152932066,1038540083,1042449913,1031906212,3186994971,1033413731,995633463,1026733726,3191936080,3197154477,3196956918,3198910951,1045243872,3198870112,3188873689,3187091920,3122327167,3190703255,1007229253,3196389180,1043581723,1040742102,3158318195,3156036199,1026337162,1035424404,1041460309,1049567557,3187706063,1025169770,3178565118,3187930701,1033566104,3178705089,3191685972,1012613582,3193882592,3188931007,1034933380,3188994118,1044395840,1049787941,1024361517,3186322533,3176275792,3174797753,3179232856,1043154168,3190173573,3176492491,3187233610,3189681785,1035436674,1006563237,3195895863,1041322977,1046692882,3148224713,3197156425,3183471196,1046927784,1032496224,3191459120,3174244291,3193287103,1019442431,3195912790,1024512135,3181712584,3196888894,1030807776,3152807950,3196244568,3173224709,3164763623,3181250665,1029321206,1047750511,1037034415,3196955905,3191986082,1047745244,1032997131,3178341798,3195738329,1037490135,1041356250,1046014941,3186236417,3182951723,1031290580,3185850424,1041611078,3155559686,1043316734,3188795559,1042575825,3165022968,3196380012,1014304379,1018826659,3183030359,1043175961,1042860197,3182557350,3180317657,3150555829,1046447604,3195586560,3181497669,3189706177,1027193227,1035957993,3183033512,3196781142,3176247831,3177778748,1029514898,1017309683,3193511748,1005462845,3180936325,3180093636,3194414975,1035203146,1033767399,3182657138,3182106275,3193686790,1045262334,3196191535,3181432441,3188050019,3194424429,3188495473,1043412264,3195530267,1024979527,1041417260,3185128272,1046443094,1035071233,1049625451,1031989541,3197707424,1048711512,3179560777,3190062907,3186991348,3163571419,3193913397,3193019751,3189436360,3185970866,3171122544,1038617115,1051338420,3173490633,1049472589,1033032970,1016344899,3146891328,3184081588,1026753567,3176983973,1051255761,1024959914,3186347865,1040031027,3187138572,3164106482,3178838487,1039973981,1036394057,1033250246,1051497934,1040673998,1024818188,1036542175,1024001398,3146171679,3149659021,1002018014,3131796894,3165260068,3155253122,1008731286,3166738352,999923860,984096704,1001268527,3149196257,3162659510,3145813285,1005798239,1004029690,3167932811,1044846032,3185432068,3185454512,1048845073,1034090197,1013949843,3188693661,3168098959,1027767951,1039289147,1026855352,3181476034,3191051409,3186306990,1044182366,1008694509,1026009119,1037206499,3181061100,1042535260,1033975431,3187721862,1028031028,1040986043,3181615696,1013022492,1031997268,1045153351,1040990788,1046746344,1045117062,1021867406,3188089848,1044209992,3172878933,3176561841,3193638977,1028924576,3185091960,1044491828,1043871110,1043196369,1038769986,3191386344,1041697544,3182105042,3172610810,1018181720,3163311006,3172707662,3169593653,1036227447,3196360615,3193863166,1041209299,3196628672,3162724288,1028306685,3187548836,3180770055,3192734086,3169463899,1036239500,996034241,3172469420,3180541886,3192652236,1026271312,3136015360,3189891326,3175981221,1046101202,3181596657,1040432749,3186160314,3179996132,1039626907,1014820914,1030990412,1035903396,3193444418,3191354217,3175768029,1034545909,3158793684,1010627523,3172231848,3186008200,1043358212,3151569936,1025913211,3196632547,3180012727,1048372744,1042675680,3194844902,3177339576,1027458496,1027002409,1025311427,1035076582,1001087101,1043055535,1038047002,3195850554,3191085418,3192184542,1024571595,3171207880,3188289054,3190028167,3168614061,3167282026,1040630334,1040664467,3173023395,1037444935,1042079057,3184114382,3180285611,1030021027,1043224621,1032088255,1008973698,1017991959,1013836161,3177999195,3103989152,3158475240,3192624355,1044174602,3188264271,1047047659,3183661789,3190649721,3192000543,3187375358,1026069415,1036670795,3176082617,1045172350,3185536337,3124790333,3170391571,1034655244,1043894660,1036804268,3165426385,1029772160,1047750874,3154150153,1017773638,1037201503,1042293294,1036856738,1039496576,1042005286,3176766174,3194486419,1042645574,998419118,1043928764,3173118408,1032219600,3188911069,3177727068,1021240737,3191822822,1021477941,1036780402,1032877680,1017578161,1042508130,3190998984,3179295327,1038593050,3179490391,3192526088,3177091612,3190074057,3178073410,1007924426,3165790217,3163215683,3182294494,3180835343,3189918981,1038222103,1026398504,3179367550,1037404861,1027723513,1021598103,3188396960,1031575681,1039966497,1045283875,3180411422,3187974043,1020023934,3169576869,1019492948,3153489032,3182976524,1007991120,3172399368,1040855858,1035261874,1013923048,3190143668,3179013143,1021351887,3145874505,3174412749,1036116945,1042080175,3195956277,1017686688,3191841116,3178898313,1037412780,3184070605,3182499694,3168291504,3137537135,3164769473,1034463977,3179949153,1046810077,3184035499,1037924791,3176818960,3174951815,1042111136,3171590797,3196812370,3176546917,3189396803,3180950081,3184316787,1046866020,1040983518,3184229796,3190671265,3184534459,1047711215,1040577733,3188265425,1018754753,1036641623,1040496338,1035764576,3143217854,3188523705,3175389237,3177280657,3190627407,1032608519,3190888279,3184958469,3182825505,1042992983,3186504185,1008661407,3189760071,1042455441,1038196385,3157581795,3190660150,3184282876,3188005063,3192744152,1037426394,1045241086,1039937876,1040055359,1036046755,3187104206,3182980193,1039964901,3190921454,1009499665,1027365169,3178321433,1039738807,1031194410,3194346695,967065408,1044995070,3176436142,1004064803,1038816611,3193464436,1024468975,3188399734,1047044611,3176605341,3177858832,3176527086,1036653621,3182852767,3174862880,3195446727,1021405663,1035096513,995721204,3194955360,3176776942,3187750590,3169291269,1006976808,3169450098,1034018724,1042194366,1031929321,1048277886,3180429107,3173674945,1019945261,3170606796,1043221613,1045288825,3179171889,3197809301,3193168864,3177698448,3167129476,3091032307,3165951600,3180872601,3145735872,3181339330,1041537782,3188402477,3176143101,999207350,3154592702,3153284796]} 2 | -------------------------------------------------------------------------------- /pxt/extension.ts: -------------------------------------------------------------------------------- 1 | import { compileModelAndFullValidate, loadFlatJSONModel } from "../src/driver" 2 | import { setBackend, loadLayersModel, SymbolicTensor } from "@tensorflow/tfjs" 3 | 4 | export function inIFrame() { 5 | try { 6 | return typeof window !== "undefined" && window.self !== window.top 7 | } catch (e) { 8 | return typeof window !== "undefined" 9 | } 10 | } 11 | 12 | const CHANGE = "change" 13 | export const READ = "read" 14 | export const MESSAGE_PACKET = "messagepacket" 15 | const HIDDEN = "hidden" 16 | const SHOWN = "shown" 17 | //const SENDER = "jacdac-editor-extension" 18 | const CONNECT = "connect" 19 | 20 | export interface ReadResponse { 21 | code?: string 22 | json?: string 23 | jres?: string 24 | } 25 | 26 | export interface SMap { 27 | [index: string]: T 28 | } 29 | 30 | const fakeSample = ` 31 | export function _sample() { 32 | while (true) { 33 | basic.showString("_sample() missing") 34 | } 35 | return [@samples@] 36 | }` 37 | 38 | const accelSample = ` 39 | export function _sample() { 40 | return [ 41 | input.acceleration(Dimension.X) / 1024, 42 | input.acceleration(Dimension.Y) / 1024, 43 | input.acceleration(Dimension.Z) / 1024 44 | ] 45 | }` 46 | 47 | const buttonSample = ` 48 | let _button = Button.A 49 | export function _sample() { 50 | return [input.buttonIsPressed(_button) ? 1 : 0] 51 | } 52 | 53 | //% block="set ml button %button" blockId="ml_set_button" 54 | export function setButton(button: Button) { 55 | _button = button 56 | } 57 | ` 58 | 59 | export class MakeCodeEditorExtensionClient { 60 | private readonly pendingCommands: { 61 | [key: string]: { 62 | action: string 63 | resolve: (resp: any) => void 64 | reject: (e: any) => void 65 | } 66 | } = {} 67 | private readonly extensionId: string = inIFrame() 68 | ? window.location.hash.substr(1) 69 | : undefined 70 | private _target: any // full apptarget 71 | private _connected = false 72 | private _visible = false 73 | 74 | constructor() { 75 | this.handleMessage = this.handleMessage.bind(this) 76 | window.addEventListener("message", this.handleMessage, false) 77 | // notify parent that we're ready 78 | this.init() 79 | } 80 | 81 | emit(id: string, arg?: any) { 82 | console.log("EMIT", id, { arg }) 83 | } 84 | 85 | log(msg: string) { 86 | console.log(`ML4F-PXT: ${msg}`) 87 | } 88 | 89 | get target() { 90 | return this._target 91 | } 92 | 93 | get connected() { 94 | return this._connected 95 | } 96 | 97 | get visible() { 98 | return this._visible 99 | } 100 | 101 | private setVisible(vis: boolean) { 102 | if (this._visible !== vis) { 103 | this._visible = vis 104 | this.emit(CHANGE) 105 | } 106 | } 107 | 108 | private nextRequestId = 1 109 | private mkRequest( 110 | resolve: (resp: any) => void, 111 | reject: (e: any) => void, 112 | action: string, 113 | body?: any 114 | ): any { 115 | const id = "ml_" + this.nextRequestId++ 116 | this.pendingCommands[id] = { action, resolve, reject } 117 | return { 118 | type: "pxtpkgext", 119 | action, 120 | extId: this.extensionId, 121 | response: true, 122 | id, 123 | body, 124 | } 125 | } 126 | 127 | private sendRequest(action: string, body?: any): Promise { 128 | this.log(`send ${action}`) 129 | if (!this.extensionId) return Promise.resolve(undefined) 130 | 131 | return new Promise((resolve, reject) => { 132 | const msg = this.mkRequest(resolve, reject, action, body) 133 | window.parent.postMessage(msg, "*") 134 | }) 135 | } 136 | 137 | private handleMessage(ev: any) { 138 | const msg = ev.data 139 | if (msg?.type !== "pxtpkgext") return 140 | if (!msg.id) { 141 | switch (msg.event) { 142 | case "extinit": 143 | this.log(`init`) 144 | this._target = msg.target 145 | this._connected = true 146 | this.emit(CONNECT) 147 | this.emit(CHANGE) 148 | break 149 | case "extloaded": 150 | this.log(`loaded`) 151 | break 152 | case "extshown": 153 | this.setVisible(true) 154 | this.refresh() 155 | this.emit(SHOWN) 156 | this.emit(CHANGE) 157 | break 158 | case "exthidden": 159 | this.setVisible(false) 160 | this.emit(HIDDEN) 161 | this.emit(CHANGE) 162 | break 163 | case "extdatastream": 164 | this.emit("datastream", true) 165 | break 166 | case "extconsole": 167 | this.emit("console", msg.body) 168 | break 169 | case "extmessagepacket": 170 | this.emit(MESSAGE_PACKET, msg.body) 171 | break 172 | default: 173 | console.debug("Unhandled event", msg) 174 | } 175 | } else { 176 | const { action, resolve, reject } = 177 | this.pendingCommands[msg.id] || {} 178 | delete this.pendingCommands[msg.id] 179 | 180 | if (msg.success && resolve) resolve(msg.resp) 181 | else if (!msg.success && reject) reject(msg.resp) 182 | // raise event as well 183 | switch (action) { 184 | case "extinit": 185 | this._connected = true 186 | this.emit("CONNECT") 187 | this.emit(CHANGE) 188 | break 189 | case "extusercode": 190 | // Loaded, set the target 191 | this.emit("readuser", msg.resp) 192 | this.emit(CHANGE) 193 | break 194 | case "extreadcode": 195 | // Loaded, set the target 196 | this.emit(READ, msg.resp) 197 | this.emit(CHANGE) 198 | break 199 | case "extwritecode": 200 | this.emit("written", undefined) 201 | break 202 | } 203 | } 204 | } 205 | 206 | private async init() { 207 | this.log(`initializing`) 208 | await this.sendRequest("extinit") 209 | this.log(`connected`) 210 | await this.refresh() 211 | } 212 | 213 | private async refresh() { 214 | this.log(`refresh`) 215 | const r = await this.read() 216 | } 217 | 218 | async read(): Promise { 219 | if (!this.extensionId) { 220 | const r: ReadResponse = {} 221 | this.emit(READ, r) 222 | return r 223 | } else { 224 | const resp: ReadResponse = await this.sendRequest("extreadcode") 225 | return resp 226 | } 227 | } 228 | 229 | async readUser() { 230 | await this.sendRequest("extusercode") 231 | } 232 | 233 | async write( 234 | code: string, 235 | json?: string, 236 | jres?: string, 237 | dependencies?: SMap 238 | ): Promise { 239 | if (!this.extensionId) { 240 | // Write to local storage instead 241 | this.emit("written", undefined) 242 | } else { 243 | await this.sendRequest("extwritecode", { 244 | code: code || undefined, 245 | json: json || undefined, 246 | jres: jres || undefined, 247 | dependencies, 248 | }) 249 | } 250 | } 251 | 252 | async queryPermission() { 253 | await this.sendRequest("extquerypermission") 254 | } 255 | 256 | async requestPermission(console: boolean) { 257 | await this.sendRequest("extrequestpermission", { 258 | console, 259 | }) 260 | } 261 | 262 | async dataStreamConsole(console: boolean) { 263 | await this.sendRequest("extdatastream", { 264 | console, 265 | }) 266 | } 267 | 268 | async dataStreamMessages(messages: boolean) { 269 | await this.sendRequest("extdatastream", { 270 | messages, 271 | }) 272 | } 273 | } 274 | 275 | export interface FlatJSONModel { 276 | name: string 277 | inputTypes: string[] // ["x","y","z"]; ["pressure"] 278 | labels: string[] 279 | modelJSON: unknown 280 | inputInterval: number // ms 281 | weights: number[] // UInt32Array (little endian) 282 | } 283 | 284 | export async function start() { 285 | setBackend("cpu") 286 | 287 | const options: SMap = { 288 | f16: true, 289 | } 290 | const pxtClient = new MakeCodeEditorExtensionClient() 291 | 292 | const maindiv = document.createElement("div") 293 | maindiv.style.background = "white" 294 | document.body.appendChild(maindiv) 295 | 296 | const status = div("") 297 | maindiv.append(status) 298 | setStatus("waiting for model file") 299 | 300 | const d = div("Drop TF.JS model file here") 301 | d.style.padding = "2em" 302 | d.style.margin = "1em 0em" 303 | d.style.border = "1px dotted gray" 304 | maindiv.append(d) 305 | addCheckbox("f16", "Use float16 type") 306 | 307 | const dropbox = maindiv 308 | dropbox.addEventListener("dragenter", stopEv, false) 309 | dropbox.addEventListener("dragover", stopEv, false) 310 | dropbox.addEventListener( 311 | "drop", 312 | e => { 313 | setStatus("reading model") 314 | stopEv(e) 315 | const file = e.dataTransfer.files.item(0) 316 | const reader = new FileReader() 317 | reader.onload = async e => { 318 | try { 319 | const mod: FlatJSONModel = JSON.parse( 320 | e.target.result as string 321 | ) 322 | await compileModel(mod, file.name) 323 | } catch (e) { 324 | console.error(e.stack) 325 | setError(e.message) 326 | } 327 | } 328 | reader.readAsText(file) 329 | }, 330 | false 331 | ) 332 | 333 | function shapeElements(shape: number[]) { 334 | let res = 1 335 | for (const s of shape) if (s != null) res *= s 336 | return res 337 | } 338 | 339 | function toCamelCase(name: string) { 340 | return name.replace(/(^|( +))(.)/g, (_0, _1, _2, l) => l.toUpperCase()) 341 | } 342 | 343 | async function compileModel(mod: FlatJSONModel, fileName: string) { 344 | const name = mod.name || fileName 345 | const ma = loadFlatJSONModel(mod) 346 | const m = await loadLayersModel({ load: () => Promise.resolve(ma) }) 347 | const inpTen = m.getInputAt(0) as SymbolicTensor 348 | const numClasses = shapeElements( 349 | (m.getOutputAt(0) as SymbolicTensor).shape 350 | ) 351 | const labels = (mod.labels || []).slice() 352 | while (labels.length > numClasses) labels.pop() 353 | while (labels.length < numClasses) labels.push("class " + labels.length) 354 | const inputShape = inpTen.shape 355 | const samplingPeriod = mod.inputInterval || 100 356 | setStatus("compiling...") // can't see that... 357 | const res = await compileModelAndFullValidate(m, { 358 | verbose: false, 359 | includeTest: true, 360 | float16weights: options.f16, 361 | optimize: true, 362 | }) 363 | setStatus("compiled!") 364 | const shape2 = inputShape.filter(v => v != null) 365 | const samplesInWindow = shape2.shift() 366 | const elementsInSample = shapeElements(shape2) 367 | 368 | let code = 369 | `// model: ${name}; input: ${JSON.stringify( 370 | inputShape 371 | )}; sampling at: ${samplingPeriod}ms\n` + 372 | `// ${res.memInfo}\n` + 373 | `// ${res.timeInfo}\n` 374 | 375 | code += "const enum MLEvent {\n" 376 | let idx = 0 377 | for (let lbl of labels) { 378 | lbl = lbl.replace(/_/g, " ") 379 | code += ` //% block="${lbl}"\n` 380 | code += ` ${toCamelCase(lbl)} = ${idx},\n` 381 | idx++ 382 | } 383 | code += `}\n\n` 384 | code += `namespace ml {\n` 385 | code += ` 386 | let _classifier: Classifier 387 | export function classifier() { 388 | if (_classifier) return _classifier 389 | _classifier = new Classifier(input => _model.invoke(input), _sample) 390 | _classifier.detectionThreshold = 0.7 391 | _classifier.samplingInterval = ${Math.round( 392 | samplingPeriod 393 | )} // ms 394 | _classifier.samplesOverlap = ${Math.max( 395 | samplesInWindow >> 2, 396 | 1 397 | )} 398 | _classifier.samplesInWindow = ${samplesInWindow} 399 | _classifier.elementsInSample = ${elementsInSample} 400 | _classifier.noiseClassNo = -1 // disable 401 | _classifier.noiseSuppressionTime = 500 // ms 402 | _classifier.start() 403 | return _classifier 404 | } 405 | 406 | /** 407 | * Run some code when a particular ML event is detected. 408 | */ 409 | //% blockId=ml_on_event block="on ml event %condition" 410 | //% blockGap=12 411 | export function onEvent(mlevent: MLEvent, handler: () => void) { 412 | classifier().onEvent(mlevent, handler) 413 | } 414 | ` 415 | 416 | let sample = fakeSample 417 | if (elementsInSample == 1) sample = buttonSample 418 | else if (elementsInSample == 3) sample = accelSample 419 | 420 | const exampleSample = [] 421 | for (let i = 0; i < elementsInSample; ++i) exampleSample.push(i) 422 | code += 423 | "\n" + 424 | sample.replace("@sample@", JSON.stringify(exampleSample)) + 425 | "\n" 426 | 427 | code += `export const _model = new ml4f.Model(\n` + "hex`" 428 | for (let i = 0; i < res.machineCode.length; ++i) { 429 | code += ("0" + res.machineCode[i].toString(16)).slice(-2) 430 | if ((i + 3) % 32 == 0) code += "\n" 431 | } 432 | code += "`);\n" 433 | code += "\n} // namespace ml\n" 434 | 435 | console.log(code.replace(/([a-f0-9]{64}\n)+/, "...")) 436 | await pxtClient.write(code) 437 | setStatus("done; you can Go back now") 438 | } 439 | 440 | function stopEv(e: Event) { 441 | e.stopPropagation() 442 | e.preventDefault() 443 | } 444 | 445 | function div(text: string): HTMLDivElement { 446 | const d = document.createElement("div") 447 | d.textContent = text 448 | return d 449 | } 450 | 451 | function setError(msg: string) { 452 | status.style.color = "red" 453 | status.textContent = "Error: " + msg 454 | } 455 | 456 | function setStatus(msg: string) { 457 | status.style.color = "green" 458 | status.textContent = msg 459 | } 460 | 461 | function addCheckbox(field: string, name: string) { 462 | const lbl = document.createElement("label") 463 | lbl.textContent = name 464 | const box = document.createElement("input") 465 | lbl.prepend(box) 466 | box.type = "checkbox" 467 | box.checked = !!options[field] 468 | box.addEventListener("change", () => { 469 | if (box.checked) options[field] = !!box.checked 470 | }) 471 | maindiv.appendChild(lbl) 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/testing.ts: -------------------------------------------------------------------------------- 1 | import * as tf from '@tensorflow/tfjs' 2 | import * as U from './util' 3 | import { CompileResult, Options } from './compiler'; 4 | import { compileAndTest, compileModelAndFullValidate, setRandomWeights } from './driver'; 5 | import { testFloatConv } from './float16'; 6 | import { mkRuntime } from './runtime'; 7 | 8 | 9 | function randomModel() { 10 | const model = tf.sequential(); 11 | 12 | const inputShape = [U.randomInclusive(1, 100), U.randomInclusive(1, 50), U.randomInclusive(1, 32)] 13 | const kernelSize = [U.randomInclusive(1, 5), U.randomInclusive(1, 5)] 14 | const strides = [U.randomInclusive(1, 3), U.randomInclusive(1, 3)] 15 | const filters = U.randomInclusive(1, 5) 16 | 17 | kernelSize[0] = Math.min(kernelSize[0], inputShape[0]) 18 | kernelSize[1] = Math.min(kernelSize[1], inputShape[1]) 19 | 20 | const desc = `Conv2D ${inputShape} X ${kernelSize} @${strides} -> ${filters}` 21 | 22 | model.add(tf.layers.conv2d({ 23 | inputShape, 24 | kernelSize, 25 | filters, 26 | strides, 27 | padding: 'valid', 28 | activation: 'relu', 29 | kernelInitializer: 'varianceScaling' 30 | })); 31 | 32 | // make sure weights are deterministic 33 | for (const l of model.layers) 34 | setRandomWeights(l) 35 | 36 | model.name = desc 37 | return model 38 | } 39 | 40 | function logThumb(cres: CompileResult) { 41 | let str = cres.thumb 42 | function hex2(n: number) { 43 | return ("0" + n.toString(16)).slice(-2) 44 | } 45 | str += "// BUF: " 46 | for (const v of cres.machineCode) str += hex2(v) 47 | console.log(str) 48 | console.log(cres.memInfo) 49 | console.log(cres.timeInfo) 50 | } 51 | 52 | 53 | export async function runBrowser(seed: number) { 54 | tf.setBackend('cpu'); 55 | const t0 = Date.now() 56 | 57 | U.seedRandom(seed || 220) 58 | 59 | testFloatConv() 60 | 61 | // const m = await tf.loadLayersModel("./models/gestures.tfjsmodel.json") 62 | const sample = sampleModel("oneD") 63 | const float16weights = true 64 | const optimize = false 65 | const opts: Options = { verbose: true, float16weights, optimize } 66 | logThumb(compileAndTest(sample, opts)) 67 | 68 | await testAllModels({ verbose: false, optimize }) 69 | 70 | console.log(Date.now() - t0 + "ms") 71 | } 72 | 73 | function getSampleModels(): SMap { 74 | return { 75 | id: [tf.layers.inputLayer({ 76 | inputShape: [10, 3, 1] 77 | })], 78 | conv2d: [tf.layers.conv2d({ 79 | inputShape: [50, 3, 1], 80 | kernelSize: [4, 4], 81 | filters: 16, 82 | strides: [1, 1], 83 | padding: 'same', 84 | activation: 'relu', 85 | kernelInitializer: 'varianceScaling' 86 | })], 87 | dense: [ 88 | tf.layers.flatten({ 89 | inputShape: [10, 3, 1], 90 | }), 91 | tf.layers.dense({ 92 | units: 5, 93 | activation: "softmax", 94 | })], 95 | padding: [ 96 | tf.layers.inputLayer({ 97 | inputShape: [50, 3, 1] 98 | }), 99 | tf.layers.conv2d({ 100 | filters: 16, 101 | kernelSize: 4, 102 | strides: 1, 103 | padding: "same", 104 | activation: "relu" 105 | }) 106 | ], 107 | dspDense: [ 108 | tf.layers.inputLayer({ inputShape: [33] }), 109 | tf.layers.dense({ units: 20, activation: "relu" }), 110 | tf.layers.dense({ units: 10, activation: "relu" }), 111 | tf.layers.dense({ units: 3, activation: "softmax" }), 112 | ], 113 | noDsp: [ 114 | tf.layers.inputLayer({ inputShape: [150] }), 115 | tf.layers.reshape({ targetShape: [50, 3, 1] }), 116 | tf.layers.conv2d({ filters: 16, kernelSize: 4, strides: 1, padding: "same", activation: "relu" }), 117 | tf.layers.maxPooling2d({ poolSize: 2, strides: 2, padding: "same" }), 118 | tf.layers.dropout({ rate: 0.1 }), 119 | tf.layers.conv2d({ filters: 16, kernelSize: 2, strides: 1, padding: "same", activation: "relu" }), 120 | tf.layers.maxPooling2d({ poolSize: 2, strides: 2, padding: "same" }), 121 | tf.layers.flatten(), 122 | tf.layers.dense({ units: 30, activation: "relu" }), 123 | tf.layers.dense({ units: 3, activation: "softmax" }), 124 | ], 125 | tfjsGest: [ 126 | tf.layers.conv2d({ 127 | inputShape: [50, 3, 1], 128 | kernelSize: [4, 3], 129 | filters: 16, 130 | strides: [1, 1], 131 | padding: 'valid', 132 | activation: 'relu', 133 | kernelInitializer: 'varianceScaling' 134 | }), 135 | tf.layers.maxPooling2d({ poolSize: [2, 1], strides: [2, 1] }), 136 | tf.layers.dropout({ rate: 0.1 }), 137 | tf.layers.conv2d({ 138 | kernelSize: [2, 1], 139 | filters: 16, 140 | strides: 1, 141 | activation: 'relu', 142 | kernelInitializer: 'varianceScaling' 143 | }), 144 | tf.layers.dropout({ rate: 0.1 }), 145 | tf.layers.conv2d({ 146 | kernelSize: [2, 1], 147 | filters: 16, 148 | strides: 1, 149 | activation: 'relu', 150 | kernelInitializer: 'varianceScaling' 151 | }), 152 | tf.layers.dropout({ rate: 0.1 }), 153 | tf.layers.flatten(), 154 | tf.layers.dense({ 155 | units: 4, 156 | kernelInitializer: 'varianceScaling', 157 | activation: 'softmax' 158 | }) 159 | ], 160 | microSpeech: [ 161 | tf.layers.conv2d({ 162 | inputShape: [49, 40, 1], 163 | kernelSize: [10, 8], 164 | filters: 8, 165 | padding: "same", 166 | activation: "relu", 167 | strides: 2, 168 | }), 169 | tf.layers.flatten(), 170 | tf.layers.dense({ 171 | units: 4, 172 | kernelInitializer: 'varianceScaling', 173 | activation: 'softmax' 174 | }) 175 | ], 176 | oneD: [ 177 | tf.layers.conv1d({ 178 | inputShape: [50, 4], 179 | kernelSize: [4], 180 | strides: 1, 181 | filters: 16, 182 | activation: 'relu' 183 | }), 184 | tf.layers.maxPooling1d({ poolSize: [2] }), 185 | tf.layers.dropout({ rate: 0.1 }), 186 | tf.layers.conv1d({ 187 | kernelSize: [2], 188 | strides: 1, 189 | filters: 16, 190 | activation: 'relu' 191 | }), 192 | tf.layers.maxPooling1d({ poolSize: [2] }), 193 | tf.layers.dropout({ rate: 0.1 }), 194 | tf.layers.conv1d({ 195 | kernelSize: [2], 196 | strides: 1, 197 | filters: 16, 198 | activation: 'relu' 199 | }), 200 | tf.layers.dropout({ rate: 0.1 }), 201 | tf.layers.flatten(), 202 | tf.layers.dense({ 203 | units: 3, 204 | activation: "softmax", 205 | }) 206 | ], 207 | oneD2: [ 208 | tf.layers.conv1d({ 209 | inputShape: [10, 3], 210 | kernelSize: [4], 211 | strides: 1, 212 | padding: 'same', 213 | filters: 1, 214 | activation: 'relu' 215 | }), 216 | tf.layers.flatten(), 217 | tf.layers.dense({ 218 | units: 3, 219 | activation: "softmax", 220 | }) 221 | ], 222 | oneD2_x: [ 223 | tf.layers.conv1d({ 224 | inputShape: [23, 3], 225 | kernelSize: [4], 226 | strides: 1, 227 | padding: 'same', 228 | filters: 16, 229 | activation: 'relu' 230 | }), 231 | tf.layers.flatten(), 232 | tf.layers.dense({ 233 | units: 3, 234 | activation: "softmax", 235 | }) 236 | ], 237 | avgPool: [ 238 | tf.layers.inputLayer({ inputShape: [150] }), 239 | tf.layers.reshape({ targetShape: [50, 3, 1] }), 240 | tf.layers.conv2d({ filters: 16, kernelSize: 4, strides: 1, padding: "same", activation: "relu" }), 241 | tf.layers.avgPooling2d({ poolSize: 2, strides: 2, padding: "valid" }), 242 | tf.layers.flatten(), 243 | tf.layers.dense({ units: 3, activation: "softmax" }), 244 | ], 245 | avgPool2: [ 246 | tf.layers.inputLayer({ inputShape: [150] }), 247 | tf.layers.reshape({ targetShape: [50, 3, 1] }), 248 | tf.layers.conv2d({ filters: 16, kernelSize: 4, strides: 1, padding: "same", activation: "relu" }), 249 | tf.layers.avgPooling2d({ poolSize: [2, 1], strides: [2, 1], padding: "valid" }), 250 | tf.layers.flatten(), 251 | tf.layers.dense({ units: 3, activation: "softmax" }), 252 | ], 253 | avgPool3: [ 254 | tf.layers.inputLayer({ inputShape: [150] }), 255 | tf.layers.reshape({ targetShape: [50, 3, 1] }), 256 | // tf.layers.conv2d({ filters: 16, kernelSize: 4, strides: 1, padding: "same", activation: "relu" }), 257 | tf.layers.avgPooling2d({ poolSize: [8, 1], strides: [2, 1], padding: "valid" }), 258 | tf.layers.flatten(), 259 | tf.layers.dense({ units: 3, activation: "softmax" }), 260 | ], 261 | avgPool4: [ 262 | tf.layers.inputLayer({ inputShape: [2700] }), 263 | tf.layers.reshape({ targetShape: [27, 1, 100] }), 264 | tf.layers.avgPooling2d({ poolSize: [27, 1], strides: [27, 1], padding: "valid" }), 265 | tf.layers.flatten(), 266 | tf.layers.dense({ units: 3, activation: "softmax" }), 267 | ], 268 | depth0: [ 269 | tf.layers.inputLayer({ inputShape: [15, 1, 1] }), 270 | tf.layers.depthwiseConv2d({ kernelSize: [5, 1], depthMultiplier: 4, strides: 1, useBias: false }) 271 | ], 272 | depth1: [ 273 | tf.layers.inputLayer({ inputShape: [213, 1, 15] }), 274 | tf.layers.depthwiseConv2d({ kernelSize: [10, 1], depthMultiplier: 4, strides: 2, useBias: false }) 275 | ], 276 | batch1: [ 277 | tf.layers.inputLayer({ inputShape: [213, 1, 15] }), 278 | tf.layers.batchNormalization({}) 279 | ], 280 | batch2: [ 281 | tf.layers.inputLayer({ inputShape: [213, 1, 100] }), 282 | tf.layers.batchNormalization({}) 283 | ], 284 | singleDimBatchNorm: [ 285 | tf.layers.inputLayer({ inputShape: [24] }), 286 | tf.layers.batchNormalization({}), 287 | tf.layers.dense({ 288 | units: 16, 289 | activation: "relu" 290 | }), 291 | tf.layers.dense({ 292 | units: 3, 293 | activation: "softmax" 294 | }) 295 | ], 296 | } 297 | } 298 | 299 | let _models: SMap 300 | 301 | export function allSampleModels() { 302 | if (!_models) _models = getSampleModels() 303 | return Object.keys(_models).map(sampleModel) 304 | } 305 | 306 | export function sampleModel(id: string) { 307 | const model = tf.sequential(); 308 | model.name = id 309 | 310 | if (!_models) _models = getSampleModels() 311 | 312 | const layers = _models[id] 313 | if (!layers) { 314 | let msg = `no such model ${id}; options:\n` 315 | for (const name of Object.keys(_models)) { 316 | msg += `- ${name}: ${_models[name].length} layer(s)\n` 317 | } 318 | throw new Error(msg) 319 | } 320 | 321 | for (const l of layers) 322 | model.add(l); 323 | 324 | // make sure weights are deterministic 325 | for (const l of model.layers) 326 | setRandomWeights(l) 327 | 328 | return model; 329 | } 330 | 331 | export async function testAllModels(opts: Options) { 332 | const t0 = Date.now() 333 | opts = U.flatClone(opts) 334 | for (const m of allSampleModels()) { 335 | console.log(`***\n*** ${m.name}\n***`) 336 | console.log(opts.float16weights ? "--- F16" : "--- F32") 337 | await compileModelAndFullValidate(m, opts) 338 | opts.float16weights = !opts.float16weights 339 | console.log(opts.float16weights ? "--- F16" : "--- F32") 340 | await compileModelAndFullValidate(m, opts) 341 | } 342 | console.log(`\n*** All OK (${Date.now() - t0}ms)\n`) 343 | } 344 | 345 | export type EvalSample = number | number[] | number[][] | number[][][] 346 | export interface EvalData { 347 | x: EvalSample[] 348 | y: number[][] 349 | } 350 | 351 | function flattenSample(s: EvalSample) { 352 | const res: number[] = [] 353 | const rec = (v: any) => { 354 | if (Array.isArray(v)) 355 | v.forEach(rec) 356 | else if (typeof v == "number") 357 | res.push(v) 358 | else 359 | throw new Error("invalid input") 360 | } 361 | rec(s) 362 | return res 363 | } 364 | 365 | function argmax(r: ArrayLike) { 366 | let maxI = 0 367 | let max = r[0] 368 | for (let i = 1; i < r.length; ++i) { 369 | if (r[i] > max) { 370 | max = r[i] 371 | maxI = i 372 | } 373 | } 374 | return maxI 375 | } 376 | 377 | export function evalModel(cres: CompileResult, data: EvalData) { 378 | let numOK = 0 379 | const dim = data.y[0].length 380 | const confusion = U.range(dim).map(_ => U.range(dim).map(_ => 0)) 381 | for (let i = 0; i < data.x.length; ++i) { 382 | const predProb = cres.execute(flattenSample(data.x[i])) 383 | const pred = argmax(predProb) 384 | const ok = argmax(data.y[i]) 385 | confusion[pred][ok]++ 386 | if (pred == ok) numOK++ 387 | } 388 | 389 | let r = "" 390 | 391 | r += `Accuracy: ${(numOK / data.x.length).toFixed(4)}\n` 392 | for (let i = 0; i < dim; i++) { 393 | for (let j = 0; j < dim; j++) { 394 | r += (" " + confusion[i][j]).slice(-5) 395 | } 396 | r += "\n" 397 | } 398 | 399 | return r 400 | } 401 | 402 | function flatten(d: any): number[] { 403 | const r: number[] = [] 404 | if (Array.isArray(d)) { 405 | for (const e of d) { 406 | for (const q of flatten(e)) { 407 | r.push(q) 408 | } 409 | } 410 | } else { 411 | r.push(d) 412 | } 413 | return r 414 | } 415 | 416 | export function runModel(js: string, data: any) { 417 | const { modelFromRuntime, inputSize } = new Function(js)() 418 | const runModel: (inp: number[]) => Float32Array = modelFromRuntime(mkRuntime) 419 | const reqSize: number = inputSize 420 | 421 | let inputs = data.x ? data.x : data 422 | let outputs = data.y ? data.y : [] 423 | 424 | if (Array.isArray(inputs) && flatten(inputs[0]).length == reqSize) { 425 | for (let i = 0; i < inputs.length; ++i) { 426 | execModel(inputs[i], outputs[i]) 427 | } 428 | } else { 429 | execModel(inputs, outputs) 430 | } 431 | 432 | function execModel(inp: number[], exp: any) { 433 | if (inp.length != reqSize) { 434 | console.error(`bad input size - need ${reqSize} got ${inp.length}`) 435 | return 436 | } 437 | const res = runModel(inp) 438 | const max = argmax(res) 439 | if (typeof exp == "number") { 440 | if (max == exp) { 441 | console.log("OK!", max) 442 | } else { 443 | const tmp = Array.from(res) 444 | tmp.sort() 445 | console.log(`got ${max} (${res[max]}), exp ${exp} (we have ${res[exp]}); median ${tmp[tmp.length >> 1]}`) 446 | } 447 | } else { 448 | console.log(max) 449 | } 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /models/microbit_acc_gestures.json: -------------------------------------------------------------------------------- 1 | {"name":"model_editor-model","inputShape":[20,3],"inputTypes":["x","y","z"],"inputInterval":50.30000001192093,"labels":["still","tilt_left","tilt_right","shake"],"modelJSON":{"modelTopology":{"class_name":"Sequential","config":{"name":"sequential_4","layers":[{"class_name":"Conv1D","config":{"filters":16,"kernel_initializer":{"class_name":"VarianceScaling","config":{"scale":1,"mode":"fan_avg","distribution":"normal","seed":null}},"kernel_regularizer":null,"kernel_constraint":null,"kernel_size":[4],"strides":[1],"padding":"valid","dilation_rate":[1],"activation":"relu","use_bias":true,"bias_initializer":{"class_name":"Zeros","config":{}},"bias_regularizer":null,"activity_regularizer":null,"bias_constraint":null,"name":"conv1d_Conv1D10","trainable":true,"batch_input_shape":[null,20,3],"dtype":"float32"}},{"class_name":"MaxPooling1D","config":{"pool_size":[2],"padding":"valid","strides":[2],"name":"max_pooling1d_MaxPooling1D7","trainable":true}},{"class_name":"Dropout","config":{"rate":0.1,"noise_shape":null,"seed":null,"name":"dropout_Dropout10","trainable":true}},{"class_name":"Conv1D","config":{"filters":16,"kernel_initializer":{"class_name":"VarianceScaling","config":{"scale":1,"mode":"fan_avg","distribution":"normal","seed":null}},"kernel_regularizer":null,"kernel_constraint":null,"kernel_size":[2],"strides":[1],"padding":"valid","dilation_rate":[1],"activation":"relu","use_bias":true,"bias_initializer":{"class_name":"Zeros","config":{}},"bias_regularizer":null,"activity_regularizer":null,"bias_constraint":null,"name":"conv1d_Conv1D11","trainable":true}},{"class_name":"MaxPooling1D","config":{"pool_size":[2],"padding":"valid","strides":[2],"name":"max_pooling1d_MaxPooling1D8","trainable":true}},{"class_name":"Dropout","config":{"rate":0.1,"noise_shape":null,"seed":null,"name":"dropout_Dropout11","trainable":true}},{"class_name":"Conv1D","config":{"filters":16,"kernel_initializer":{"class_name":"VarianceScaling","config":{"scale":1,"mode":"fan_avg","distribution":"normal","seed":null}},"kernel_regularizer":null,"kernel_constraint":null,"kernel_size":[2],"strides":[1],"padding":"valid","dilation_rate":[1],"activation":"relu","use_bias":true,"bias_initializer":{"class_name":"Zeros","config":{}},"bias_regularizer":null,"activity_regularizer":null,"bias_constraint":null,"name":"conv1d_Conv1D12","trainable":true}},{"class_name":"Dropout","config":{"rate":0.1,"noise_shape":null,"seed":null,"name":"dropout_Dropout12","trainable":true}},{"class_name":"Flatten","config":{"name":"flatten_Flatten4","trainable":true}},{"class_name":"Dense","config":{"units":4,"activation":"softmax","use_bias":true,"kernel_initializer":{"class_name":"VarianceScaling","config":{"scale":1,"mode":"fan_avg","distribution":"normal","seed":null}},"bias_initializer":{"class_name":"Zeros","config":{}},"kernel_regularizer":null,"bias_regularizer":null,"activity_regularizer":null,"kernel_constraint":null,"bias_constraint":null,"name":"dense_Dense4","trainable":true}}]},"keras_version":"tfjs-layers 3.8.0","backend":"tensor_flow.js"},"format":"layers-model","generatedBy":"TensorFlow.js tfjs-layers v3.8.0","convertedBy":null,"weightData":null,"weightSpecs":[{"name":"conv1d_Conv1D10/kernel","shape":[4,3,16],"dtype":"float32"},{"name":"conv1d_Conv1D10/bias","shape":[16],"dtype":"float32"},{"name":"conv1d_Conv1D11/kernel","shape":[2,16,16],"dtype":"float32"},{"name":"conv1d_Conv1D11/bias","shape":[16],"dtype":"float32"},{"name":"conv1d_Conv1D12/kernel","shape":[2,16,16],"dtype":"float32"},{"name":"conv1d_Conv1D12/bias","shape":[16],"dtype":"float32"},{"name":"dense_Dense4/kernel","shape":[32,4],"dtype":"float32"},{"name":"dense_Dense4/bias","shape":[4],"dtype":"float32"}]},"modelStats":{"total":{"name":"TOTAL","inputShape":[20,3],"outputShape":[4],"arenaBytes":1608,"codeBytes":2564,"weightBytes":5544,"unoptimizedCycles":54950,"optimizedCycles":48362},"layers":[{"name":"conv1d_Conv1D10","unoptimizedCycles":21462,"optimizedCycles":19186,"arenaBytes":1328,"inputShape":[20,3],"outputShape":[17,16],"weightBytes":448,"codeBytes":434},{"name":"max_pooling1d_MaxPooling1D7","unoptimizedCycles":4209,"optimizedCycles":3617,"arenaBytes":1600,"inputShape":[17,16],"outputShape":[8,16],"weightBytes":0,"codeBytes":72},{"name":"dropout_Dropout10","unoptimizedCycles":0,"optimizedCycles":0,"arenaBytes":0,"inputShape":[8,16],"outputShape":[8,16],"weightBytes":0,"codeBytes":0},{"name":"conv1d_Conv1D11","unoptimizedCycles":19142,"optimizedCycles":16802,"arenaBytes":960,"inputShape":[8,16],"outputShape":[7,16],"weightBytes":1088,"codeBytes":706},{"name":"max_pooling1d_MaxPooling1D8","unoptimizedCycles":1809,"optimizedCycles":1537,"arenaBytes":640,"inputShape":[7,16],"outputShape":[3,16],"weightBytes":0,"codeBytes":72},{"name":"dropout_Dropout11","unoptimizedCycles":0,"optimizedCycles":0,"arenaBytes":0,"inputShape":[3,16],"outputShape":[3,16],"weightBytes":0,"codeBytes":0},{"name":"conv1d_Conv1D12","unoptimizedCycles":6822,"optimizedCycles":5730,"arenaBytes":320,"inputShape":[3,16],"outputShape":[2,16],"weightBytes":1088,"codeBytes":666},{"name":"dropout_Dropout12","unoptimizedCycles":0,"optimizedCycles":0,"arenaBytes":0,"inputShape":[2,16],"outputShape":[2,16],"weightBytes":0,"codeBytes":0},{"name":"flatten_Flatten4","unoptimizedCycles":0,"optimizedCycles":0,"arenaBytes":0,"inputShape":[2,16],"outputShape":[32],"weightBytes":0,"codeBytes":0},{"name":"dense_Dense4","unoptimizedCycles":1506,"optimizedCycles":1490,"arenaBytes":144,"inputShape":[32],"outputShape":[4],"weightBytes":272,"codeBytes":278}]},"outputShape":4,"status":"trained","trainingAcc":0.9750000238418579,"weights":[3200482900,3180887003,3184142801,3178523697,3183061750,1049593818,1037089184,1053644868,3190045066,3183040905,3191885443,3190982125,3195420175,3196009732,3191174177,1046271135,3187119021,1040234439,1054162976,3180832582,1040167754,3177596694,3182870628,3192581570,3170909782,1031478889,1052604698,1035364517,3184292801,3197470994,1051618220,1044548027,1054178850,1024279603,1044937409,3170917915,1028143649,1043717577,3184836654,3194780023,3197286709,3180276896,3173966172,1044392231,1037865695,3184454694,3186233850,3201038971,3197169962,3190233478,3179053953,1045469021,1022969794,3164478103,1050247781,3184331235,3188028466,1032701813,1025151022,3199792895,3194035196,1035859917,1039447251,3176295490,3168665638,1028868838,1045346143,3194075627,1045441157,1041009996,3197483940,1027600719,1049439139,1050507199,1054811703,1034264169,1031375044,1034028508,3170760775,1032015988,1034871066,3191075142,3175406674,1049442005,1042217207,3198461618,3181921844,3197654018,3184495922,1035789407,1031702897,1029374292,1038759182,3186122332,1014256477,3181407393,1051170917,1048004340,1031181001,3196374758,1042710240,3179357954,1051130875,3167638913,3191152634,3186505296,1044046968,3197243194,1049422422,1040787386,1032732823,3170890321,3181071190,3171256059,1050989314,1004662818,1049244712,3179045003,1044527332,3189955390,1038034055,3168736750,1047456830,3174633647,1042138805,1031219304,3191101753,1043941681,3167841464,3182042559,1039440791,1055502440,1042739139,1048802856,3181821914,3179552469,3191560304,3190388754,3166224083,3165866303,1053042820,3187647139,1031392702,3198093085,1051563682,1043424460,1038385952,3190992278,3185602495,3181420292,1053436524,3192664079,3177853021,1035991400,3181609573,3181965362,1050206613,3190730410,3149854179,3195104746,1050638145,3184316161,1024353504,3181695522,1051209061,1023322088,3201972436,1044082068,1045235692,1042594456,1047505961,1051728847,3189563881,1039350693,1050612936,1030312950,3198377701,3205060431,1035412605,1050341477,3192966087,3192362555,1000456542,3191435088,3179240840,1041731995,1033705154,1036682627,1053264072,1051346036,1041479316,3187526907,1021785464,3176183140,1034787497,3178788545,1035242664,3177776628,1045508155,1033234423,1027860287,1021618340,1037295857,1031206197,1042749569,3162334924,1024428263,3188708840,1043154709,1054785378,1033967605,1011249244,1053741713,1034126655,3179919648,1022467894,1053274968,1051414003,3171773913,3178368551,1048472012,1050324699,1044003406,1052982355,3192778287,1038087213,1050791920,1031508460,1049773394,3201438211,3156620623,3198576799,1048597807,1054852920,1017656223,1052462224,3194525528,1026198658,3186347895,1052138508,1051160369,3189503172,1033261685,3190817115,3193830954,1044289123,1043049370,3198028994,3174223865,1040932670,3183256513,3196269045,3175256964,1035240268,3191579763,3198809800,3198990276,1027104420,1049592742,1053786233,1039577077,3202727693,3156885060,3183223023,3167124878,3185816781,1053433082,1053075177,3194910420,3185244832,1042639235,1047701655,1040567313,1018637622,3185736163,989306296,1046602442,1057161416,1036654770,3164940990,1042250252,1029598774,3188558427,3189495970,3191424882,3190337310,3154678048,1034648571,3198498697,1055877913,1050771976,984581620,3179870248,3192755731,1045933861,3171476134,1052402482,1015047633,3178267677,1032322741,3196845499,1034841036,1037463963,1026785687,1055219040,3179741197,3173334442,3173580346,1040784127,1007949906,3188121617,1043921661,3175192425,1043869689,1034729497,1028308013,1054133428,3191943903,1051718663,3197068936,3181397458,1016730481,1050719390,3171637492,1047145469,3197805870,1026129703,3166228058,1041633049,1041131306,1029243936,1051150128,3173555443,3197721134,1049255103,1038970408,3172172297,3190279693,3185796878,3191734705,3196136076,3182204868,1032666482,3187578967,3177973437,1004111402,1033328674,1040595468,1040632630,1044437066,1044885335,993218578,3172941963,3154359649,1051915933,3188229804,3190116275,1038459184,1040525350,3189390382,1038756373,3174438541,3197026408,1032182290,3193880636,3163658653,3186556223,3187437782,1029713516,3187989715,3191287175,3199627250,1011414243,1054266919,1052127409,3180175958,1029503370,3174142886,3191088720,3191206276,3194596816,3196634644,1032748429,3199645902,3198234136,1001857095,1029206207,1034928111,3185979196,3183519129,1044277372,3188036048,1034686881,1024300239,1043018713,3187683601,1049802092,1044071385,1027099925,3191238588,1040328357,1034986936,3195192797,1024813841,1041251220,1042948951,3180705031,3196650382,3180259429,1055754585,1045545115,1044275486,1045895264,1025372099,1045920237,3178903550,3188234529,1049436504,1043155499,3172055397,1038153086,3175202526,3180321539,1038232983,1046961907,1052120754,3196667653,1045417316,3175428245,1022722518,1043975886,3179229699,1038208509,1053797031,1038553766,3174183474,1052233217,1052560209,1043850581,3190265571,1046678650,1034135036,1044158357,1046819261,1048782048,3190789288,1043326382,3194704363,3197682788,3183794640,1015470892,1037891134,3156640275,3163911051,1052866517,3167570243,3185112825,3196198343,1035503693,1044156865,3195982040,1026982646,1028001123,1046680552,1032385504,1057929015,1050332455,3177720208,1049261332,3172767479,3139696392,3195413072,1057368775,1058800532,3199629349,3190764963,3197562839,1008587700,1051974425,3186133511,3180313638,1050312172,1048264751,1052770564,1042400877,3202352057,1039725749,3204233966,1037527745,3132198218,1048700660,1003484799,3195208063,1031589361,1051887102,1046980820,1017450596,3178996872,1039700824,3196700510,3194496444,1037922657,1050730932,3183768232,1049000713,1033482908,1022876165,3166347304,3201079258,1023116049,1049194366,3146684181,3198457417,3196786184,3183510307,1049889806,1041770718,3197691720,3198524463,1048549675,3197007165,3156117846,1049199764,1056585059,3180549313,1048835307,1049145105,1050933082,3172980326,3172128876,3194252208,1025823088,1044577379,1049350001,1043979389,3192633424,3187949031,1049603573,3196142947,3188432413,1022400780,1039543146,1035506998,1033415784,1051989816,1049655663,1046315482,1046474189,1057733124,3199673836,1036182206,3188188781,1043638236,1049478008,1037672028,3189228856,3184599027,1035775497,1007793873,1040082104,1054582623,3160365900,1036179918,3157340246,3188945544,1032401809,3192972683,3170347930,3159291413,1022850449,1020590080,1041330153,1048885865,1044864425,1049330126,1048413144,3191851902,1026878752,3172610069,1051238459,3164511879,3176211378,3182050218,1041949231,3159616985,1038996744,1040942021,1035753998,1042663610,1046777018,1034308810,1039033073,3192467981,3172748295,1009874512,1046613861,3162180387,1051385538,1032236930,3187971048,1042757036,3196188624,1044623718,1052548756,1023540929,3189263883,3155645604,3186649688,1044726631,3181815821,1046394230,1040720137,3172394769,1050674542,1000607197,1054986218,1041505901,3189109756,3200093684,3166486658,3197175697,3167488694,3193733718,1044089208,3192291471,1047306833,1028411381,3173442244,3181258524,1052413907,1050306685,3190385158,1049569205,1025465442,3196789183,3193534861,3196237732,3196798814,1044417704,1043628013,1042315123,1042550311,1025128813,3180541013,1028755245,1053293333,1051108535,1044476125,3197423047,3185825069,1050787357,3177754705,1040103190,3173396441,1042384080,3184661840,1038954942,1044595668,1040498013,3191586299,3197975308,3187062300,3192200633,1042033945,1045993577,1040228273,1050741842,3171731885,3181811788,3177611407,1050716631,1044190101,1036424954,3128510577,1051860487,1053293139,1043473617,3154781821,1049430062,1031344434,1049021487,1052360781,1035748991,3187767885,3176151432,3152098508,1013102145,1051309207,1036810186,1035465111,3154424894,1051711092,1041065394,1048881856,3193167399,3163198313,1039081471,1015530115,3196178339,3184599306,3192651139,1031541434,3188025801,3174233824,3178497849,3196777002,1042112484,3173376445,3174481549,1016373693,3185753470,3134852376,1041760923,3199974363,1035939780,1047410362,1032970058,3187784967,3181121996,1032866685,1020097019,3180614171,3187793813,1037866301,3185842563,1040085897,1022355315,3183650600,3186762909,3182360661,1042875760,1028297632,1045425689,3185957477,1023183984,3153449291,3175385629,3177084165,1051553011,3182806299,3159581793,3195761725,1039413660,1027566157,1042811345,3175010854,3165487275,3196823199,3187519751,1046995944,1007297164,1025964573,3186186523,3151392494,1056595380,1054404288,3199586933,3197507045,1035815605,3182599998,3176150067,1034610680,3199014421,3190919107,1025940846,1043352203,3203147382,1036841552,3186506456,1052794866,3187745571,1041928191,1034757268,3197484172,1027651079,3199752303,1049698899,1043859074,1028114821,3190778373,3199013314,3171570374,3175007118,1053161141,1052059275,3173448189,3184766471,1054965251,3090731620,3198935021,3197386301,1026942483,1032925646,3192713626,3182957087,1036294484,3175595450,3180138825,3179381297,1042739825,3187230320,3196478571,3174707877,1049165649,1012183654,1024418517,1050817840,1026173830,1044170209,1025758294,3191531742,3201011803,3202574314,1040849023,3193669826,1054462670,3178756677,1045436382,1042873825,999265824,3183209053,3184058838,1052310138,1041878351,1032216533,3180604241,3178322105,997428778,1052774519,3191695252,1049917749,3190502476,3148203816,1047710898,3188788249,3167936998,1036389472,3193021212,1050777919,1018230297,3200009006,1043394886,1049902920,3179131769,1031704279,3199076829,1041323650,3189180249,3195783233,1040862176,3188467411,1035496427,3184835906,1039662185,3183453020,3184241220,1035174858,1022212886,3194216451,3188281068,3183792370,1025511193,3190406547,1027818887,3202002246,1043606337,1038764007,1037738402,1025799180,3197943683,1048241028,3194687672,3194120388,3189515670,1050634674,3192737002,1023810788,3188262258,3171282716,1039505102,3190225508,3178320027,1057731914,1049595708,3170725824,1018808876,3176756493,3165918137,1050970348,3198202059,3169307424,3196488044,3192586530,1044064881,3205197870,1051124237,1052659649,3185758580,3174093881,3163942518,1038551263,1024630577,3195681399,1054529212,3178098778,1051764201,3150335266,1046546052,3175388837,3196729879,3195415622,1042419248,1053165737,1008887383,3196670319,1040592935,1037976242,3186043366,3197049758,1043430465,1036742486,1048271461,1052153609,1053547673,3199591403,1030446514,3168930885,1042248919,1047887892,3191029949,3194980185,3178288445,3195932313,3183389092,1048557265,1050884334,1041530244,1035489036,3130026841,1043211758,1045507510,1052450262,1036288727,3184526652,1037003549,3191768012,1049001589,1048853008,1040146883,1050442231,3176377708,3182143943,1035748257,3190185328,3181833121,1015705642,1034579875,1047089834,3178553862,3185968016,1048622306,1032347887,3171471284,1049047551,1027176504,1024614124,1052960417,1042806765,1047408885,1041835060,1044910537,3181924853,1043635616,1043281470,1027105828,1046210710,3185642052,3182688500,1049068000,1041822832,1050037271,3195270768,3194482618,1016072252,1015927622,3178016123,3189810616,1045809895,3190374234,1054362081,3182972962,1049081525,1031091930,1044201263,1038179855,3191201870,3188640089,3161324218,3183176560,3162516273,3130688354,1042043321,3198378565,3185756173,1056902068,3179396889,3195856128,3190090564,3199644658,3188063081,1046219331,1051401069,3167664904,1042040661,1046690242,3185147250,3180806939,3187512314,3200576113,3157115189,3136584965,1048642362,3202517277,1040482342,3190856109,3198052332,3157627115,1053620398,1026050105,3188521719,3191641997,3200439095,3175158428,3185574371,1034346866,1049959807,3191377319,3195073370,3191029886,1056604889,3162633938,3188911916,3189136810,3181530656,3179443652,3193655389,3198293888,3174140991,1052886394,3155442427,1052183924,1044690405,3193237963,3157599571,1045336585,1051938919,3184257085,3198403004,1042807209,1055304179,1041014763,1036064039,1050358339,3196608057,1053536319,973953959,3190360631,3195120488,3178936650,1048703212,1032081588,1041976611,3190830473,1033698810,3188783509,3190702051,3172041126,3189457786,1053158527,3196251914,3192915568,3189975980,3188580181,1027351666,1042835613,3191059170,1032529897,3198295126,3180068040,1025634190,1033514022,1045397916,1048649171,3183954659,1023119051,3169571017,3162868376,3196107126,1018256930,1012810854,1044622156,1046015608,1034049258,1047204118,3197976455,1046939322,1034197819,3190705171,3185784614,3192172244,1054847105,3134314485,1041377080,3187714522,1037638448,3192452756,1040660477,3179643186,3190235582,1044787199,3195259547,1038006076,1046280710,1036228082,3162990725,3196466645,1026560963,3194852309,3176519988,3192558898,3181355674,1032151266,3176611255,3186983867,3197499442,1005197181,3177441347,1040404632,1056935307,1052200792,3195663728,1011282885,1036574843,1050004867,3172984758,3190910646,3201504308,1019208798,1049101263,1048805897,3205265955,1041018305,1053966745,1039233716,3198711558,3181684056,3149813615,1021368027,1024455573,1041946705,1037054436,1028951818,1050264990,1046714447,3188222442,999733375,1037909807,1034744627,1032589209,3169812176,1041356539,1037251078,1047352276,1042621269,1037135124,1032160046,1051298941,1043623180,1051749034,1054633643,3193602428,1049639260,1049293133,1047159637,1051276980,1036403855,1022767520,3186135604,3159771819,3137548309,3192638411,1038218939,1034212483,1050739366,1046914884,3197546902,1029082605,3189880624,1037115935,3192677469,3195591156,3183490246,1041442500,1042873154,3176557766,3177656019,3196481343,1040730711,3177052460,1046547294,1022054049,1013241983,3170814850,1029694268,1010802327,1049175739,3157713210,1044579168,3173464744,3156502832,3172245666,3164304869,3167692801,3162791254,1034035198,1040839251,1005395506,1049437846,1048010065,1040767143,3179362535,3137396053,3181199133,3194980506,3180637084,3188140395,3194473778,3199485780,3158953376,1041316589,1052683994,1045360326,1047882640,1048820692,1035013141,1035150978,3164500206,1053267578,1047418726,1016343781,995612197,3188620541,1039639133,3174284164,1028095418,1050189568,3173135188,1044240316,1046116262,1032666374,1045441790,3174155715,1040339996,3190180966,1057184023,1022209531,3188090160,3196987434,3197187449,1044084847,3154786531,3206131251,3188344880,3196805271,1044461317,1017956496,1043604124,3196079476,1051036282,1049378971,1042881893,1046312833,1033497155,3202449981,1023469549,1049077334,3195140681,1034028486,3207911814,1047491336,1043127907,3180793907,1047108802,1040938881,3201440413,3205679004,1040867664,1034959183,3204512082,1050628300,1015970355,1017986464,3200451483,3196081446,1050680141,3168804908,3177670429,1020185304,3162809371,3204510024,1044756166,3183174630,3197358348,1043755094,1049636157,3206728974,3173245429,1035540135,3208031998,1046377931,3191208747,1032454883,3197904002,3184011989,3196054400,3195090328,3190564645,1048401922,1059931611,1049930887,3146004806,3200084718,1028545158,3145750933,1049416573,1052181702,3205094757,1024952783,1050454596,1017198497,3201610381,3205735159,3189964550,1056698247,1046141484,3173302630,3196487833,1052266938,3190889795,1049227070,3196374431,3165668322,3206749921,1052650090,1047711771,1039344206,1052098250,1048145287,3195383677,3206736731,1040559550,3190633327,1035647975,3179693789,1046299431,1040633251,3189025337,3150737990,1051692198,1049739255,3205007618,3189779944,1052054163,3204417491,3200387258,3201151296,3197699360,3188232446,1041124479,3205544199,3189253159,1051904842,3200051997,1029369788,3191840185,1061806965,3201717744,3187404080,1038491103,3203275956,3197931026,1040634540,1038155174,1029996100,1015492415,3190770132]} -------------------------------------------------------------------------------- /models/accel.json: -------------------------------------------------------------------------------- 1 | {"name":"classifier1","inputShape":[20,6],"inputTypes":["x","y","z","x","y","z"],"inputInterval":99.89999997615814,"labels":["still","swipe_right","swipe_left"],"modelJSON":"{\"modelTopology\":{\"class_name\":\"Sequential\",\"config\":{\"name\":\"sequential_2\",\"layers\":[{\"class_name\":\"Conv1D\",\"config\":{\"filters\":16,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"kernel_regularizer\":null,\"kernel_constraint\":null,\"kernel_size\":[4],\"strides\":[1],\"padding\":\"valid\",\"dilation_rate\":[1],\"activation\":\"relu\",\"use_bias\":true,\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"bias_regularizer\":null,\"activity_regularizer\":null,\"bias_constraint\":null,\"name\":\"conv1d_Conv1D2\",\"trainable\":true,\"batch_input_shape\":[null,20,6],\"dtype\":\"float32\"}},{\"class_name\":\"MaxPooling1D\",\"config\":{\"pool_size\":[2],\"padding\":\"valid\",\"strides\":[2],\"name\":\"max_pooling1d_MaxPooling1D1\",\"trainable\":true}},{\"class_name\":\"Dropout\",\"config\":{\"rate\":0.1,\"noise_shape\":null,\"seed\":null,\"name\":\"dropout_Dropout2\",\"trainable\":true}},{\"class_name\":\"Conv1D\",\"config\":{\"filters\":16,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"kernel_regularizer\":null,\"kernel_constraint\":null,\"kernel_size\":[2],\"strides\":[1],\"padding\":\"valid\",\"dilation_rate\":[1],\"activation\":\"relu\",\"use_bias\":true,\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"bias_regularizer\":null,\"activity_regularizer\":null,\"bias_constraint\":null,\"name\":\"conv1d_Conv1D3\",\"trainable\":true}},{\"class_name\":\"MaxPooling1D\",\"config\":{\"pool_size\":[2],\"padding\":\"valid\",\"strides\":[2],\"name\":\"max_pooling1d_MaxPooling1D2\",\"trainable\":true}},{\"class_name\":\"Dropout\",\"config\":{\"rate\":0.1,\"noise_shape\":null,\"seed\":null,\"name\":\"dropout_Dropout3\",\"trainable\":true}},{\"class_name\":\"Conv1D\",\"config\":{\"filters\":16,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"kernel_regularizer\":null,\"kernel_constraint\":null,\"kernel_size\":[2],\"strides\":[1],\"padding\":\"valid\",\"dilation_rate\":[1],\"activation\":\"relu\",\"use_bias\":true,\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"bias_regularizer\":null,\"activity_regularizer\":null,\"bias_constraint\":null,\"name\":\"conv1d_Conv1D4\",\"trainable\":true}},{\"class_name\":\"Dropout\",\"config\":{\"rate\":0.1,\"noise_shape\":null,\"seed\":null,\"name\":\"dropout_Dropout4\",\"trainable\":true}},{\"class_name\":\"Flatten\",\"config\":{\"name\":\"flatten_Flatten2\",\"trainable\":true}},{\"class_name\":\"Dense\",\"config\":{\"units\":3,\"activation\":\"softmax\",\"use_bias\":true,\"kernel_initializer\":{\"class_name\":\"VarianceScaling\",\"config\":{\"scale\":1,\"mode\":\"fan_avg\",\"distribution\":\"normal\",\"seed\":null}},\"bias_initializer\":{\"class_name\":\"Zeros\",\"config\":{}},\"kernel_regularizer\":null,\"bias_regularizer\":null,\"activity_regularizer\":null,\"kernel_constraint\":null,\"bias_constraint\":null,\"name\":\"dense_Dense2\",\"trainable\":true}}]},\"keras_version\":\"tfjs-layers 3.8.0\",\"backend\":\"tensor_flow.js\"},\"format\":\"layers-model\",\"generatedBy\":\"TensorFlow.js tfjs-layers v3.8.0\",\"convertedBy\":null,\"weightData\":null,\"weightSpecs\":[{\"name\":\"conv1d_Conv1D2/kernel\",\"shape\":[4,6,16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D2/bias\",\"shape\":[16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D3/kernel\",\"shape\":[2,16,16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D3/bias\",\"shape\":[16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D4/kernel\",\"shape\":[2,16,16],\"dtype\":\"float32\"},{\"name\":\"conv1d_Conv1D4/bias\",\"shape\":[16],\"dtype\":\"float32\"},{\"name\":\"dense_Dense2/kernel\",\"shape\":[32,3],\"dtype\":\"float32\"},{\"name\":\"dense_Dense2/bias\",\"shape\":[3],\"dtype\":\"float32\"}]}","outputShape":3,"status":"trained","trainingAcc":0.7916666865348816,"weights":[1032180131,1012211850,3190782591,3187977795,3190938170,3179877375,1041213821,3171692732,3183326536,3169834548,1035959331,3162026577,1041145072,3155386733,3158326405,1042674422,3189491761,1030207070,1035664493,3188190792,3196872173,1032792002,1031690664,1033333522,3181023526,3165964640,3187828983,3172947536,1005119674,1037226840,1029606871,1029144825,1037071321,3179915892,3196852056,1040837000,3195728406,3174582244,1046668736,1034055382,1041011756,3187342827,3175742654,1047107233,3165028482,3195769364,3190028841,3179432095,3188488741,3172346043,3189615135,3157856918,3171566682,1049430350,1033232726,1041036340,1041441305,1040260950,1038917214,1027010658,3193377858,3168630835,1049277705,1023664633,989250059,1025907406,3177050247,3193622570,1026205566,1038093931,3188751411,3170994641,1045022020,1000362608,1044886041,3130302415,1006601847,3192221878,3187523803,1020185416,3188895307,3184185947,3171226951,1036014423,1045767151,1044920114,3187658986,1044163347,3185479588,3188737808,1027568736,1035890105,1036081460,3193302433,1040418521,1043239490,3182410576,1032198192,1037329614,3197359405,3185934778,1029528574,3194790959,1004928843,3184952692,3163032017,3168045034,3179199456,1039759757,3197936099,1041333697,1037104970,3162135454,1038527212,3179872020,1019872991,1038929959,1012310271,3190699063,3182322414,1038380840,1042010737,1048051203,1008022429,1015234883,1023998379,1026753274,1040360468,1047730412,1046929833,1047400594,3184972869,1034031922,1042023608,1031920437,3142929585,3198115934,1048692379,1031822917,3167717723,1038554223,1038827976,3192405336,3180460211,1008280355,1035918031,1046070081,1018068720,1026182552,3183519140,1043427258,1043878788,3188598336,1025755821,3188140018,3196723059,1030007451,1040285770,1033758415,1028466337,1024593131,1035133587,3189248385,1044711992,3181950503,3197161925,1043271099,3185753533,3188332084,3191536717,1041922397,3139590689,1045964674,3192901815,3172666185,1034420669,3187989305,3163601237,3190497417,3181978449,1032006694,3191441927,1023638528,1048058351,3174051965,3181763592,3170150264,1044590833,3182025833,3188088022,1037952338,3183900931,3180028002,1033400084,1001846132,3192121886,1021321472,1024388186,3189024253,1028769714,1046045550,1046486491,1032441458,1021163725,3156668789,1038364604,1040367062,1045878084,3171931407,1036282313,1044723345,3182767506,3193198895,1040363778,3197007138,3188666753,1032439903,3167738747,1013181485,1045340018,3196583874,3149775665,3183592845,3191081003,1046514169,3175731682,3179365139,3189713758,1046299827,3178550555,1050112426,1013177219,3181394773,1047506971,1047299926,3185333859,1008571917,3166512855,1028122874,1045594308,1043890768,3181835188,1017884001,1041498111,1018938900,1032927048,3172109364,3169658581,1030624174,1040515611,1043492251,1008348714,1030501624,3180895322,1046577696,3192851565,3192383548,3171821563,1029512752,1033445064,1049705929,1039397248,1042810808,1043648745,1042255899,970056334,1044272000,3180848736,1022045408,3174671471,1049889611,3186419190,3182462774,3186287986,1033096951,3186180832,1036681012,3182528801,994769221,1043292155,3138848111,1030034019,3178937378,1039268073,1036904567,3196509469,3192255451,3187958937,1042487554,3190939356,1043676299,1039421168,3177850878,3194077366,1037667429,3193150736,1024800398,3173482654,1011123433,3185992685,1043650852,1026074471,1044112705,3196941925,3169110622,1037850783,1037813144,1040498930,1032132967,3150776429,1040391298,1042756454,1039357830,1018643926,3184416600,1041535239,3192601231,3173716131,3177765425,3197418521,3133260924,1045908201,3173949596,3190605638,3182482134,1013097589,1042808388,1043620625,1039676721,3183315116,3188475322,1049218250,1031331101,3175186894,3173382700,3154464811,1046321383,1044941775,3194504570,3196204624,3182895358,1032929934,1034256584,1048800620,1042935363,3164648211,3163208320,3191895223,3197279101,3195934727,3192289409,1022750616,3179361992,3194034543,1042488661,3179794139,1033760244,3173205747,1028997374,1024807818,3192639068,1038341706,3170074193,3176308274,3189788254,3197476323,1032069312,1037201372,1026542573,1035172931,993972905,3186006227,3177295355,3184520682,3193042734,1048304314,3196764691,3193159920,1035243401,3190182300,1044008588,1047177806,3162154342,3192109083,1021819859,3157520272,1015158689,3125897418,3171873665,3143423143,1027640747,1022309111,3156380836,1011988643,1025936113,1010990186,1025000700,1017005855,3176716813,3162761604,3190066280,1021366628,1048808770,1034650322,3183370197,3190291848,3137208480,1026576448,3191498110,3190201767,1048698524,1050282604,1041784035,3172864502,1051302443,1043099003,1040684775,3164122290,3199480430,3180460721,3160573661,3189656110,3189796983,3178039078,1024582872,1044006765,1019962580,3199453647,1036528404,1042487454,1025823615,3190968756,1051492475,1035916419,3189085941,3175501855,1050863878,3159793749,3198007059,3196362621,1050917209,993433787,1049322896,1036706412,3182859504,1049418073,3186835666,3169417803,1047642385,1050288711,1020455965,1036384433,3188825347,3189417214,1038860011,3189715578,3197639648,3166222848,3193413596,1037796010,1045563360,1046510905,1021797759,3180114428,1033494726,1050086862,1050725596,1035234777,1015578547,1047196450,1010382746,3185532520,3189828176,3193522370,3185363473,1048799113,1020176113,1046692497,3190274730,1042316905,1037520919,3180504691,3189151551,3192936411,3183750024,3192453033,3190509938,3172977519,3168154605,3170764662,3183984502,3194513090,1038289551,1026272839,1033792106,1007042813,3192916350,3188331661,3183595013,1043574457,3163264518,1046354881,1031026458,1045734121,3179351875,3189866028,1036307835,3194539335,3191406886,1048372503,1047039095,1043262514,1040336620,3184314848,1043566248,3189452639,3191550898,3185882683,3195653205,1031969152,3187924855,3171236305,3165191174,3189740091,1039364567,1040238994,1028344285,1033588027,1043080796,3196365014,3168341061,3174112334,3188336040,1042614412,1031943949,3194210804,3185465771,3183183842,3177270762,1049302093,1021974639,1037038953,1031087767,3186344260,1051605927,3195044067,3194604440,1051204518,3181823612,1033071586,3193167471,1034738887,1035610492,1037344121,1012731283,3178761607,1043110220,3182657102,1028216177,1051867967,3191238983,1035082657,3191892148,1040632565,3178014152,3179776037,3197270542,1036322854,3154949253,1045361992,1044651964,1024897187,3196667904,1047903430,3130280352,1035462025,1034803890,1032139387,3150091625,3167945097,3177632813,3197215153,1007222609,1040878048,3168094077,3171940455,1011440396,1020892948,1043550253,3196564652,1041837792,1016691995,3196031980,3182639096,3182442723,1049560982,1030263325,1036217600,3178379240,3181472117,1044717283,3185795691,3190223975,1049090004,3184908917,3191249047,1026301918,1046256725,1052257242,3169646227,1036169240,3189746057,3194347866,1034657868,1041205751,1045349362,3181169537,1037117237,3179489817,3194815457,3196382217,3195031209,3178320386,3188449227,3192298258,3197460829,1043077638,1023113522,1042443454,3197341493,3197809703,3164437848,3175273753,1038084809,3189191858,1051624779,3173484355,1039134913,3196397382,1021472228,3168925934,3198671094,3198503493,3188648242,3172366795,3199001096,3192605142,3175102132,1011959790,998062100,1034652587,1036328040,1037270701,1041019217,3191550455,1019530665,3154529441,1036517062,1010615565,1051184416,3185445398,3176952819,3183466193,1032651897,1041696623,1035716565,1036475816,1041663042,1016414761,1040917707,1048654155,3186958008,1035773790,3178307997,3189928574,3183585909,3185820778,3189647700,3176549656,1042566682,3192267871,1045479083,3194984681,3182271049,1018753360,3190794251,3197205186,3195258015,3190324970,1023874892,3197375003,1027712884,3194337567,1045518909,1040363313,3176464942,3188062284,1036754041,1030866304,3185579789,993439261,3197188680,3196337308,1011909191,1041129181,3191211762,3172193928,1023251878,1030288520,1023211998,3173007818,1024791558,1045824602,3198021400,3152652827,1040389459,1033632598,1051937519,1015517475,1051544615,3152281677,3182627306,3190819127,1033658431,3187540032,1045754350,3188727709,3196570493,1015095018,1036474898,3161958395,1048603353,1043619423,3189162810,3198607260,3187031646,1049773029,1048753117,1044694005,1041181900,3190864037,1031505463,3181577950,1000036525,3166023378,3177254208,3178627656,1014400293,1045274803,3188214917,3159951285,1038016554,3176682340,1049070759,3183301359,3166300397,1031645160,1024799683,3176458331,1043807379,1022683292,1019359362,1046322594,3189144054,1019674842,3189742352,3190501623,3192657419,1034724519,3193414031,3186156187,3163321744,3161464459,3187964876,3190308320,1042086518,1032958609,1025260688,1052776553,3172368444,1016173545,3180447871,996998330,1015140345,3177465089,1031991945,1034016157,3197100546,3193897838,1039898731,3191850204,1028153967,1044688802,1036955370,1023212899,1045110920,1043749598,1042723076,1047328015,1040758511,1032585390,1035990797,3183867241,1042666072,1034516058,3183255025,3194435725,1047443553,1023146442,3185822749,1027627076,1033252857,1039942326,3187779996,3183307978,1031897873,3193607162,1041396767,3186940422,1019890299,1049694137,1031696586,3196143437,3181586495,3181641987,1040439831,3179925019,1034160410,3164270306,3187780896,1046098299,3194849613,1043358508,1040876145,3190054983,3187442662,1026143369,1043915197,3176163563,1036808627,3194220192,3185643626,3195270389,3195359325,3188530781,3180081945,1019002780,1046377787,1032378679,1039548995,3188168791,1052274103,1042604804,3195847812,3179703956,1022052997,3186109374,1048714935,1044032733,1028309464,3183788011,3197352945,1043327123,1040509745,1001478319,3189335629,3155757803,1030548264,1009453082,1046694526,3178193937,1048287056,3184493413,3187818843,3196337528,1049300191,3193444432,3196394582,1024865449,3193711433,3198857243,3198698036,1050048609,1040523526,1048997120,3174522270,3190693296,3191197136,1026902966,1030927968,3192852614,3189945906,3185113165,3197587531,3186923799,3183168637,3180431611,3167745590,3192199545,1036541531,1039668278,3184993093,1030300894,3188453379,1049261408,3199096666,1041278500,1041696092,1041482241,1041516513,1051337964,3182303617,1030035107,1021204038,3153155539,1029604960,1008033774,975335125,3162800948,1032490687,3164872322,1025009964,1035677120,3174832838,3170597372,1018545642,1020717616,3154905095,1037980596,1026813336,3197799515,3197306596,1035076212,3197936025,3183308259,1042094679,1050156973,3197312473,1038918965,1022670403,3173782959,1042105204,3166630042,3198276452,1024000945,3184036916,1048494527,1037372699,3192347229,1049797687,3172596110,1045490973,1016795685,3193960389,3159688576,3193015466,3196303463,1045586529,3196640713,998359824,1035246859,3158542586,1042824843,3150899351,3188336047,1036181502,1023817891,3194014499,1048877069,1042203692,1048643021,1025383800,3192414935,1049953263,1007767355,3170953421,1052418950,3179589428,3192780451,1026611339,3188924977,1042097756,1052096189,1044328873,1038052506,1047660490,1034408943,3176320105,3187905121,3177733725,3169929285,3170647868,1035555938,1034482793,3197382359,3185284773,1017413456,1047999069,1024490918,3185969138,1014519129,1045686893,1042360506,3196433494,3197015900,1015462289,1035100371,3189355433,1051219453,3185607698,3167989523,3179959546,3188597915,3193124048,3184769772,3194771930,998583127,3190053798,1016367778,3198154450,1033666905,3197956442,3164002348,3193269002,3197573260,3194963139,3166979550,1042356280,1040805956,3169135327,3193251306,3182038272,1021522155,3189704859,1032308070,3194672461,1040362079,1025050198,1042566955,1043101556,1050850243,3198044558,1026701300,3123309809,3187872308,1028483686,1033846000,1042138555,3164422362,3183320950,1047376797,3183831334,1047392658,3151331566,1034720176,1029981462,3175254752,1044221902,3187459189,1007982898,1047962322,3173339790,1020611796,1048885260,1024896905,1027555147,3188507836,1036979657,3192981849,1035787010,1024746995,1041174651,1040467820,3189255491,3165429908,3153957395,1043293331,1041509592,3159130697,1032403240,3189103044,1043627903,3156045308,3188669307,1040559408,3172993498,3188927555,1044688038,1020686113,3179834079,3179948463,1035792237,1041281306,1000900017,1050731532,3179777388,3198460101,1049405291,1050790224,1043552835,3176430399,3181606330,1041144311,3154786028,3153834135,3196219657,1001305460,3193853733,3181313854,1029280408,3175812063,1049502912,1038855352,3186203024,3179903710,1027547777,1004681494,3180577556,3198645667,3182255928,3155870684,3166672378,3188277953,1049094822,1037705185,3196251268,1047740113,1033945594,3187323041,3190642823,1039852669,1045695326,3176314839,3175898108,1025132486,1031042962,3166104919,3186086724,3198165599,3192902681,3175215916,1010570054,3185556913,1050653950,1037431430,1030616916,3153166808,3150751100,3195105037,3191872668,3179133628,3164659022,3179433639,1020847930,3198163962,1042743747,1032403705,1046354834,3190677894,3184315425,1050750065,3173232951,1042617978,1028211973,998237209,3181156344,3189195793,3188071512,1048862278,1038771965,1032282970,3165437995,3196778991,1046684342,3194961366,3154298527,1045808100,1036487487,1034526991,1047843634,1035637346,3189813399,1042333490,1049763973,1039176714,1023007118,1044427047,3165236264,3172019986,3187997705,1026278460,1041630107,1043268173,3192321672,1042966586,3172869530,1029501474,1019320489,1043655544,3184062296,1046839639,1046068114,3143354207,3185033591,1040512511,1034199383,1033366446,1049080845,1050853889,1035186022,1043213277,1038276899,1036333066,3197447847,3172610459,3186795175,1042438894,3189196247,1044542362,1021374809,3186920500,3176347610,3192169366,3197482537,1037122644,3181816197,3178674941,3191372888,3183170072,3183489603,1036761523,1040222802,1032300144,3186959274,3196863759,1034147221,1050810525,3187857942,1031588840,3196278649,3178908880,3194708134,3184796615,1031722027,3176556068,1042790061,1041682585,3171203506,3173649956,1043542431,1024271480,1007793791,1040727375,1023333262,1030066046,1038135184,3172177870,3188633237,1048501703,3197113848,1042380291,1039196242,1050058799,1049061313,3188896612,1051983909,3190454453,1045760735,3173352923,3187625532,3194577685,1001630596,3192951313,1048589511,1025918265,1040800712,3179433425,1023837554,3194914229,3192163127,960207236,1050803067,1046937513,3195025532,1033359333,1034009419,1042660445,1050644157,3193135845,3191434857,3188517730,3189540139,3182386772,1035780924,1034209868,3180636823,3174102960,3171429872,3194092962,3179736043,1037127596,3194904156,1012552665,3177904079,3194564478,3192982869,1046311914,3178215768,3176455269,1043917796,3198220867,3197023463,3147912725,1045301409,3186934685,3182683908,1042294664,3179574950,3187955018,1032052049,3197224399,1024388391,3199324669,1050473439,1039755208,3168076877,1033733465,1008479670,3195661927,3177886370,3195332662,1048818818,1048944683,1034057828,3168696958,3178024912,1041918336,1041764587,1047503381,3171704147,3188094432,3196423209,3162908056,1045358076,1041752786,3170787424,3177253656,3128491892,3173712476,1039393036,3191181904,3175380947,1044107589,3192960125,1051189804,3199384383,3196416865,1012133244,3182143016,3177180202,3188475409,1048701401,1035186392,1036685758,1004589420,3151238822,1018343433,1032060542,1047378396,3181508651,3196466798,3189715492,3163623024,3187705502,1050868592,1041048083,3150099746,3132677845,3189939105,3157529909,3188112580,1039991593,1037076277,3158355239,1042823967,3185772101,3185568880,1039472813,1033014558,3185431736,1027898098,3184117328,3199684055,1029779299,3197167948,3189454921,1043738441,3191392224,3196936876,1045445339,3194616959,3184434435,3138153716,1044345059,3191267621,3166545222,3184809836,1029536825,1050476320,3199174373,1043414319,3179710270,1049254108,1025534713,1021439796,3169964722,1025461262,3197416866,3176999189,3179410128,1047088385,3181588927,1040989506,3163801286,3189143371,1024084708,1044046975,3175590323,3176121302,3186613152,1043004747,1012810990,1036730432,1040835558,3180877596,3189590175,1049181173,3196473511,3172577221,1019235746,1037782786,3171745300,981831856,3177368142,1015441310,1023517292,1019305942,1024760372,3168986803,1017823921,1043618118,3173541216,3166458378,3159812092,3164971692,3172275536,1035262286,3195568522,3196353194,3191041392,3183781592,1028391915,3190079994,1049908070,1055279877,1052840221,1045563277,1049660802,3193637895,1037854239,3184408634,1023270264,3192675759,1025347892,3181516425,3195705015,3197942208,1036909871,1026389399,3189919879,1033538111,1025347451,1046233135,1031947430,1033017367,1031220796,1052214388,3200260588,3193452830,1028787705,1042114001,1047988213,3189268421,3201861875,3192931070,3190237627,3183629695,1042932660,3192332246,1009432190,3189448534,1048981211,1048607393,3170980719,1053589674,1036066024,1004572180,3191300108,1031692505,1041593954,1051377063,1050156769,3197588454,3196785869,1050908309,1035844439,1040521816,3202763201,1041969263,1032724405,3194577692,3198598952,1033915914,3167013924,1041427026,3201379046,1036386612,3188254944,3194543052,3179187223,3189070389,1053764773,1025250369,1032546568,1051346044,1034398104,3195862948,1037809498,3143270285,1052974109,3185362396,1055624237,1048397558,3190483589,3184967239,3182390813,3170839584,1047317634,3203282567,3188685786,1042951695,3184151455,1027124673,1040590734,3191100153]} -------------------------------------------------------------------------------- /src/thumb.ts: -------------------------------------------------------------------------------- 1 | /* Docs: 2 | * 3 | * Thumb 16-bit Instruction Set Quick Reference Card 4 | * http://infocenter.arm.com/help/topic/com.arm.doc.qrc0006e/QRC0006_UAL16.pdf 5 | * 6 | * ARMv6-M Architecture Reference Manual (bit encoding of instructions) 7 | * http://ecee.colorado.edu/ecen3000/labs/lab3/files/DDI0419C_arm_architecture_v6m_reference_manual.pdf 8 | * 9 | * The ARM-THUMB Procedure Call Standard 10 | * http://www.cs.cornell.edu/courses/cs414/2001fa/armcallconvention.pdf 11 | * 12 | * Cortex-M0 Technical Reference Manual: 3.3. Instruction set summary (cycle counts) 13 | * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0432c/CHDCICDF.html // M0 14 | * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0484c/CHDCICDF.html // M0+ 15 | */ 16 | 17 | import * as assembler from "./assembler"; 18 | import { assert, lookup } from "./util"; 19 | 20 | const thumbRegs: pxt.Map = { 21 | "r0": 0, 22 | "r1": 1, 23 | "r2": 2, 24 | "r3": 3, 25 | "r4": 4, 26 | "r5": 5, 27 | "r6": 6, 28 | "r7": 7, 29 | "r8": 8, 30 | "r9": 9, 31 | "r10": 10, 32 | "r11": 11, 33 | "r12": 12, 34 | "sp": 13, 35 | "r13": 13, 36 | "lr": 14, 37 | "r14": 14, 38 | "pc": 15, 39 | "r15": 15, 40 | } 41 | 42 | const armConditions: SMap = { 43 | "eq": 0, 44 | "ne": 1, 45 | "cs": 2, 46 | "hs": 2, // cs 47 | "cc": 3, 48 | "lo": 3, // cc 49 | "mi": 4, 50 | "pl": 5, 51 | "vs": 6, 52 | "vc": 7, 53 | "hi": 8, 54 | "ls": 9, 55 | "ge": 10, 56 | "lt": 11, 57 | "gt": 12, 58 | "le": 13, 59 | "": 14, 60 | "al": 14, 61 | } 62 | 63 | let fpRegs: pxt.Map 64 | 65 | export class ThumbProcessor extends assembler.AbstractProcessor { 66 | runtimeIsARM = false 67 | 68 | constructor() { 69 | super(); 70 | 71 | if (!fpRegs) { 72 | fpRegs = {} 73 | for (let i = 0; i < 32; ++i) 74 | fpRegs["s" + i] = i 75 | } 76 | 77 | const allConds = (f: (cond: string, id: number) => void, inclAl = false) => { 78 | for (const k of Object.keys(armConditions)) 79 | if (armConditions[k] != 14 || inclAl) 80 | f(k, armConditions[k]) 81 | } 82 | 83 | // Registers 84 | // $r0 - bits 2:1:0 85 | // $r1 - bits 5:4:3 86 | // $r2 - bits 7:2:1:0 87 | // $r3 - bits 6:5:4:3 88 | // $r4 - bits 8:7:6 89 | // $r5 - bits 10:9:8 90 | 91 | this.addEnc("$r0", "R0-7", v => this.inrange(7, v, v)) 92 | this.addEnc("$r1", "R0-7", v => this.inrange(7, v, v << 3)) 93 | this.addEnc("$r2", "R0-15", v => this.inrange(15, v, (v & 7) | ((v & 8) << 4))) 94 | this.addEnc("$r3", "R0-15", v => this.inrange(15, v, v << 3)) 95 | this.addEnc("$r4", "R0-7", v => this.inrange(7, v, v << 6)) 96 | this.addEnc("$r5", "R0-7", v => this.inrange(7, v, v << 8)) 97 | // this for setting both $r0 and $r1 (two argument adds and subs) 98 | this.addEnc("$r01", "R0-7", v => this.inrange(7, v, (v | v << 3))) 99 | 100 | // Immdiates: 101 | // $i0 - bits 7-0 102 | // $i1 - bits 7-0 * 4 103 | // $i2 - bits 6-0 * 4 104 | // $i3 - bits 8-6 105 | // $i4 - bits 10-6 106 | // $i5 - bits 10-6 * 4 107 | // $i6 - bits 10-6, 0 is 32 108 | // $i7 - bits 10-6 * 2 109 | 110 | this.addEnc("$i0", "#0-255", v => this.inrange(255, v, v)) 111 | this.addEnc("$i1", "#0-1020", v => this.inrange(255, v / 4, v >> 2)) 112 | this.addEnc("$i2", "#0-510", v => this.inrange(127, v / 4, v >> 2)) 113 | this.addEnc("$i3", "#0-7", v => this.inrange(7, v, v << 6)) 114 | this.addEnc("$i4", "#0-31", v => this.inrange(31, v, v << 6)) 115 | this.addEnc("$i5", "#0-124", v => this.inrange(31, v / 4, (v >> 2) << 6)) 116 | this.addEnc("$i6", "#1-32", v => v == 0 ? null : v == 32 ? 0 : this.inrange(31, v, v << 6)) 117 | this.addEnc("$i7", "#0-62", v => this.inrange(31, v / 2, (v >> 1) << 6)) 118 | this.addEnc("$i32", "#0-2^32", v => 1) 119 | 120 | this.addEnc("$rl0", "{R0-7,...}", v => this.inrange(255, v, v)) 121 | this.addEnc("$rl1", "{LR,R0-7,...}", v => (v & 0x4000) ? this.inrange(255, (v & ~0x4000), 0x100 | (v & 0xff)) : this.inrange(255, v, v)) 122 | this.addEnc("$rl2", "{PC,R0-7,...}", v => (v & 0x8000) ? this.inrange(255, (v & ~0x8000), 0x100 | (v & 0xff)) : this.inrange(255, v, v)) 123 | 124 | 125 | this.addEnc("$la", "LABEL", v => this.inrange(255, v / 4, v >> 2)).isWordAligned = true; 126 | this.addEnc("$lb", "LABEL", v => this.inrangeSigned(127, v / 2, v >> 1)) 127 | this.addEnc("$lb11", "LABEL", v => this.inrangeSigned(1023, v / 2, v >> 1)) 128 | 129 | //this.addInst("nop", 0xbf00, 0xffff); // we use mov r8,r8 as gcc 130 | 131 | this.addInst("adcs $r0, $r1", 0x4140, 0xffc0); 132 | this.addInst("add $r2, $r3", 0x4400, 0xff00); 133 | this.addInst("add $r5, pc, $i1", 0xa000, 0xf800); 134 | this.addInst("add $r5, sp, $i1", 0xa800, 0xf800); 135 | this.addInst("add sp, $i2", 0xb000, 0xff80).canBeShared = true; 136 | this.addInst("adds $r0, $r1, $i3", 0x1c00, 0xfe00); 137 | this.addInst("adds $r0, $r1, $r4", 0x1800, 0xfe00); 138 | this.addInst("adds $r01, $r4", 0x1800, 0xfe00); 139 | this.addInst("adds $r5, $i0", 0x3000, 0xf800); 140 | this.addInst("adr $r5, $la", 0xa000, 0xf800); 141 | this.addInst("ands $r0, $r1", 0x4000, 0xffc0); 142 | this.addInst("asrs $r0, $r1", 0x4100, 0xffc0); 143 | this.addInst("asrs $r0, $r1, $i6", 0x1000, 0xf800); 144 | this.addInst("bics $r0, $r1", 0x4380, 0xffc0); 145 | this.addInst("bkpt $i0", 0xbe00, 0xff00); 146 | this.addInst("blx $r3", 0x4780, 0xff87); 147 | this.addInst("bx $r3", 0x4700, 0xff80); 148 | this.addInst("cmn $r0, $r1", 0x42c0, 0xffc0); 149 | this.addInst("cmp $r0, $r1", 0x4280, 0xffc0); 150 | this.addInst("cmp $r2, $r3", 0x4500, 0xff00); 151 | this.addInst("cmp $r5, $i0", 0x2800, 0xf800); 152 | this.addInst("eors $r0, $r1", 0x4040, 0xffc0); 153 | this.addInst("ldmia $r5!, $rl0", 0xc800, 0xf800); 154 | this.addInst("ldmia $r5, $rl0", 0xc800, 0xf800); 155 | this.addInst("ldr $r0, [$r1, $i5]", 0x6800, 0xf800); // this is used for debugger breakpoint - cannot be shared 156 | this.addInst("ldr $r0, [$r1, $r4]", 0x5800, 0xfe00); 157 | this.addInst("ldr $r5, [pc, $i1]", 0x4800, 0xf800); 158 | this.addInst("ldr $r5, $la", 0x4800, 0xf800); 159 | this.addInst("ldr $r5, [sp, $i1]", 0x9800, 0xf800).canBeShared = true; 160 | this.addInst("ldr $r5, [sp]", 0x9800, 0xf800).canBeShared = true; 161 | this.addInst("ldrb $r0, [$r1, $i4]", 0x7800, 0xf800); 162 | this.addInst("ldrb $r0, [$r1, $r4]", 0x5c00, 0xfe00); 163 | this.addInst("ldrh $r0, [$r1, $i7]", 0x8800, 0xf800); 164 | this.addInst("ldrh $r0, [$r1, $r4]", 0x5a00, 0xfe00); 165 | this.addInst("ldrsb $r0, [$r1, $r4]", 0x5600, 0xfe00); 166 | this.addInst("ldrsh $r0, [$r1, $r4]", 0x5e00, 0xfe00); 167 | this.addInst("lsls $r0, $r1", 0x4080, 0xffc0); 168 | this.addInst("lsls $r0, $r1, $i4", 0x0000, 0xf800); 169 | this.addInst("lsrs $r0, $r1", 0x40c0, 0xffc0); 170 | this.addInst("lsrs $r0, $r1, $i6", 0x0800, 0xf800); 171 | //this.addInst("mov $r0, $r1", 0x4600, 0xffc0); 172 | this.addInst("mov $r2, $r3", 0x4600, 0xff00); 173 | this.addInst("movs $r0, $r1", 0x0000, 0xffc0); 174 | this.addInst("movs $r5, $i0", 0x2000, 0xf800); 175 | this.addInst("muls $r0, $r1", 0x4340, 0xffc0); 176 | this.addInst("mvns $r0, $r1", 0x43c0, 0xffc0); 177 | this.addInst("negs $r0, $r1", 0x4240, 0xffc0); 178 | this.addInst("nop", 0x46c0, 0xffff); // mov r8, r8 179 | this.addInst("orrs $r0, $r1", 0x4300, 0xffc0); 180 | this.addInst("pop $rl2", 0xbc00, 0xfe00); 181 | this.addInst("push $rl1", 0xb400, 0xfe00); 182 | this.addInst("rev $r0, $r1", 0xba00, 0xffc0); 183 | this.addInst("rev16 $r0, $r1", 0xba40, 0xffc0); 184 | this.addInst("revsh $r0, $r1", 0xbac0, 0xffc0); 185 | this.addInst("rors $r0, $r1", 0x41c0, 0xffc0); 186 | this.addInst("sbcs $r0, $r1", 0x4180, 0xffc0); 187 | this.addInst("sev", 0xbf40, 0xffff); 188 | this.addInst("stm $r5!, $rl0", 0xc000, 0xf800); 189 | this.addInst("stmia $r5!, $rl0", 0xc000, 0xf800); // alias for stm 190 | this.addInst("stmea $r5!, $rl0", 0xc000, 0xf800); // alias for stm 191 | this.addInst("str $r0, [$r1, $i5]", 0x6000, 0xf800).canBeShared = true; 192 | this.addInst("str $r0, [$r1]", 0x6000, 0xf800).canBeShared = true; 193 | this.addInst("str $r0, [$r1, $r4]", 0x5000, 0xfe00); 194 | this.addInst("str $r5, [sp, $i1]", 0x9000, 0xf800).canBeShared = true; 195 | this.addInst("str $r5, [sp]", 0x9000, 0xf800).canBeShared = true; 196 | this.addInst("strb $r0, [$r1, $i4]", 0x7000, 0xf800); 197 | this.addInst("strb $r0, [$r1, $r4]", 0x5400, 0xfe00); 198 | this.addInst("strh $r0, [$r1, $i7]", 0x8000, 0xf800); 199 | this.addInst("strh $r0, [$r1, $r4]", 0x5200, 0xfe00); 200 | this.addInst("sub sp, $i2", 0xb080, 0xff80); 201 | this.addInst("subs $r0, $r1, $i3", 0x1e00, 0xfe00); 202 | this.addInst("subs $r0, $r1, $r4", 0x1a00, 0xfe00); 203 | this.addInst("subs $r01, $r4", 0x1a00, 0xfe00); 204 | this.addInst("subs $r5, $i0", 0x3800, 0xf800); 205 | this.addInst("svc $i0", 0xdf00, 0xff00); 206 | this.addInst("sxtb $r0, $r1", 0xb240, 0xffc0); 207 | this.addInst("sxth $r0, $r1", 0xb200, 0xffc0); 208 | this.addInst("tst $r0, $r1", 0x4200, 0xffc0); 209 | this.addInst("udf $i0", 0xde00, 0xff00); 210 | this.addInst("uxtb $r0, $r1", 0xb2c0, 0xffc0); 211 | this.addInst("uxth $r0, $r1", 0xb280, 0xffc0); 212 | this.addInst("wfe", 0xbf20, 0xffff); 213 | this.addInst("wfi", 0xbf30, 0xffff); 214 | this.addInst("yield", 0xbf10, 0xffff); 215 | 216 | this.addInst("cpsid i", 0xb672, 0xffff); 217 | this.addInst("cpsie i", 0xb662, 0xffff); 218 | 219 | allConds((cond, id) => 220 | this.addInst(`b${cond} $lb`, 0xd000 | (id << 8), 0xff00)) 221 | 222 | this.addInst("b $lb11", 0xe000, 0xf800); 223 | this.addInst("bal $lb11", 0xe000, 0xf800); 224 | 225 | // handled specially - 32 bit instruction 226 | this.addInst("bl $lb", 0xf000, 0xf800); 227 | // this is normally emitted as 'b' but will be emitted as 'bl' if needed 228 | this.addInst("bb $lb", 0xe000, 0xf800); 229 | 230 | // this will emit as PC-relative LDR or ADDS 231 | this.addInst("ldlit $r5, $i32", 0x4800, 0xf800); 232 | 233 | // 32 bit encodings 234 | this.addEnc("$RL0", "{R0-15,...}", v => this.inrange(0xffff, v, v)) 235 | this.addEnc("$R0", "R0-15", v => this.inrange(15, v, v << 8)) // 8-11 236 | this.addEnc("$R1", "R0-15", v => this.inrange(15, v, v << 16)) // 16-19 237 | this.addEnc("$R2", "R0-15", v => this.inrange(15, v, v << 12)) // 12-15 238 | this.addEnc("$R3", "R0-15", v => this.inrange(15, v, v << 0)) // 0-3 239 | this.addEnc("$I0", "#0-4095", v => this.inrange(4095, v, (v & 0xff) | ((v & 0x700) << 4) | ((v & 0x800) << 15))) 240 | this.addEnc("$I1", "#0-4095", v => this.inrange(4095, v, v)) 241 | this.addEnc("$I2", "#0-65535", v => this.inrange(0xffff, v, 242 | (v & 0xff) | ((v & 0x700) << 4) | ((v & 0x800) << 15) | ((v & 0xf000) << 4))) 243 | this.addEnc("$I3", "#0-31", v => this.inrange(31, v, ((v & 3) << 6) | ((v >> 2) << 12))) 244 | 245 | this.addEnc("$LB", "LABEL", v => { 246 | const q = ((v >> 1) & 0x7ff) 247 | | (((v >> 12) & 0x3f) << 16) 248 | | (((v >> 18) & 0x1) << 13) 249 | | (((v >> 19) & 0x1) << 11) 250 | | (((v >> 20) & 0x1) << 26) 251 | if (this.inrangeSigned((1 << 20) - 1, v / 2, q) == null) 252 | return null 253 | return q 254 | }) 255 | 256 | this.addEnc("$S0", "S0-31", v => this.inrange(31, v, ((v >> 1) << 0) | ((v & 1) << 5))) // 0-3 + 5 257 | this.addEnc("$S1", "S0-31", v => this.inrange(31, v, ((v >> 1) << 12) | ((v & 1) << 22))) // 12-15 + 22 258 | this.addEnc("$S2", "S0-31", v => this.inrange(31, v, ((v >> 1) << 16) | ((v & 1) << 7))) // 16-19 + 7 259 | 260 | this.addEnc("$SL0", "{S0-S31}", 261 | v => { 262 | v |= 0 263 | const v0 = v 264 | if (!v) return null 265 | let reg0 = 0 266 | while (reg0 < 32 && 0 == (v & (1 << reg0))) 267 | reg0++ 268 | v >>>= reg0 269 | if (!v) return null 270 | let num = 0 271 | while (v & 1) { 272 | v >>= 1 273 | num++ 274 | } 275 | if (v) return null // non-consecutive 276 | v = reg0 277 | // console.log(v0.toString(16), v, num) 278 | return ((v >> 1) << 12) | ((v & 1) << 22) | num 279 | }) 280 | 281 | this.addInst32("push $RL0", 0xe92d0000, 0xffff0000) 282 | this.addInst32("pop $RL0", 0xe8bd0000, 0xffff0000) 283 | this.addInst32("addw $R0, $R1, $I0", 0xf2000000, 0xfbf08000) 284 | this.addInst32("subw $R0, $R1, $I0", 0xf2a00000, 0xfbf08000) 285 | this.addInst32("ldr $R2, [$R1, $I1]", 0xf8d00000, 0xfff00000); 286 | this.addInst32("str $R2, [$R1, $I1]", 0xf8c00000, 0xfff00000); 287 | this.addInst32("movw $R0, $I2", 0xf2400000, 0xfbf08000); 288 | this.addInst32("add $R0, $R1, $R3, lsl $I3", 0xeb000000, 0xfff08000); 289 | 290 | // encoding $i0 is only a subset of allowed constants 291 | this.addInst32("subs $R0, $R1, $i0", 0xf1b00000, 0xfff08000) 292 | this.addInst32("sub $R0, $R1, $i0", 0xf1a00000, 0xfff08000) 293 | this.addInst32("adds $R0, $R1, $i0", 0xf1100000, 0xfff08000) 294 | this.addInst32("add $R0, $R1, $i0", 0xf1000000, 0xfff08000) 295 | 296 | allConds((cond, id) => 297 | this.addInst32(`b${cond} $LB`, 0xf0008000 | (id << 22), 0xfbc0d000), true) 298 | 299 | allConds((cond, id) => 300 | this.addInst(`it ${cond}`, 0xbf08 | (id << 4), 0xffff), true) 301 | 302 | this.addInst32("vabs.f32 $S1, $S0", 0xeeb00ac0, 0xffbf0fd0); 303 | this.addInst32("vadd.f32 $S1, $S2, $S0", 0xee300a00, 0xffb00f50); 304 | this.addInst32("vmul.f32 $S1, $S2, $S0", 0xee200a00, 0xffb00f50); 305 | this.addInst32("vcmpe.f32 $S1, #0.0", 0xeeb50ac0, 0xffbf0ff0); 306 | this.addInst32("vcmpe.f32 $S1, $S0", 0xeeb40ac0, 0xffbf0fd0); 307 | this.addInst32("vcmp.f32 $S1, #0.0", 0xeeb50a40, 0xffbf0ff0); 308 | this.addInst32("vcmp.f32 $S1, $S0", 0xeeb40a40, 0xffbf0fd0); 309 | this.addInst32("vdiv.f32 $S1, $S2, $S0", 0xee800a00, 0xffb00f50); 310 | this.addInst32("vfma.f32 $S1, $S2, $S0", 0xeea00a00, 0xffb00f50); 311 | this.addInst32("vfms.f32 $S1, $S2, $S0", 0xeea00a40, 0xffb00f50); 312 | this.addInst32("vfnma.f32 $S1, $S2, $S0", 0xee900a40, 0xffb00f50); 313 | this.addInst32("vfnms.f32 $S1, $S2, $S0", 0xee900a00, 0xffb00f50); 314 | this.addInst32("vmla.f32 $S1, $S2, $S0", 0xe2000d10, 0xffb00f10); 315 | this.addInst32("vmls.f32 $S1, $S2, $S0", 0xe2200d10, 0xffb00f10); 316 | this.addInst32("vneg.f32 $S1, $S0", 0xeeb10a40, 0xffbf0fd0); 317 | this.addInst32("vsqrt.f32 $S1, $S0", 0xeeb10ac0, 0xffbf0fd0); 318 | this.addInst32("vsub.f32 $S1, $S2, $S0", 0xee300a40, 0xffb00f50); 319 | this.addInst32("vstmdb $R1!, $SL0", 0xed200a00, 0xffb00f00); 320 | this.addInst32("vstmia $R1!, $SL0", 0xeca00a00, 0xffb00f00); 321 | this.addInst32("vstmia $R1, $SL0", 0xec800a00, 0xffb00f00); 322 | this.addInst32("vstm $R1!, $SL0", 0xeca00a00, 0xffb00f00); 323 | this.addInst32("vstm $R1, $SL0", 0xec800a00, 0xffb00f00); 324 | this.addInst32("vldmdb $R1!, $SL0", 0xed300a00, 0xffb00f00); 325 | this.addInst32("vldmia $R1!, $SL0", 0xecb00a00, 0xffb00f00); 326 | this.addInst32("vldmia $R1, $SL0", 0xec900a00, 0xffb00f00); 327 | this.addInst32("vldm $R1!, $SL0", 0xecb00a00, 0xffb00f00); 328 | this.addInst32("vldm $R1, $SL0", 0xec900a00, 0xffb00f00); 329 | this.addInst32("vldr $S1, [$R1, $i1]", 0xed900a00, 0xffb00f00); 330 | this.addInst32("vstr $S1, [$R1, $i1]", 0xed800a00, 0xffb00f00); 331 | this.addInst32("vldr $S1, [$R1]", 0xed900a00, 0xffb00f00); 332 | this.addInst32("vmrs APSR_nzcv, fpscr", 0xeef1fa10, 0xffffffff); 333 | this.addInst32("vmrs APSR_nzcv, FPSCR", 0xeef1fa10, 0xffffffff); 334 | this.addInst32("vmov.f32 $S1, $S0", 0xeeb00a40, 0xffbf0fd0); 335 | this.addInst32("vmov $S2, $R2", 0xee000a10, 0xfff00f7f); 336 | this.addInst32("vmov $R2, $S2", 0xee100a10, 0xfff00f7f); 337 | this.addInst32("vldr $S1, $la", 0xed9f0a00, 0xffbf0f00); 338 | this.addInst32("vmov.f32 $S1, #1.0", 0xeeb70a00, 0xffbf0ff0); 339 | this.addInst32("vcvt.s32.f32 $S1, $S0", 0xeebd0ac0, 0xffbf0fd0); 340 | 341 | this.addInst32("vcvtb.f32.f16 $S1, $S0", 0xeeb20a40, 0xffbf0fd0); 342 | this.addInst32("vcvtt.f32.f16 $S1, $S0", 0xeeb20ac0, 0xffbf0fd0); 343 | this.addInst32("vcvtb.f16.f32 $S1, $S0", 0xeeb30a40, 0xffbf0fd0); 344 | this.addInst32("vcvtt.f16.f32 $S1, $S0", 0xeeb30ac0, 0xffbf0fd0); 345 | 346 | /* 347 | vmsr 348 | vpush 349 | vpop 350 | vrint 351 | vsel 352 | */ 353 | 354 | } 355 | 356 | public stripCondition(name: string): string { 357 | if (name.length >= 5) { 358 | const dot = name.indexOf(".") 359 | let suff = "" 360 | let force = false 361 | if (dot > 0) { 362 | suff = name.slice(dot) 363 | name = name.slice(0, dot) 364 | if (suff == ".32") { 365 | force = true 366 | suff = "" 367 | } 368 | } 369 | if (armConditions[name.slice(-2)]) 370 | return name.slice(0, -2) + suff 371 | if (force) return name 372 | } 373 | return null 374 | } 375 | 376 | public toFnPtr(v: number, baseOff: number, lbl: string) { 377 | if (this.runtimeIsARM && /::/.test(lbl)) 378 | return (v + baseOff) & ~1; 379 | return (v + baseOff) | 1; 380 | } 381 | 382 | public wordSize() { 383 | return 4 384 | } 385 | 386 | public is32bit(i: assembler.Instruction) { 387 | return i.name == "bl" || i.name == "bb" || i.is32bit; 388 | } 389 | 390 | public postProcessAbsAddress(f: assembler.File, v: number) { 391 | // Thumb addresses have last bit set, but we are ourselves always 392 | // in Thumb state, so to go to ARM state, we signal that with that last bit 393 | v ^= 1 394 | v -= f.baseOffset 395 | return v 396 | } 397 | 398 | public emit32(v0: number, v: number, actual: string): assembler.EmitResult { 399 | let isBLX = v % 2 ? true : false 400 | if (isBLX) { 401 | v = (v + 1) & ~3 402 | } 403 | let off = v >> 1 404 | assert(off != null) 405 | // Range is +-4M (i.e., 2M instructions) 406 | if ((off | 0) != off || 407 | !(-2 * 1024 * 1024 < off && off < 2 * 1024 * 1024)) 408 | return assembler.emitErr("jump out of range", actual); 409 | 410 | // note that off is already in instructions, not bytes 411 | let imm11 = off & 0x7ff 412 | let imm10 = (off >> 11) & 0x3ff 413 | 414 | return { 415 | opcode: (off & 0xf0000000) ? (0xf400 | imm10) : (0xf000 | imm10), 416 | opcode2: isBLX ? (0xe800 | imm11) : (0xf800 | imm11), 417 | stack: 0, 418 | numArgs: [v], 419 | labelName: actual 420 | } 421 | } 422 | 423 | public expandLdlit(f: assembler.File): void { 424 | let nextGoodSpot: assembler.Line 425 | let needsJumpOver = false 426 | let outlines: assembler.Line[] = [] 427 | let values: pxt.Map = {} 428 | let seq = 1 429 | 430 | for (let i = 0; i < f.lines.length; ++i) { 431 | let line = f.lines[i] 432 | outlines.push(line) 433 | if (line.type == "instruction" && line.instruction && line.instruction.name == "ldlit") { 434 | if (!nextGoodSpot) { 435 | let limit = line.location + 900 // leave some space - real limit is 1020 436 | let j = i + 1 437 | for (; j < f.lines.length; ++j) { 438 | if (f.lines[j].location > limit) 439 | break 440 | let op = f.lines[j].getOp() 441 | if (op == "b" || op == "bb" || (op == "pop" && f.lines[j].words[2] == "pc")) 442 | nextGoodSpot = f.lines[j] 443 | } 444 | if (nextGoodSpot) { 445 | needsJumpOver = false 446 | } else { 447 | needsJumpOver = true 448 | while (--j > i) { 449 | if (f.lines[j].type == "instruction") { 450 | nextGoodSpot = f.lines[j] 451 | break 452 | } 453 | } 454 | } 455 | } 456 | let reg = line.words[1] 457 | // make sure the key in values[] below doesn't look like integer 458 | // we rely on Object.keys() returning stuff in insertion order, and integers mess with it 459 | // see https://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys 460 | // or possibly https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/ 461 | let v = "#" + line.words[3] 462 | let lbl = lookup(values, v) 463 | if (!lbl) { 464 | lbl = "_ldlit_" + ++seq 465 | values[v] = lbl 466 | } 467 | line.update(`ldr ${reg}, ${lbl}`) 468 | } 469 | if (line === nextGoodSpot) { 470 | nextGoodSpot = null 471 | let txtLines: string[] = [] 472 | let jmplbl = "_jmpwords_" + ++seq 473 | if (needsJumpOver) 474 | txtLines.push("bb " + jmplbl) 475 | txtLines.push(".balign 4") 476 | for (let v of Object.keys(values)) { 477 | let lbl = values[v] 478 | txtLines.push(lbl + ": .word " + v.slice(1)) 479 | } 480 | if (needsJumpOver) 481 | txtLines.push(jmplbl + ":") 482 | for (let t of txtLines) { 483 | f.buildLine(t, outlines) 484 | let ll = outlines[outlines.length - 1] 485 | ll.scope = line.scope 486 | ll.lineNo = line.lineNo 487 | } 488 | values = {} 489 | } 490 | } 491 | f.lines = outlines 492 | } 493 | 494 | public getAddressFromLabel(f: assembler.File, i: assembler.Instruction, s: string, wordAligned = false): number { 495 | let l = f.lookupLabel(s); 496 | if (l == null) return null; 497 | let pc = f.location() + 4 498 | if (wordAligned) pc = pc & 0xfffffffc 499 | return l - pc; 500 | } 501 | 502 | public isPop(opcode: number): boolean { 503 | return opcode == 0xbc00; 504 | } 505 | 506 | public isPush(opcode: number): boolean { 507 | return opcode == 0xb400; 508 | } 509 | 510 | public isAddSP(opcode: number): boolean { 511 | return opcode == 0xb000; 512 | } 513 | 514 | public isSubSP(opcode: number): boolean { 515 | return opcode == 0xb080; 516 | } 517 | 518 | public peephole(ln: assembler.Line, lnNext: assembler.Line, lnNext2: assembler.Line) { 519 | let lb11 = this.encoders["$lb11"] 520 | let lb = this.encoders["$lb"] 521 | 522 | // +/-8 bytes is because the code size can slightly change due to .balign directives 523 | // inserted by literal generation code; see https://github.com/Microsoft/pxt-adafruit/issues/514 524 | // Most likely 4 would be enough, but we play it safe 525 | function fits(enc: assembler.Encoder, ln: assembler.Line) { 526 | return ( 527 | enc.encode(ln.numArgs[0] + 8) != null && 528 | enc.encode(ln.numArgs[0] - 8) != null && 529 | enc.encode(ln.numArgs[0]) != null 530 | ) 531 | } 532 | 533 | let lnop = ln.getOp() 534 | let isSkipBranch = false 535 | if (lnop == "bne" || lnop == "beq") { 536 | if (lnNext.getOp() == "b" && ln.numArgs[0] == 0) 537 | isSkipBranch = true; 538 | if (lnNext.getOp() == "bb" && ln.numArgs[0] == 2) 539 | isSkipBranch = true; 540 | } 541 | 542 | if (lnop == "bb" && fits(lb11, ln)) { 543 | // RULE: bb .somewhere -> b .somewhere (if fits) 544 | ln.update("b " + ln.words[1]) 545 | } else if (lnop == "b" && ln.numArgs[0] == -2) { 546 | // RULE: b .somewhere; .somewhere: -> .somewhere: 547 | ln.update("") 548 | } else if (lnop == "bne" && isSkipBranch && fits(lb, lnNext)) { 549 | // RULE: bne .next; b .somewhere; .next: -> beq .somewhere 550 | ln.update("beq " + lnNext.words[1]) 551 | lnNext.update("") 552 | } else if (lnop == "beq" && isSkipBranch && fits(lb, lnNext)) { 553 | // RULE: beq .next; b .somewhere; .next: -> bne .somewhere 554 | ln.update("bne " + lnNext.words[1]) 555 | lnNext.update("") 556 | } else if (lnop == "push" && ln.numArgs[0] == 0x4000 && lnNext.getOp() == "push" && !(lnNext.numArgs[0] & 0x4000)) { 557 | // RULE: push {lr}; push {X, ...} -> push {lr, X, ...} 558 | ln.update(lnNext.text.replace("{", "{lr, ")) 559 | lnNext.update("") 560 | } else if (lnop == "pop" && lnNext.getOp() == "pop" && lnNext.numArgs[0] == 0x8000) { 561 | // RULE: pop {X, ...}; pop {pc} -> push {X, ..., pc} 562 | ln.update(ln.text.replace("}", ", pc}")) 563 | lnNext.update("") 564 | } else if (lnop == "push" && lnNext.getOp() == "pop" && ln.numArgs[0] == lnNext.numArgs[0]) { 565 | // RULE: push {X}; pop {X} -> nothing 566 | assert(ln.numArgs[0] > 0) 567 | ln.update("") 568 | lnNext.update("") 569 | } else if (lnop == "push" && lnNext.getOp() == "pop" && 570 | ln.words.length == 4 && 571 | lnNext.words.length == 4) { 572 | // RULE: push {rX}; pop {rY} -> mov rY, rX 573 | assert(ln.words[1] == "{") 574 | ln.update("mov " + lnNext.words[2] + ", " + ln.words[2]) 575 | lnNext.update("") 576 | } else if (lnNext2 && ln.getOpExt() == "movs $r5, $i0" && lnNext.getOpExt() == "mov $r0, $r1" && 577 | ln.numArgs[0] == lnNext.numArgs[1] && 578 | clobbersReg(lnNext2, ln.numArgs[0])) { 579 | // RULE: movs rX, #V; mov rY, rX; clobber rX -> movs rY, #V 580 | ln.update("movs r" + lnNext.numArgs[0] + ", #" + ln.numArgs[1]) 581 | lnNext.update("") 582 | } else if (lnop == "pop" && singleReg(ln) >= 0 && lnNext.getOp() == "push" && 583 | singleReg(ln) == singleReg(lnNext)) { 584 | // RULE: pop {rX}; push {rX} -> ldr rX, [sp, #0] 585 | ln.update("ldr r" + singleReg(ln) + ", [sp, #0]") 586 | lnNext.update("") 587 | } else if (lnop == "push" && lnNext.getOpExt() == "ldr $r5, [sp, $i1]" && 588 | singleReg(ln) == lnNext.numArgs[0] && lnNext.numArgs[1] == 0) { 589 | // RULE: push {rX}; ldr rX, [sp, #0] -> push {rX} 590 | lnNext.update("") 591 | } else if (lnNext2 && lnop == "push" && singleReg(ln) >= 0 && preservesReg(lnNext, singleReg(ln)) && 592 | lnNext2.getOp() == "pop" && singleReg(ln) == singleReg(lnNext2)) { 593 | // RULE: push {rX}; movs rY, #V; pop {rX} -> movs rY, #V (when X != Y) 594 | ln.update("") 595 | lnNext2.update("") 596 | } 597 | } 598 | 599 | public registerNo(actual: string, enc: assembler.Encoder) { 600 | if (!actual) return null; 601 | actual = actual.toLowerCase() 602 | let map = thumbRegs 603 | if (enc.name[1] == "S") { 604 | map = fpRegs 605 | } 606 | const r = map[actual] 607 | if (r === undefined) 608 | return null 609 | return r 610 | } 611 | 612 | public testAssembler() { 613 | assembler.expectError(this, "lsl r0, r0, #8"); 614 | //assembler.expectError(this, "push {pc,lr}"); 615 | assembler.expectError(this, "push {r17}"); 616 | assembler.expectError(this, "mov r0, r1 foo"); 617 | assembler.expectError(this, "movs r14, #100"); 618 | assembler.expectError(this, "push {r0"); 619 | assembler.expectError(this, "push lr,r0}"); 620 | //assembler.expectError(this, "pop {lr,r0}"); 621 | assembler.expectError(this, "b #+11"); 622 | assembler.expectError(this, "b #+10240000"); 623 | assembler.expectError(this, "bne undefined_label"); 624 | assembler.expectError(this, ".foobar"); 625 | 626 | assembler.expect(this, 627 | "0200 lsls r0, r0, #8\n" + 628 | "b500 push {lr}\n" + 629 | "2064 movs r0, #100 ; 0x64\n" + 630 | "b401 push {r0}\n" + 631 | "bc08 pop {r3}\n" + 632 | "b501 push {r0, lr}\n" + 633 | "bd20 pop {r5, pc}\n" + 634 | "bc01 pop {r0}\n" + 635 | "4770 bx lr\n" + 636 | "0000 .balign 4\n" + 637 | "e6c0 .word -72000\n" + 638 | "fffe\n") 639 | 640 | assembler.expect(this, 641 | "4291 cmp r1, r2\n" + 642 | "d100 bne l6\n" + 643 | "e000 b l8\n" + 644 | "1840 l6: adds r0, r0, r1\n" + 645 | "4718 l8: bx r3\n") 646 | 647 | assembler.expect(this, 648 | " @stackmark base\n" + 649 | "b403 push {r0, r1}\n" + 650 | " @stackmark locals\n" + 651 | "9801 ldr r0, [sp, locals@1]\n" + 652 | "b401 push {r0}\n" + 653 | "9802 ldr r0, [sp, locals@1]\n" + 654 | "bc01 pop {r0}\n" + 655 | " @stackempty locals\n" + 656 | "9901 ldr r1, [sp, locals@1]\n" + 657 | "9102 str r1, [sp, base@0]\n" + 658 | " @stackempty locals\n" + 659 | "b002 add sp, #8\n" + 660 | " @stackempty base\n") 661 | 662 | assembler.expect(this, 663 | "b090 sub sp, #4*16\n" + 664 | "b010 add sp, #4*16\n" 665 | ) 666 | 667 | assembler.expect(this, 668 | "6261 .string \"abc\"\n" + 669 | "0063 \n" 670 | ) 671 | 672 | assembler.expect(this, 673 | "6261 .string \"abcde\"\n" + 674 | "6463 \n" + 675 | "0065 \n" 676 | ) 677 | 678 | assembler.expect(this, 679 | "3042 adds r0, 0x42\n" + 680 | "1c0d adds r5, r1, #0\n" + 681 | "d100 bne #0\n" + 682 | "2800 cmp r0, #0\n" + 683 | "6b28 ldr r0, [r5, #48]\n" + 684 | "0200 lsls r0, r0, #8\n" + 685 | "2063 movs r0, 0x63\n" + 686 | "4240 negs r0, r0\n" + 687 | "46c0 nop\n" + 688 | "b500 push {lr}\n" + 689 | "b401 push {r0}\n" + 690 | "b402 push {r1}\n" + 691 | "b404 push {r2}\n" + 692 | "b408 push {r3}\n" + 693 | "b520 push {r5, lr}\n" + 694 | "bd00 pop {pc}\n" + 695 | "bc01 pop {r0}\n" + 696 | "bc02 pop {r1}\n" + 697 | "bc04 pop {r2}\n" + 698 | "bc08 pop {r3}\n" + 699 | "bd20 pop {r5, pc}\n" + 700 | "9003 str r0, [sp, #4*3]\n") 701 | } 702 | } 703 | 704 | 705 | // if true then instruction doesn't write r and doesn't read/write memory 706 | function preservesReg(ln: assembler.Line, n: number) { 707 | if (ln.getOpExt() == "movs $r5, $i0" && ln.numArgs[0] != n) 708 | return true; 709 | return false; 710 | } 711 | 712 | function clobbersReg(ln: assembler.Line, n: number) { 713 | // TODO add some more 714 | if (ln.getOp() == "pop" && ln.numArgs[0] & (1 << n)) 715 | return true; 716 | return false; 717 | } 718 | 719 | function singleReg(ln: assembler.Line) { 720 | assert(ln.getOp() == "push" || ln.getOp() == "pop") 721 | let k = 0; 722 | let ret = -1; 723 | let v = ln.numArgs[0] 724 | while (v > 0) { 725 | if (v & 1) { 726 | if (ret == -1) ret = k; 727 | else ret = -2; 728 | } 729 | v >>= 1; 730 | k++; 731 | } 732 | if (ret >= 0) return ret; 733 | else return -1; 734 | } 735 | --------------------------------------------------------------------------------