Bläddra i källkod

Add kmodel test files (#871)

Lutz Roeder 4 år sedan
förälder
incheckning
dd0bc95b60
2 ändrade filer med 314 tillägg och 83 borttagningar
  1. 278 83
      source/kmodel.js
  2. 36 0
      test/models.json

+ 278 - 83
source/kmodel.js

@@ -56,23 +56,29 @@ kmodel.Graph = class {
             index++;
         }
         for (const layer of model.layers) {
-            if (layer.type.name === 'INPUT') {
-                for (const input of layer.outputs) {
-                    this._inputs.push(new kmodel.Parameter('input', input.arguments.map((argument) => {
-                        return new kmodel.Argument(argument.name);
-                    })));
+            switch (layer.type.name) {
+                case 'INPUT':
+                case 'input': {
+                    for (const input of layer.outputs) {
+                        this._inputs.push(new kmodel.Parameter('input', input.arguments.map((argument) => {
+                            return new kmodel.Argument(argument.name);
+                        })));
+                    }
+                    break;
                 }
-                continue;
-            }
-            if (layer.type.name === 'OUTPUT') {
-                for (const output of layer.inputs) {
-                    this._outputs.push(new kmodel.Parameter(output.name, output.arguments.map((argument) => {
-                        return new kmodel.Argument(argument.name);
-                    })));
+                case 'OUTPUT':
+                case 'output': {
+                    for (const output of layer.inputs) {
+                        this._outputs.push(new kmodel.Parameter(output.name, output.arguments.map((argument) => {
+                            return new kmodel.Argument(argument.name);
+                        })));
+                    }
+                    break;
                 }
-                continue;
+                default:
+                    this._nodes.push(new kmodel.Node(layer));
+                    break;
             }
-            this._nodes.push(new kmodel.Node(layer));
         }
     }
 
@@ -186,7 +192,86 @@ kmodel.Tensor = class {
     }
 
     get state() {
-        return 'Tensor data not implemented.';
+        return this._context().state;
+    }
+
+    get value() {
+        const context = this._context();
+        if (context.state) {
+            return null;
+        }
+        context.limit = Number.MAX_SAFE_INTEGER;
+        return this._decode(context, 0);
+    }
+
+    toString() {
+        const context = this._context();
+        if (context.state) {
+            return '';
+        }
+        context.limit = 10000;
+        const value = this._decode(context, 0);
+        return JSON.stringify(value, null, 4);
+    }
+
+    _context() {
+        const context = {};
+        context.state = null;
+        context.index = 0;
+        context.count = 0;
+        if (this._data == null || this._data.length === 0) {
+            context.state = 'Tensor data is empty.';
+            return context;
+        }
+        const dataType = this.type.dataType;
+        const shape = this.type.shape.dimensions;
+        if (dataType !== 'uint8' && dataType !== 'float32') {
+            context.state = "Tensor data type '" + dataType + "' is not implemented.";
+            return context;
+        }
+        context.dataType = dataType;
+        context.shape = shape;
+        context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
+        return context;
+    }
+
+    _decode(context, dimension) {
+        const shape = (context.shape.length == 0) ? [ 1 ] : context.shape;
+        const size = shape[dimension];
+        const results = [];
+        if (dimension == shape.length - 1) {
+            for (let i = 0; i < size; i++) {
+                if (context.count > context.limit) {
+                    results.push('...');
+                    return results;
+                }
+                switch (context.dataType) {
+                    case 'uint8':
+                        results.push(context.data.getUint8(context.index));
+                        context.index += 1;
+                        context.count++;
+                        break;
+                    case 'float32':
+                        results.push(context.data.getFloat32(context.index, true));
+                        context.index += 4;
+                        context.count++;
+                        break;
+                }
+            }
+        }
+        else {
+            for (let j = 0; j < size; j++) {
+                if (context.count > context.limit) {
+                    results.push('...');
+                    return results;
+                }
+                results.push(this._decode(context, dimension + 1));
+            }
+        }
+        if (context.shape.length == 0) {
+            return results[0];
+        }
+        return results;
     }
 };
 
@@ -211,7 +296,7 @@ kmodel.Node = class {
         }
         for (const input of layer.inputs || []) {
             this._inputs.push(new kmodel.Parameter(input.name, input.arguments.map((argument) => {
-                const type = argument.shape ? new kmodel.TensorType(argument.data_type || '?', argument.shape) : null;
+                const type = argument.shape ? new kmodel.TensorType(argument.datatype || '?', argument.shape) : null;
                 const tensor = argument.data ? new kmodel.Tensor(type, argument.data) : null;
                 return new kmodel.Argument(argument.name, type, tensor);
             })));
@@ -333,12 +418,6 @@ kmodel.Reader = class {
                 }
                 return obj;
             };
-            reader.mem_address = function(memory_type, name) {
-                const mem_address = this.uint32();
-                const argument = { name: memory_type + ':' + mem_address.toString() };
-                const parameter = { name: name, arguments: [ argument ] };
-                return [ parameter ];
-            };
             switch (this._version) {
                 case 3: {
                     reader.kpu_model_header_t = function() {
@@ -353,7 +432,7 @@ kmodel.Reader = class {
                     };
                     reader.kpu_model_output_t = function(name) {
                         return {
-                            address: this.mem_address('main', name),
+                            address: [ this.parameter(name) ],
                             size: reader.uint32()
                         };
                     };
@@ -363,6 +442,15 @@ kmodel.Reader = class {
                             body_size: reader.uint32()
                         };
                     };
+                    reader.argument = function(memory_type) {
+                        memory_type = memory_type || 'main';
+                        const address = this.uint32();
+                        return { name: memory_type + ':' + address.toString() };
+                    };
+                    reader.parameter = function(name, memory_type) {
+                        const argument = this.argument(memory_type);
+                        return { name: name, arguments: [ argument ] };
+                    };
                     const model_header = reader.kpu_model_header_t();
                     this._layers = new Array(model_header.layers_length);
                     const outputs = new Array(model_header.output_count);
@@ -386,8 +474,8 @@ kmodel.Reader = class {
                     register(    4, 'QUANTIZED_GLOBAL_MAX_POOL2D', 'Pool');
                     register(    5, 'GLOBAL_AVERAGE_POOL2D', 'Pool', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.kernel_size = reader.uint32();
                         layer.channels = reader.uint32();
                     });
@@ -395,8 +483,8 @@ kmodel.Reader = class {
                     register(    7, 'MAX_POOL2D', 'Pool');
                     register(    8, 'QUANTIZED_MAX_POOL2D', 'Pool', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.inputs[0].arguments[0].shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
                         layer.outputs[0].arguments[0].shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
                         layer.kernel = [ reader.uint32(), reader.uint32() ];
@@ -407,16 +495,16 @@ kmodel.Reader = class {
                     register(   10, 'QUANTIZED_AVERAGE_POOL2D', 'Pool');
                     register(   11, 'QUANTIZE', '', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.count = reader.uint32();
                         layer.scale = reader.float32();
                         layer.bias = reader.float32();
                     });
                     register(   12, 'DEQUANTIZE', '', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.count = reader.uint32();
                         layer.scale = reader.float32();
                         layer.bias = reader.float32();
@@ -425,13 +513,13 @@ kmodel.Reader = class {
                     register(   14, 'L2_NORMALIZATION', 'Normalization');
                     register(   15, 'SOFTMAX', 'Activation', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.channels = reader.uint32();
                     });
                     register(   16, 'CONCAT', 'Tensor', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.inputs_mem = new Array(reader.uint32());
                         for (let i = 0; i < layer.inputs_mem.length; i++) {
                             layer.inputs_mem[i] = {
@@ -442,7 +530,7 @@ kmodel.Reader = class {
                     });
                     register(   17, 'QUANTIZED_CONCAT', 'Tensor', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.inputs_mem = new Array(reader.uint32());
                         for (let i = 0; i < layer.inputs_mem.length; i++) {
                             layer.inputs_mem[i] = {
@@ -453,8 +541,8 @@ kmodel.Reader = class {
                     });
                     register(   18, 'FULLY_CONNECTED', 'Layer', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.in_channels = reader.uint32();
                         layer.out_channels = reader.uint32();
                         const act = reader.uint32();
@@ -469,22 +557,22 @@ kmodel.Reader = class {
                             }
                             layer.chain = [ { type: activations[act] } ];
                         }
-                        layer.inputs.push({ name: 'weights', arguments: [ { name: '', data_type: 'float32', shape: [ layer.in_channels, layer.out_channels ], data: reader.read(4 * layer.in_channels * layer.out_channels) } ] });
-                        layer.inputs.push({ name: 'bias', arguments: [ { name: '', data_type: 'float32', shape: [ layer.out_channels ], data: reader.read(4 * layer.out_channels) } ] });
+                        layer.inputs.push({ name: 'weights', arguments: [ { name: '', datatype: 'float32', shape: [ layer.in_channels, layer.out_channels ], data: reader.read(4 * layer.in_channels * layer.out_channels) } ] });
+                        layer.inputs.push({ name: 'bias', arguments: [ { name: '', datatype: 'float32', shape: [ layer.out_channels ], data: reader.read(4 * layer.out_channels) } ] });
                     });
                     register(   19, 'QUANTIZED_FULLY_CONNECTED', 'Layer');
                     register(   20, 'TENSORFLOW_FLATTEN', 'Shape', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         const shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
                         layer.inputs[0].arguments[0].shape = shape;
                         layer.outputs[0].arguments[0].shape = shape;
                     });
                     register(   21, 'QUANTIZED_TENSORFLOW_FLATTEN', 'Shape', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         const shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
                         layer.inputs[0].arguments[0].shape = shape;
                         layer.outputs[0].arguments[0].shape = shape;
@@ -495,16 +583,17 @@ kmodel.Reader = class {
                     register( 1003, 'RESHAPE', 'Shape');
                     register(10240, 'K210_CONV', 'Layer', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.outputs = [ reader.parameter('output') ];
                         const layer_offset = reader.uint32();
                         const weights_offset = reader.uint32();
                         const bn_offset = reader.uint32();
                         const act_offset = reader.uint32();
                         reader.seek(layer_offset);
                         layer.interrupt_enabe = reader.uint64_bits({ int_en: 0, ram_flag: 1, full_add: 2, depth_wise_layer: 3 });
-                        layer.inputs = reader.mem_address('kpu', 'inputs');
-                        const outputs = reader.mem_address('kpu', 'outputs');
-                        layer.outputs = layer.flags & 1 ? layer.outputs : outputs;
+                        layer.inputs = [ reader.parameter('input', 'kpu') ];
+                        const outputs = [ reader.parameter('output', 'kpu') ];
+                        layer.outputs[0].arguments.push(outputs[0].arguments[0]);
+                        // layer.outputs = layer.flags & 1 ? layer.outputs : outputs;
                         layer.image_channel_num = reader.uint64_bits({ i_ch_num: 0, o_ch_num: 32, o_ch_num_coef: 48 });
                         layer.image_size =  reader.uint64_bits({ i_row_wid: 0, i_col_high: 10, o_row_wid: 32, o_col_high : 42 });
                         layer.kernel_pool_type_cfg = reader.uint64_bits({ kernel_type: 0, pad_type: 3, pool_type: 4, first_stride: 8, bypass_conv: 9, load_para: 10, dma_burst_size: 16, pad_value: 24, bwsx_base_addr: 32 });
@@ -515,26 +604,65 @@ kmodel.Reader = class {
                         layer.conv_value = reader.uint64_bits({ shr_w: 0, shr_x: 4, arg_w: 8, arg_x: 32 });
                         layer.conv_value2 = reader.uint64_bits({ arg_add: 0 });
                         layer.dma_parameter = reader.uint64_bits({ send_data_out: 0, channel_byte_num: 16, dma_total_byte: 32 });
-                        reader.seek(weights_offset);
+                        layer.chain = [];
+                        const ic = layer.image_channel_num.i_ch_num + 1;
+                        const oc = layer.image_channel_num.o_ch_num + 1;
+                        const filter = [ 1, 3 ][layer.kernel_pool_type_cfg.kernel_type];
+                        const weights_shape = layer.interrupt_enabe.depth_wise_layer ? [ oc, filter, filter ] : [ ic, oc, filter, filter ];
+                        const weights_size = weights_shape.reduce((a, b) => a * b);
                         reader.seek(bn_offset);
+                        const batch_norm = {
+                            type: { name: 'BATCH_NORM', category: 'Normalization' },
+                            weights: []
+                        };
+                        batch_norm.weights = new Array(oc);
+                        for (let i = 0; i < oc; i++) {
+                            batch_norm.weights[i] = reader.uint64_bits({ norm_mul: 0, norm_add: 24, norm_shift: 56, reserved: 60 });
+                            delete batch_norm.weights[i].reserved;
+                        }
+                        layer.chain.push(batch_norm);
                         reader.seek(act_offset);
+                        const activation = {};
+                        activation.type = { name: 'ACTIVATION', category: 'Activation' };
+                        activation.activate_para = new Array(16);
+                        for (let i = 0; i < 16; i++) {
+                            activation.activate_para[i] = reader.uint64_bits({ shift_number: 0, y_mul: 8, x_start: 24, reserved: 60 });
+                            delete activation.activate_para[i].reserved;
+                        }
+                        for (let i = 0; i < 16; i++) {
+                            activation.activate_para[i].bias = reader.int8();
+                        }
+                        layer.chain.push(activation);
+                        reader.seek(weights_offset);
+                        layer.inputs.push({
+                            name: 'weights',
+                            arguments: [ {
+                                name: 'const',
+                                datatype: 'uint8',
+                                shape: weights_shape,
+                                data: reader.read(weights_size)
+                            } ]
+                        });
+                        delete layer.kernel_pool_type_cfg.bwsx_base_addr;
+                        delete layer.kernel_calc_type_cfg.active_addr;
+                        delete layer.kernel_load_cfg.para_start_addr;
                     });
                     register(10241, 'K210_ADD_PADDING', '', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('kpu', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output', 'kpu') ];
                         layer.channels = reader.uint32();
                     });
                     register(10242, 'K210_REMOVE_PADDING', '', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('main', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
                         layer.channels = reader.uint32();
                     });
                     register(10243, 'K210_UPLOAD', '', (layer, reader) => {
                         layer.flags = reader.uint32();
-                        layer.inputs = reader.mem_address('main', 'inputs');
-                        layer.outputs = reader.mem_address('kpu', 'outputs');
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output', 'kpu') ];
                         const shape = [ reader.uint32(), reader.uint32(), reader.uint32() ];
                         layer.inputs[0].arguments[0].shape = shape;
                         layer.outputs[0].arguments[0].shape = shape;
@@ -555,14 +683,14 @@ kmodel.Reader = class {
                         // console.log(JSON.stringify(Object.fromEntries(Object.entries(layer).filter((entry) => !(entry[1] instanceof Uint8Array))), null, 2));
                     }
                     if (this._layers.length > 0) {
-                        this._layers.push({
-                            type: { name: 'INPUT' },
-                            outputs: this._layers[0].inputs
+                        this._layers.unshift({
+                            type: { name: 'input' },
+                            outputs: [ this._layers[0].inputs[0] ]
                         });
                     }
                     for (const output of outputs) {
                         this._layers.push({
-                            type: { name: 'OUTPUT' },
+                            type: { name: 'output' },
                             inputs: output.address
                         });
                     }
@@ -579,18 +707,32 @@ kmodel.Reader = class {
                         outputs: reader.uint32(),
                         reserved0: reader.uint32(),
                     };
+                    reader.memory_type_t = function() {
+                        const value = this.uint32();
+                        return [ 'const', 'main', 'kpu' ][value];
+                    };
+                    reader.datatype_t = function() {
+                        const value = this.uint32();
+                        return [ 'float32', 'uint8' ][value];
+                    };
                     reader.memory_range = function() {
                         return {
-                            memory_type: this.uint32(), // 0=const, 1=main, 2=k210_kpu
-                            datatype: this.uint32(), // 0=float32, 1=uint8
+                            memory_type: this.memory_type_t(),
+                            datatype: this.datatype_t(),
                             start: this.uint32(),
                             size: this.uint32()
                         };
                     };
                     reader.argument = function() {
                         const memory = this.memory_range();
-                        const type = [ 'const', 'main', 'kpu' ][memory.memory_type];
-                        return { name: type + ':' + memory.start.toString() };
+                        const value = {
+                            name: memory.memory_type + ':' + memory.start.toString(),
+                            datatype: memory.datatype
+                        };
+                        if (memory.memory_type === 'const') {
+                            value.data = constants.slice(memory.start, memory.start + memory.size);
+                        }
+                        return value;
                     };
                     reader.parameter = function(name) {
                         const argument = this.argument();
@@ -599,8 +741,11 @@ kmodel.Reader = class {
                     reader.runtime_shape_t = function() {
                         return [ reader.uint32(), reader.uint32(), reader.uint32(), reader.uint32() ];
                     };
+                    reader.padding = function() {
+                        return { before: reader.int32(), after: reader.int32() };
+                    };
                     reader.runtime_paddings_t = function() {
-                        return [ reader.int32(), reader.int32(), reader.int32(), reader.int32(), reader.int32(), reader.int32(), reader.int32(), reader.int32() ];
+                        return [ this.padding(), this.padding(), this.padding(), this.padding() ];
                     };
                     reader.scalar = function() {
                         return {
@@ -613,9 +758,11 @@ kmodel.Reader = class {
                         value.activate_para = new Array(16);
                         for (let i = 0; i < 16; i++) {
                             value.activate_para[i] = this.uint64_bits({ shift_number: 0, y_mul: 8, x_start: 24, reserved: 60 });
+                            delete value.activate_para[i].reserved;
+                        }
+                        for (let i = 0; i < 16; i++) {
+                            value.activate_para[i].bias = reader.int8();
                         }
-                        value.activate_para_bias0 = reader.read(8);
-                        value.activate_para_bias1 = reader.read(8);
                         return value;
                     };
                     reader.unary_op_t = function() {
@@ -630,18 +777,18 @@ kmodel.Reader = class {
                         const value = reader.uint32();
                         return [ 'mean', 'min', 'max', 'sum' ][value];
                     };
-                    this._inputs = new Array(model_header.inputs);
-                    for (let i = 0; i < this._inputs.length; i++) {
-                        this._inputs[i] = reader.memory_range();
+                    const inputs = new Array(model_header.inputs);
+                    for (let i = 0; i < inputs.length; i++) {
+                        inputs[i] = reader.parameter('input' + (i == 0 ? '' : (i + 1).toString()));
                     }
-                    for (let i = 0; i < this._inputs.length; i++) {
-                        this._inputs[i].shape = reader.runtime_shape_t();
+                    for (let i = 0; i < inputs.length; i++) {
+                        inputs[i].arguments[0].shape = reader.runtime_shape_t();
                     }
-                    this._outputs = new Array(model_header.outputs);
-                    for (let i = 0; i < this._outputs.length; i++) {
-                        this._outputs[i] = reader.memory_range();
+                    const outputs = new Array(model_header.outputs);
+                    for (let i = 0; i < outputs.length; i++) {
+                        outputs[i] = reader.parameter('output' + (i == 0 ? '' : (i + 1).toString()));
                     }
-                    this._constants = reader.read(model_header.constants);
+                    const constants = reader.read(model_header.constants);
                     this._layers = new Array(model_header.nodes);
                     for (let i = 0; i < this._layers.length; i++) {
                         this._layers[i] = {
@@ -687,7 +834,13 @@ kmodel.Reader = class {
                         layer.a_cols = reader.int32();
                         layer.b_cols = reader.int32();
                         layer.fused_activation = [ reader.float32(), reader.float32() ];
-                        // xtl::span<const float> bias;
+                        const bias = reader.read(4 * layer.b_cols);
+                        if (!bias.every((value) => value === 0)) {
+                            layer.inputs.push({
+                                name: 'bias',
+                                arguments: [ { name: 'const', datatype: 'float32', shape: [ layer.b_cols ], data: bias } ]
+                            });
+                        }
                     });
                     register(  0x05, 'pad', 'Shape', (layer, reader) => {
                         layer.inputs = [ reader.parameter('input') ];
@@ -710,8 +863,26 @@ kmodel.Reader = class {
                         layer.outputs[0].arguments[0].shape = reader.runtime_shape_t();
                         layer.init_value = reader.float32();
                     });
-                    register(  0x08, 'reduce_window2d');
-                    register(  0x09, 'memory_copy', '');
+                    register(  0x08, 'reduce_window2d', '', (layer, reader) => {
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
+                        layer.reduce_op = reader.reduce_op_t();
+                        layer.inputs[0].arguments[0].shape = reader.runtime_shape_t();
+                        layer.padding_h = reader.padding();
+                        layer.padding_w = reader.padding();
+                        layer.filter_h = reader.int32();
+                        layer.filter_w = reader.int32();
+                        layer.stride_h = reader.int32();
+                        layer.stride_w = reader.int32();
+                        layer.dilation_h = reader.int32();
+                        layer.dilation_w = reader.int32();
+                        layer.init_value = reader.float32();
+                        layer.fused_activation = [ reader.float32(), reader.float32() ];
+                    });
+                    register(  0x09, 'memory_copy', '', (layer, reader) => {
+                        layer.inputs = [ reader.parameter('input') ];
+                        layer.outputs = [ reader.parameter('output') ];
+                    });
                     register(  0x0A, 'resize_image', '');
                     register(  0x0B, 'softmax', 'Activation');
                     register(  0x0C, 'transpose', 'Transform', (layer, reader) => {
@@ -766,8 +937,10 @@ kmodel.Reader = class {
                         layer.batches = reader.int32();
                         layer.reserved0 = reader.int32();
                         layer.interrupt_enabe = reader.uint64_bits({ int_en: 0, ram_flag: 1, full_add: 2, depth_wise_layer: 3 });
-                        layer.inputs = reader.mem_address('kpu', 'inputs'); // image_src_addr
-                        const outputs = reader.mem_address('kpu', 'output'); // image_dst_addr
+                        const image_src_addr = reader.uint32();
+                        const image_dst_addr = reader.uint32();
+                        layer.inputs = [ { name: 'input', arguments: [ { name: 'kpu:' + image_src_addr.toString() } ] } ];
+                        const outputs = [ { name: 'output', arguments: [ { name: 'kpu:' + image_dst_addr.toString() } ] } ];
                         layer.outputs[0].arguments.push(outputs[0].arguments[0]);
                         // layer.outputs = layer.flags & 1 ? layer.outputs : outputs;
                         layer.image_channel_num = reader.uint64_bits({ i_ch_num: 0, o_ch_num: 32, o_ch_num_coef: 48 });
@@ -784,16 +957,18 @@ kmodel.Reader = class {
                         const ic = layer.image_channel_num.i_ch_num + 1;
                         const oc = layer.image_channel_num.o_ch_num + 1;
                         const filter = [ 1, 3 ][layer.kernel_pool_type_cfg.kernel_type];
-                        const weights_size = layer.interrupt_enabe.depth_wise_layer ? oc * filter * filter : ic * oc * filter * filter;
+                        const weights_shape = layer.interrupt_enabe.depth_wise_layer ? [ oc, filter, filter ] : [ ic, oc, filter, filter ];
+                        const weights_size = weights_shape.reduce((a, b) => a * b);
                         reader.skip(layer.kernel_pool_type_cfg.bwsx_base_addr);
                         delete layer.kernel_pool_type_cfg.bwsx_base_addr;
                         const batch_norm = {
-                            type: { name: 'batchnorm', category: 'Normalization' },
+                            type: { name: 'batch_norm', category: 'Normalization' },
                             weights: []
                         };
                         batch_norm.weights = new Array(oc);
                         for (let i = 0; i < oc; i++) {
-                            batch_norm.weights[i] = reader.uint64_bits({ norm_mul: 0, norm_add: 24, norm_shift: 56, reserverd: 60 });
+                            batch_norm.weights[i] = reader.uint64_bits({ norm_mul: 0, norm_add: 24, norm_shift: 56, reserved: 60 });
+                            delete batch_norm.weights[i].reserved;
                         }
                         layer.chain.push(batch_norm);
                         reader.skip(layer.kernel_calc_type_cfg.active_addr);
@@ -803,7 +978,15 @@ kmodel.Reader = class {
                         layer.chain.push(activation);
                         reader.skip(layer.kernel_load_cfg.para_start_addr);
                         delete layer.kernel_load_cfg.para_start_addr;
-                        layer.weights = reader.read(weights_size);
+                        layer.inputs.push({
+                            name: 'weights',
+                            arguments: [ {
+                                name: 'const',
+                                datatype: 'uint8',
+                                shape: weights_shape,
+                                data: reader.read(weights_size)
+                            } ]
+                        });
                     });
                     for (const layer of this._layers) {
                         const type = types.get(layer.opcode);
@@ -826,6 +1009,18 @@ kmodel.Reader = class {
                         // console.log(JSON.stringify(Object.fromEntries(Object.entries(layer).filter((entry) => !(entry[1] instanceof Uint8Array))), null, 2));
                         delete layer.opcode;
                     }
+                    for (const input of inputs) {
+                        this._layers.unshift({
+                            type: { name: 'INPUT' },
+                            outputs: [ input ]
+                        });
+                    }
+                    for (const output of outputs) {
+                        this._layers.push({
+                            type: { name: 'OUTPUT' },
+                            inputs: [ output ]
+                        });
+                    }
                     break;
                 }
                 case 5: {

+ 36 - 0
test/models.json

@@ -2318,6 +2318,20 @@
     "format": "kmodel v3",
     "link":   "https://github.com/lutzroeder/netron/issues/871"
   },
+  {
+    "type":   "kmodel",
+    "target": "iris.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/8111178/iris.kmodel.zip[iris.kmodel]",
+    "format": "kmodel v4",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
+  {
+    "type":   "kmodel",
+    "target": "mbnetv1.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/8111310/mbnetv1.kmodel.zip[mbnetv1.kmodel]",
+    "format": "kmodel v3",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
   {
     "type":   "kmodel",
     "target": "mobilenet_v2.kmodel",
@@ -2326,6 +2340,21 @@
     "error":  "Unsupported model version '5' in 'mobilenet_v2.kmodel'.",
     "link":   "https://github.com/lutzroeder/netron/issues/871"
   },
+  {
+    "type":   "kmodel",
+    "target": "model.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/8111371/model.kmodel.zip[model.kmodel]",
+    "format": "kmodel v4",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
+  {
+    "type":   "kmodel",
+    "target": "mnist.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/8111312/mnist.kmodel.zip[mnist.kmodel]",
+    "format": "kmodel v5",
+    "error":  "Unsupported model version '5' in 'mnist.kmodel'.",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
   {
     "type":   "kmodel",
     "target": "RFB-320.kmodel",
@@ -2333,6 +2362,13 @@
     "format": "kmodel v4",
     "link":   "https://github.com/lutzroeder/netron/issues/871"
   },
+  {
+    "type":   "kmodel",
+    "target": "ulffd_landmark.kmodel",
+    "source": "https://github.com/lutzroeder/netron/files/8111629/ulffd_landmark.kmodel.zip[ulffd_landmark.kmodel]",
+    "format": "kmodel v4",
+    "link":   "https://github.com/lutzroeder/netron/issues/871"
+  },
   {
     "type":   "lasagne",
     "target": "net2.pkl",