MLBEDSW-7211: Convert fixup_asymmetric_weights to supported ops check

Changed default behaviour to place int8 ops with asymmetric quantization on cpu, and added an option to force symmetric quantization

Change-Id: Ib9b717aaf61eae78833254ca3dfa745f4f253dc6
Signed-off-by: wilisa01 <william.isaksson@arm.com>
diff --git a/ethosu/vela/tflite_graph_optimiser.py b/ethosu/vela/tflite_graph_optimiser.py
index 2026892..3a49309 100644
--- a/ethosu/vela/tflite_graph_optimiser.py
+++ b/ethosu/vela/tflite_graph_optimiser.py
@@ -1583,16 +1583,42 @@
     return op
 
 
-def fixup_asymmetric_weights(op, arch, nng):
-    if op.run_on_npu and (op.type.is_conv2d_op() or op.type.is_depthwise_conv2d_op()):
-        if op.ifm.dtype == DataType.int8:
+def detect_asymmetric_weights(op):
+    # Check all ops (cpu and npu)
+    if op.type.is_conv2d_op() or op.type.is_depthwise_conv2d_op():
+        if op.ifm.dtype in (DataType.int8, DataType.int16):
             if not np.all(op.weights.quantization.zero_point == 0):
-                print(f"Warning: {op.type} '{op.name}' has asymmetric weights, zero points have been adjusted.")
-                op.weights.quantization.zero_point *= 0
+                print(f"Warning: Op {op.type} '{op.name}' has asymmetric weights.", end=" ")
+                return True
+    return False
 
+
+def fixup_asymmetric_weights(op, arch, nng):
+    if detect_asymmetric_weights(op):
+        if op.run_on_npu:
+            print("Zero points have been adjusted.")
+            op.weights.quantization.zero_point *= 0
     return op
 
 
+def check_asymmetric_weights(op, arch, nng):
+    # This function can modify the run_on_npu flag which causes an operator to be placed on the CPU. It is usually only
+    # set by the supported operator checks. Therefore, it should be run immediately after those checks to avoid the
+    # possibility of other graph optimiser functions modify the operator (that is later run on the CPU)
+    if detect_asymmetric_weights(op):
+        if op.run_on_npu:
+            print("To run the operator on Ethos-U use the option --force-symmetric-int-weights")
+            op.run_on_npu = False
+    return op
+
+
+def fixup_or_check_asymmetric_weights(force_symmetric_int_weights):
+    if force_symmetric_int_weights:
+        return fixup_asymmetric_weights
+    else:
+        return check_asymmetric_weights
+
+
 def convert_mean_to_depthwise_conv_or_avgpool(op, arch, nng):
     if op.type == Op.Mean and op.run_on_npu:
         inp, axis = op.inputs
@@ -1871,9 +1897,13 @@
     return op
 
 
-def tflite_optimise_graph(nng, arch):
+def tflite_optimise_graph(nng, arch, force_symmetric_int_weights):
     # Compile time static optimisations
-    optimisation_list = [optimise_quantize, convert_shape_op_to_constant_tensor]
+    optimisation_list = [
+        optimise_quantize,
+        convert_shape_op_to_constant_tensor,
+        fixup_or_check_asymmetric_weights(force_symmetric_int_weights),
+    ]
 
     for idx, sg in enumerate(nng.subgraphs):
         nng.subgraphs[idx] = rewrite_graph.rewrite_graph_pre_order(
@@ -1886,10 +1916,7 @@
         )
 
     # Pre-processing step
-    pre_process_list = [
-        supported_operator_check,
-        set_ifm_ofm_op_shapes,
-    ]
+    pre_process_list = [supported_operator_check, set_ifm_ofm_op_shapes]
 
     for idx, sg in enumerate(nng.subgraphs):
         nng.subgraphs[idx] = rewrite_graph.rewrite_graph_pre_order(