Opensource ML embedded evaluation kit

Change-Id: I12e807f19f5cacad7cef82572b6dd48252fd61fd
diff --git a/scripts/py/gen_fpga_mem_map.py b/scripts/py/gen_fpga_mem_map.py
new file mode 100644
index 0000000..6a2d1d2
--- /dev/null
+++ b/scripts/py/gen_fpga_mem_map.py
@@ -0,0 +1,192 @@
+#  Copyright (c) 2021 Arm Limited. All rights reserved.
+#  SPDX-License-Identifier: Apache-2.0
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+from argparse import ArgumentParser
+
+"""
+This file is used as part of post build steps to generate 'images.txt' file
+which can be copied over onto the MPS3 board's SD card. The purpose is to
+limit having to manually edit the file based on different load regions that
+the build scatter file might dictate.
+"""
+
+def is_commented(line):
+    if (line.startswith(";")):
+        return True
+    else:
+        return False
+
+
+def is_load_rom(line):
+    load_region_specifiers = ['LOAD_ROM', 'LD_ROM', 'LOAD_REGION']
+
+    for load_specifier in load_region_specifiers:
+        if line.startswith(load_specifier):
+            return True
+
+    return False
+
+
+class TargetSubsystem:
+
+    def __init__(self, target_subsystem_name: str):
+        """
+        Constructor for target class.
+        Arguments:
+            target_subsystem_name: name of the target subsystem
+        """
+        # Dict with mem map and binary names we expect
+        self.subsystems = {
+            "sse-200": {
+                "mmap_mcc" : {
+                    # FPGA addr |  MCC addr  |
+                    "0x00000000": "0x00000000", # ITCM (NS)
+                    "0x10000000": "0x01000000", # ITCM (S)
+                    "0x20000000": "0x02000000", # DTCM (NS)
+                    "0x30000000": "0x03000000", # DTCM (S)
+                    "0x60000000": "0x08000000"  # DDR (NS)
+                },
+                "bin_names": {
+                    0: "itcm.bin",
+                    1: "dram.bin"
+                }
+            },
+            "sse-300": {
+                "mmap_mcc" : {
+                    # FPGA addr |  MCC addr  |
+                    "0x00000000": "0x00000000", # ITCM (NS)
+                    "0x01000000": "0x02000000", # BRAM or FPGA's data SRAM (NS)
+                    "0x60000000": "0x08000000", # DDR (NS)
+                    "0x70000000": "0x0c000000"  # DDR (S)
+                },
+                "bin_names": {
+                    0: "itcm.bin",
+                    1: "dram.bin"
+                }
+            }
+        }
+
+        self.name = target_subsystem_name
+
+
+    def is_supported(self, target_subsystem: str) -> bool:
+        """
+        Checks if the target subsystem exists within systems
+        supported by this script
+        """
+        if target_subsystem in self.subsystems.keys():
+            return True
+
+        print(f"Platforms supported: {self.subsystems.keys()}")
+        return False
+
+
+    def mps3_mappings(self) -> dict:
+        """
+        Returns the FPGA <--> MCC address translations
+        as a dict
+        """
+        if self.is_supported(self.name):
+            return self.subsystems[self.name]['mmap_mcc']
+        return {}
+
+
+    def mps3_bin_names(self) -> dict:
+        """
+        Returns expected binary names for the executable built
+        for Cortex-M55 or Cortex-M55+Ethos-U55 targets in the
+        form of a dict with index and name
+        """
+        if self.is_supported(self.name):
+            return self.subsystems[self.name]['bin_names']
+
+        return {}
+
+
+def main(args):
+    """
+    Generates the output txt file with MCC to FPGA address mapping used
+    that is used by the MCC on FPGA to load executable regions into
+    correct regions in memory.
+    """
+    # List out arguments used:
+    scatter_file_path = args.scatter_file_path
+    target_subsystem_name = args.target_subsystem
+    output_file_path = args.output_file_path
+
+    target = TargetSubsystem(target_subsystem_name=target_subsystem_name)
+
+    if target.is_supported(target_subsystem_name) != True:
+        print(f'Target {target_subsystem_name} not supported.')
+        return
+
+    with open(scatter_file_path,'r') as scatter_file:
+        lines_read = scatter_file.readlines()
+        str_list = []
+
+        bin_names = None
+        mem_map = None
+
+        mem_map = target.mps3_mappings()
+        bin_names = target.mps3_bin_names()
+
+        str_list.append("TITLE: Arm MPS3 FPGA prototyping board Images Configuration File\n")
+        str_list.append("[IMAGES]\n\n")
+
+        cnt = 0
+        for line in lines_read:
+            if is_commented(line) or is_load_rom(line) != True:
+                continue
+
+            addr = line.split()[1]
+
+            if mem_map.get(addr, None) == None:
+                raise RuntimeError(
+                    'Translation for this address unavailable')
+            if cnt > len(bin_names):
+                raise RuntimeError(
+                    f"bin names len exceeded: {cnt}")
+
+            str_list.append("IMAGE" + str(cnt) + "ADDRESS: " +
+                mem_map[addr] + " ; MCC@" + mem_map[addr] +
+                " <=> FPGA@"  + addr + "\n")
+            str_list.append("IMAGE" + str(cnt) + "UPDATE: AUTO\n")
+            str_list.append("IMAGE" + str(cnt) + "FILE: \SOFTWARE\\" +
+                bin_names[cnt] + "\n\n")
+            cnt += 1
+
+        if cnt > 0 and cnt < 33:
+            str_list.insert(2,
+                "TOTALIMAGES: {} ;Number of Images (Max: 32)\n\n".format(
+                    cnt))
+        else:
+            raise RuntimeError('Invalid image count')
+
+        if os.path.exists(output_file_path):
+            os.remove(output_file_path)
+        print(''.join(str_list), file=open(output_file_path, "a"))
+
+
+if __name__ == "__main__":
+    parser = ArgumentParser()
+    parser.add_argument("--scatter_file_path", type=str, required=True,
+                        help="Path to the scatter file")
+    parser.add_argument("--target_subsystem", type=str, required=True,
+                        help="Target subsystem in use")
+    parser.add_argument("--output_file_path", type=str, required=True,
+                        help="Output file path")
+    args = parser.parse_args()
+    main(args)