MLBEDSW-7626: Add constraint for PAD op paddings

PAD input tensor shape plus paddings must equal output tensor shape.

Change-Id: Icc5dea9bf6a8f6e1c8402f4d9af4d9796e8ef1aa
Signed-off-by: Johan Gunnarsson <johan.gunnarsson@arm.com>
diff --git a/SUPPORTED_OPS.md b/SUPPORTED_OPS.md
index 8a992b5..17a17ec 100644
--- a/SUPPORTED_OPS.md
+++ b/SUPPORTED_OPS.md
@@ -19,7 +19,7 @@
 # Supported Ops
 
 This file was automatically generated by Vela using the `--supported-ops-report` parameter.  
-Vela version: `3.8.1.dev17+g1d5e859.d20230711`
+Vela version: `3.8.1.dev24+g3c2461d`
 
 This file complies with
 [**Gitiles Markdown syntax**](https://github.com/google/gitiles/blob/master/Documentation/markdown.md)
@@ -292,6 +292,7 @@
 
 - Number of input tensors must be exactly 2
 - The padding tensor must be constant
+- Shape of output tensor must equal to size of input tensor plus padding
 - The padding tensor must have the shape [3,2] or [4,2]
 - The pad tensor can only pad width and height
 - Pad tensor must be of type: int32, int64
@@ -427,3 +428,4 @@
 - Must not use Projection
 - Must not use Normalisation
 - All input and recurrent weights must be available
+- All recurrent weights must be 2D
diff --git a/ethosu/vela/test/test_tflite_model_semantic.py b/ethosu/vela/test/test_tflite_model_semantic.py
index e7fd307..7ca1bbd 100644
--- a/ethosu/vela/test/test_tflite_model_semantic.py
+++ b/ethosu/vela/test/test_tflite_model_semantic.py
@@ -356,6 +356,18 @@
     assert not semantic_checker.is_operator_semantic_valid(op)
 
 
+def test_constraint_pad_output_shape():
+    # Incorrect output tensor shape
+    op = create_pad_op(
+        in_shape=[1, 1, 1, 1],
+        out_shape=[1, 3, 3, 1],
+        padding=[[0, 0], [1, 1], [1, 1], [0, 0]],
+    )
+    assert semantic_checker.is_operator_semantic_valid(op)
+    op.outputs[0].shape = [1, 1, 1, 1]
+    assert not semantic_checker.is_operator_semantic_valid(op)
+
+
 def create_strided_slice():
     # Creates a valid strided slice operator with some valid inputs/outputs
     op = create_strided_slice_op([1, 10, 10, 10], [1, 5, 5, 10], [127, 2, 2, 0], [0, 7, -3, 0])
diff --git a/ethosu/vela/tflite_model_semantic.py b/ethosu/vela/tflite_model_semantic.py
index 6264891..ea7ef4a 100644
--- a/ethosu/vela/tflite_model_semantic.py
+++ b/ethosu/vela/tflite_model_semantic.py
@@ -184,6 +184,7 @@
         # Pad specific checks:
         self.specific_constraints[Op.Pad].append(TFLiteSemantic.constraint_pad_input_count)
         self.specific_constraints[Op.Pad].append(TFLiteSemantic.constraint_pad_constant)
+        self.specific_constraints[Op.Pad].append(TFLiteSemantic.constraint_pad_output_shape)
 
         # HardSwish specific checks:
         self.specific_constraints[Op.HardSwish].append(TFLiteSemantic.constraint_input_8bit)
@@ -586,6 +587,16 @@
         return valid, f"Op has non-constant padding tensor: {op.inputs[1].values}"
 
     @staticmethod
+    def constraint_pad_output_shape(op):
+        "Shape of output tensor must equal to size of input tensor plus padding"
+        input_shape = op.inputs[0].shape
+        expected_output_shape = op.outputs[0].shape
+        pad_tensor = op.inputs[1].values
+        actual_output_shape = input_shape + pad_tensor.T[0] + pad_tensor.T[1]
+        valid = np.array_equal(actual_output_shape, expected_output_shape)
+        return valid, f"Op has wrong output tensor shape: {expected_output_shape}, has shape: {actual_output_shape}"
+
+    @staticmethod
     def constraint_stridedslice_inputs_const(op):
         "Begin, End and Stride Input tensors must be constant"
         valid = True