Build system refactoring

The source tree is configured for a specific target as defined in the
targets directory.

The common target components are defined in targets/common. Targets
for real platform should include this directory to get the default
target libraries setup.

Change-Id: I7fced4bfacec97432cbbd4125bd5b4cdd21122e3
diff --git a/applications/CMakeLists.txt b/applications/CMakeLists.txt
new file mode 100644
index 0000000..e014136
--- /dev/null
+++ b/applications/CMakeLists.txt
@@ -0,0 +1,19 @@
+#
+# 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
+#
+# 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.
+#
+
+add_subdirectory(freertos)
diff --git a/applications/freertos/CMakeLists.txt b/applications/freertos/CMakeLists.txt
new file mode 100644
index 0000000..f803d43
--- /dev/null
+++ b/applications/freertos/CMakeLists.txt
@@ -0,0 +1,27 @@
+#
+# 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
+#
+# 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.
+#
+
+# Exclude this app if freertos target does not exist
+if (NOT TARGET freertos_kernel)
+    message("Skipping FreeRTOS application")
+    return()
+endif()
+
+ethosu_add_executable(freertos PRIVATE
+    SOURCES main.cpp
+    LIBRARIES freertos_kernel)
diff --git a/targets/corstone-300/main.cpp b/applications/freertos/main.cpp
similarity index 95%
rename from targets/corstone-300/main.cpp
rename to applications/freertos/main.cpp
index 8a8b978..095af50 100644
--- a/targets/corstone-300/main.cpp
+++ b/applications/freertos/main.cpp
@@ -28,7 +28,6 @@
 // Ethos-U
 #include "ethosu_driver.h"
 #include "inference_process.hpp"
-#include "uart.h"
 
 // System includes
 #include <stdio.h>
@@ -37,14 +36,6 @@
 using namespace InferenceProcess;
 
 /****************************************************************************
- * Defines
- ****************************************************************************/
-
-#define ETHOSU_BASE_ADDRESS 0x48102000
-
-#define ETHOSU_IRQ 56
-
-/****************************************************************************
  * InferenceJob
  ****************************************************************************/
 
@@ -203,10 +194,6 @@
     0x9A, 0xC5, 0xE0, 0xE5, 0xC2, 0xCB, 0xDC, 0xA4, 0xF8, 0xB2, 0x90, 0xA3, 0xB4, 0xE0, 0xE9, 0x72, 0xE4, 0xE9, 0xFE,
     0xE4, 0xFB, 0x95, 0xE7, 0xF3, 0xFB, 0xEA, 0x90, 0xFA};
 
-void ethosuIrqHandler() {
-    ethosu_irq_handler();
-}
-
 void inferenceProcessTask(void *pvParameters) {
     QueueHandle_t queue = reinterpret_cast<QueueHandle_t>(pvParameters);
 
@@ -256,24 +243,12 @@
     xQueueReceive(senderQueue, &j, portMAX_DELAY);
     printf("Received inference job response. status=%u\n", j->status);
 
-    vTaskDelete(NULL);
+    exit(0);
 }
 
 } // namespace
 
 int main() {
-    // Initialize UART driver
-    uart_init();
-
-    // Initialize Ethos-U driver
-    if (ethosu_init(reinterpret_cast<const void *>(ETHOSU_BASE_ADDRESS))) {
-        printf("Failed to initialize Arm Ethos-U.\n");
-        return 1;
-    }
-
-    NVIC_SetVector(static_cast<IRQn_Type>(ETHOSU_IRQ), reinterpret_cast<uint32_t>(&ethosuIrqHandler));
-    NVIC_EnableIRQ(static_cast<IRQn_Type>(ETHOSU_IRQ));
-
     // Inference process
     QueueHandle_t inferenceProcessQueue = xQueueCreate(10, sizeof(xInferenceJob *));
     xTaskCreate(inferenceProcessTask, "inferenceProcess", 2 * 1024, inferenceProcessQueue, 1, nullptr);
diff --git a/cmake/helpers.cmake b/cmake/helpers.cmake
index e3b604a..686488b 100644
--- a/cmake/helpers.cmake
+++ b/cmake/helpers.cmake
@@ -16,26 +16,48 @@
 # limitations under the License.
 #
 
-function(ethosu_link_options target scope)
-    cmake_parse_arguments(ARG "" "LINK_FILE" "" ${ARGN})
+# Add .elf to all executables
+set(CMAKE_EXECUTABLE_SUFFIX ".elf")
+
+#############################################################################
+# Link options
+#############################################################################
+
+function(ethosu_target_link_options target scope)
+    cmake_parse_arguments(ARG "" "LINK_FILE;ENTRY" "" ${ARGN})
+
+    # Store the link file in a property to be evaluated by the executable.
+    set_property(GLOBAL PROPERTY ETHOSU_TARGET_LINK_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_LINK_FILE})
+
+    if (ARG_ENTRY)
+        target_link_options(${target} ${scope} --entry Reset_Handler)
+    endif()
+endfunction()
+
+function(ethosu_eval_link_options target)
+    # Get the link file from the cache
+    get_property(LINK_FILE GLOBAL PROPERTY ETHOSU_TARGET_LINK_FILE)
+
+    set(prop "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>")
 
     if (CMAKE_CXX_COMPILER_ID STREQUAL "ARMClang")
-        set(LINK_FILE_OUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_LINK_FILE}.scatter)
+        set(LINK_FILE_OUT ${LINK_FILE}.scatter)
         set(LINK_FILE_OPTION "--scatter")
 
+        target_link_options(${target} PUBLIC
+            --predefine=\"-D$<JOIN:${prop},\" ;--predefine=\"-D>\")
     elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-        set(LINK_FILE ${ARG_LINK_FILE}.ld)
-        set(LINK_FILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/${LINK_FILE})
-        set(LINK_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/${LINK_FILE})
+        set(LINK_FILE_IN ${LINK_FILE}.ld)
+        get_filename_component(LINK_FILE_OUT_BASE ${LINK_FILE} NAME)
+        set(LINK_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/${LINK_FILE_OUT_BASE}-${target}.ld)
         set(LINK_FILE_OPTION "-T")
 
-        set(prop "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>")
         add_custom_command(
             OUTPUT ${LINK_FILE_OUT}
             DEPENDS ${LINK_FILE_IN}
             BYPRODUCTS ${LINK_FILE_OUT}
             COMMAND ${CMAKE_C_COMPILER} -E -x c -P -o ${LINK_FILE_OUT} ${LINK_FILE_IN}
-            COMMAND_EXPAND_LISTS "$<$<BOOL:${prop}>:-D$<JOIN:${prop},;-D>>"
+            COMMAND_EXPAND_LISTS "-D$<JOIN:${prop},;-D>"
             COMMENT "Preprocessing and generating linker script"
             VERBATIM)
         add_custom_target(${target}-linker-script
@@ -44,6 +66,59 @@
         add_dependencies(${target} ${target}-linker-script)
     endif()
 
-    target_link_options(${target} ${scope} ${LINK_FILE_OPTION} ${LINK_FILE_OUT})
+    target_link_options(${target} PUBLIC ${LINK_FILE_OPTION} ${LINK_FILE_OUT})
     set_target_properties(${target} PROPERTIES LINK_DEPENDS ${LINK_FILE_OUT})
 endfunction()
+
+#############################################################################
+# Executable
+#############################################################################
+
+function(ethosu_add_executable target)
+    cmake_parse_arguments(ARGS "" "TARGET_LIBRARY" "SOURCES;LIBRARIES" ${ARGN})
+    add_executable(${target})
+
+    target_sources(${target} PRIVATE
+        ${ARGS_SOURCES})
+
+    if(NOT ARGS_TARGET_LIBRARY)
+        set(ARGS_TARGET_LIBRARY ethosu_target_init)
+    endif()
+
+    target_link_libraries(${target} PRIVATE
+        ${ARGS_TARGET_LIBRARY} ethosu_core ${ARGS_LIBRARIES})
+
+    ethosu_eval_link_options(${target})
+endfunction()
+
+#############################################################################
+# Test
+#############################################################################
+
+function(ethosu_add_test target)
+    if(NOT BUILD_TESTING)
+        return()
+    endif()
+
+    cmake_parse_arguments(ARGS "" "NAME" "COMMAND" ${ARGN})
+
+    if (NOT ARGS_COMMAND)
+        set(ARGS_COMMAND ${ETHOSU_COMMAND_DEFAULT})
+    endif()
+
+    if (NOT ARGS_NAME)
+        set(ARGS_NAME ${target})
+    endif()
+
+    add_test(NAME ${ARGS_NAME}
+        COMMAND ${ARGS_COMMAND} $<TARGET_FILE:${target}>)
+endfunction()
+
+#############################################################################
+# Executable and test
+#############################################################################
+
+function(ethosu_add_executable_test target)
+    ethosu_add_executable(${target} ${ARGN})
+    ethosu_add_test(${target} ${ARGN})
+endfunction()
diff --git a/targets/common/CMakeLists.txt b/targets/common/CMakeLists.txt
new file mode 100644
index 0000000..fa9b953
--- /dev/null
+++ b/targets/common/CMakeLists.txt
@@ -0,0 +1,63 @@
+#
+# 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
+#
+# 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.
+#
+
+#############################################################################
+# Core software
+#############################################################################
+
+set(ETHOSU_CORE_SOFTWARE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../../core_software" CACHE PATH "Path to Arm Ethos-U Core Software")
+
+set(CORE_SOFTWARE_RTOS "All" CACHE STRING "")
+
+add_subdirectory(${ETHOSU_CORE_SOFTWARE_PATH} core_software)
+
+###############################################################################
+# Target
+#
+# ethosu_target_init       # Init and drivers
+#     |
+#     v
+# ethosu_target_startup    # CMSIS startup
+#     |
+#     v
+# ethosu_target_link       # Target linker script
+#     |
+#     v
+# ethosu_target_common     # Common for all targets
+###############################################################################
+
+# Common
+add_library(ethosu_target_common INTERFACE)
+target_include_directories(ethosu_target_common INTERFACE include)
+
+# Link
+add_library(ethosu_target_link INTERFACE)
+target_link_libraries(ethosu_target_link INTERFACE ethosu_target_common)
+
+# Startup
+add_library(ethosu_target_startup INTERFACE)
+target_link_libraries(ethosu_target_startup INTERFACE ethosu_target_link)
+
+target_link_libraries(ethosu_target_startup INTERFACE $<TARGET_OBJECTS:cmsis_startup>)
+add_dependencies(ethosu_target_startup $<TARGET_OBJECTS:cmsis_startup>)
+
+# Init
+add_library(ethosu_target_init INTERFACE)
+target_link_libraries(ethosu_target_init INTERFACE ethosu_target_startup)
+target_sources(ethosu_target_init INTERFACE src/init.cpp)
+
diff --git a/targets/common/include/target.hpp b/targets/common/include/target.hpp
new file mode 100644
index 0000000..abe3fdc
--- /dev/null
+++ b/targets/common/include/target.hpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 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
+ *
+ * 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.
+ */
+
+#ifndef _TARGET_HPP_
+#define _TARGET_HPP_
+
+#ifdef __cplusplus
+
+namespace EthosU {
+
+/**
+ * Initialize the target platform.
+ */
+void targetSetup();
+
+} // namespace EthosU
+
+#endif
+
+#endif
diff --git a/targets/common/src/init.cpp b/targets/common/src/init.cpp
new file mode 100644
index 0000000..6eecc42
--- /dev/null
+++ b/targets/common/src/init.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020 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
+ *
+ * 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.
+ */
+
+#include <target.hpp>
+
+__attribute__((constructor)) void init() {
+    EthosU::targetSetup();
+}
diff --git a/targets/corstone-300/CMakeLists.txt b/targets/corstone-300/CMakeLists.txt
index b141ac9..d162537 100644
--- a/targets/corstone-300/CMakeLists.txt
+++ b/targets/corstone-300/CMakeLists.txt
@@ -17,7 +17,7 @@
 #
 
 #############################################################################
-# Toolchain
+# Default parameters
 #############################################################################
 
 set(TARGET_CPU "cortex-m55")
@@ -26,6 +26,14 @@
     set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/toolchain/armclang.cmake")
 endif()
 
+set(ETHOSU_COMMAND_DEFAULT
+    FVP_Corstone_SSE-300_Ethos-U55
+        -C mps3_board.visualisation.disable-visualisation=1
+        -C mps3_board.telnetterminal0.start_telnet=0
+        -C mps3_board.uart0.out_file="-"
+        -C mps3_board.uart0.unbuffered_output=1
+        -C mps3_board.uart0.shutdown_tag="EXITTHESIM")
+
 #############################################################################
 # Project
 #############################################################################
@@ -34,42 +42,34 @@
 
 project(ethos-u-corstone-300 VERSION 0.0.1)
 
+include(CTest)
+
 include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/helpers.cmake)
 
 #############################################################################
-# Configuration
-#############################################################################
-
-set(ETHOS_U_CORE_SOFTWARE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../core_software" CACHE PATH "Path to Arm Ethos-U Core Software")
-
-#############################################################################
-# Core software
-#############################################################################
-
-set(CORE_SOFTWARE_RTOS "FreeRTOS" CACHE STRING "")
-
-add_subdirectory(${ETHOS_U_CORE_SOFTWARE_PATH} core_software)
-
-#############################################################################
 # Corstone-300
 #############################################################################
 
-add_executable(ethosu_corstone_300)
+add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../common target)
 
-target_sources(ethosu_corstone_300 PRIVATE
-    main.cpp
+# Linker script
+ethosu_target_link_options(ethosu_target_link INTERFACE
+    LINK_FILE platform
+    ENTRY Reset_Handler)
+
+# Add drivers
+target_sources(ethosu_target_startup INTERFACE
     retarget.c
-    uart.c)
+    uart.c
+    target.cpp)
 
-target_link_libraries(ethosu_corstone_300 PRIVATE
-    ethosu_core
-    $<TARGET_OBJECTS:cmsis_startup>
-    cmsis_device)
+target_compile_definitions(ethosu_core_driver PUBLIC ETHOSU)
+target_link_libraries(ethosu_target_startup INTERFACE ethosu_core_driver)
 
-add_dependencies(ethosu_corstone_300 cmsis_startup)
+###############################################################################
+# Applications
+###############################################################################
 
-target_link_options(ethosu_corstone_300 PRIVATE
-    --entry Reset_Handler)
+# Add all applications
+add_subdirectory(../../applications applications)
 
-ethosu_link_options(ethosu_corstone_300 PRIVATE
-                    LINK_FILE platform)
diff --git a/targets/corstone-300/platform.scatter b/targets/corstone-300/platform.scatter
index e3037a7..2ab371f 100644
--- a/targets/corstone-300/platform.scatter
+++ b/targets/corstone-300/platform.scatter
@@ -26,22 +26,90 @@
 #define HEAP_SIZE 0x8000
 #endif
 
-APP_IMAGE 0x00000000 0x01000000
+#if defined(TRUSTZONE_BUILD) && !defined(ETHOSU_TEST)
+/*
+ * Include trustzone.h with common addresses and sizes.
+ * The build configuration sets whether TRUSTZONE_SECURE is set or
+ * TRUSTZONE_NONSECURE which sets the memory start addresses and sizes.
+ */
+
+#include "trustzone.h"
+#define USE_TRUSTZONE
+
+#else //TRUSTZONE_BUILD
+
+#define LR_START   0x10000000
+#define LR_SIZE    0x01000000
+
+#define ITCM_START 0x10000000
+#define ITCM_SIZE  0x00080000
+
+#define BRAM_START 0x11000000
+#define BRAM_SIZE  0x00200000
+
+#define DTCM_START 0x30000000
+#define DTCM_SIZE  0x00080000
+
+#define SRAM_START 0x31000000
+#define SRAM_SIZE  0x00200000
+
+#define DDR_START  0x70000000
+#define DDR_SIZE   0x02000000
+
+#define STACK_HEAP 0x30080000
+
+#endif //TRUSTZONE_BUILD
+
+/* ----------------------------------------------------------------------------
+  Stack seal size definition
+ *----------------------------------------------------------------------------*/
+#if defined(USE_TRUSTZONE) && defined(TRUSTZONE_SECURE)
+#define __STACKSEAL_SIZE   ( 8 )
+#else
+#define __STACKSEAL_SIZE   ( 0 )
+#endif
+
+APP_IMAGE LR_START LR_SIZE
 {
     ; ITCM 512kB
-    rom_exec 0x00000000 0x00080000
+    rom_exec ITCM_START ITCM_SIZE
     {
         *.o (RESET, +First)
         *(InRoot$$Sections)
+        ; Make sure reset_handler ends up in root segment, when split across
+        ; ITCM and DTCM
+        startup_ARMCM55.o
         .ANY (+RO)
     }
 
+#if defined(USE_TRUSTZONE) && defined(TRUSTZONE_SECURE)
+    ; MPS3 BRAM
     ; Shared between Cortex-M and the NPU
-    DATA_SRAM 0x01000000 UNINIT 0x00200000 {}
+    BRAM BRAM_START UNINIT (BRAM_SIZE - TZ_NSC_SIZE)
+    {
+    }
+
+    ROM_NSC TZ_NSC_START TZ_NSC_SIZE
+    {
+        *(Veneer$$CMSE)
+    }
+#else
+    ; MPS3 BRAM
+    BRAM BRAM_START UNINIT BRAM_SIZE
+    {
+    }
+#endif
+
+    ; DTCM 512kB
+    ; Only accessible from the Cortex-M
+    DTCM DTCM_START (DTCM_SIZE - STACK_SIZE - HEAP_SIZE - __STACKSEAL_SIZE)
+    {
+        .ANY1 (+RW +ZI)
+    }
 
     ; SSE-300 SRAM (3 cycles read latency) from M55/U55
     ; 2x2MB - only first part mapped
-    SRAM 0x21000000 UNINIT 0x00200000
+    SRAM SRAM_START UNINIT SRAM_SIZE
     {
 #ifndef ETHOSU_FAST_MEMORY_SIZE
         ; Place tensor arena in SRAM if we do not have a fast memory area
@@ -51,25 +119,29 @@
 #endif
     }
 
-    ; DTCM 512kB
-    ; Only accessible from the Cortex-M
-    DTCM 0x20000000 (0x00080000 - STACK_SIZE - HEAP_SIZE)
-    {
-        .ANY (+RW +ZI)
-    }
+    ARM_LIB_HEAP  (STACK_HEAP - STACK_SIZE - __STACKSEAL_SIZE - HEAP_SIZE) EMPTY ALIGN 8 HEAP_SIZE {}
+    ARM_LIB_STACK (STACK_HEAP - STACK_SIZE - __STACKSEAL_SIZE) EMPTY ALIGN 8 STACK_SIZE {}
 
-    ARM_LIB_HEAP  (0x20080000 - STACK_SIZE - HEAP_SIZE) EMPTY ALIGN 8 HEAP_SIZE {}
-    ARM_LIB_STACK (0x20080000 - STACK_SIZE) EMPTY ALIGN 8 STACK_SIZE {}
+#if defined(USE_TRUSTZONE) && defined(TRUSTZONE_SECURE)
+    STACKSEAL +0 EMPTY __STACKSEAL_SIZE {
+        ; Reserve empty region for stack seal immediately after stack
+    }
+#endif
 }
 
-LOAD_REGION_1 0x60000000 0x02000000
+LOAD_REGION_1 DDR_START DDR_SIZE
 {
     ; 2GB DDR4 available
-    rom_dram 0x60000000 0x02000000
+    rom_dram DDR_START
+#if defined(USE_TRUSTZONE) && defined(TRUSTZONE_NONSECURE)
+    {
+    }
+#else //trustzone secure or non-trustzone
     {
         * (network_model_sec)
         * (input_data_sec)
         * (expected_output_data_sec)
+        * (output_data_sec)
     }
 
 #ifdef ETHOSU_FAST_MEMORY_SIZE
@@ -79,4 +151,5 @@
         * (.bss.NoInit) ; Tensor Arena
     }
 #endif
+#endif
 }
diff --git a/targets/corstone-300/retarget.c b/targets/corstone-300/retarget.c
index 1598427..d53431f 100644
--- a/targets/corstone-300/retarget.c
+++ b/targets/corstone-300/retarget.c
@@ -183,7 +183,28 @@
 }
 
 void RETARGET(_exit)(int return_code) {
-    exit(return_code);
+    char exit_code_buffer[64] = {0};
+    const char *p = exit_code_buffer;
+
+    /* Print out the exit code on the uart so any reader know how we exit. */
+    /* By appending 0x04, ASCII for end-of-transmission the FVP model exits,
+     * if the configuration parameter shutdown_on_eot on the uart is enabled.
+     * For some versions of FVP, the shutdown_on_eot is broken, but the same
+     * behaviour can be created by passing specifying a shutdown_tag= for the
+     * uart when starting the model so that is added last as well.
+     */
+
+    snprintf(exit_code_buffer, sizeof(exit_code_buffer),
+            "Application exit code: %d.\n"  // Let the readers know how we exit
+            "\04\n"                         // end-of-transmission
+            "EXITTHESIM\n",                 // shutdown_tag
+            return_code);
+
+    while (*p != '\0') {
+        uart_putc(*p++);
+    }
+
+    while (1) {}
 }
 
 int system(const char *cmd) {
@@ -231,12 +252,6 @@
     return 0;
 }
 
-void exit(int code) {
-    uart_putc((char)0x4);
-    uart_putc((char)code);
-    while (1) {}
-}
-
 int fputc(int ch, FILE *f) {
     (void)(f);
     return uart_putc(ch);
diff --git a/targets/corstone-300/target.cpp b/targets/corstone-300/target.cpp
new file mode 100644
index 0000000..3d18680
--- /dev/null
+++ b/targets/corstone-300/target.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2020 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
+ *
+ * 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.
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "target.hpp"
+
+#ifdef ETHOSU
+#include <ethosu_driver.h>
+#endif
+
+#include "uart.h"
+
+#include <stdio.h>
+
+using namespace EthosU;
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define ETHOSU_BASE_ADDRESS 0x48102000
+
+#define ETHOSU_IRQ 56
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+#if defined(ETHOSU_FAST_MEMORY_SIZE) && ETHOSU_FAST_MEMORY_SIZE > 0
+__attribute__((aligned(16), section(".bss.ethosu_scratch"))) uint8_t ethosu_scratch[ETHOSU_FAST_MEMORY_SIZE];
+#else
+#define ethosu_scratch          0
+#define ETHOSU_FAST_MEMORY_SIZE 0
+#endif
+
+/****************************************************************************
+ * Cache maintenance
+ ****************************************************************************/
+
+#if defined(CPU_CACHE_ENABLE) && defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
+extern "C" {
+void ethosu_flush_dcache(uint32_t *p, size_t bytes) {
+    if (p)
+        SCB_CleanDCache_by_Addr(p, bytes);
+    else
+        SCB_CleanDCache();
+}
+
+void ethosu_invalidate_dcache(uint32_t *p, size_t bytes) {
+    if (p)
+        SCB_InvalidateDCache_by_Addr(p, bytes);
+    else
+        SCB_InvalidateDCache();
+}
+}
+#endif
+
+/****************************************************************************
+ * Init
+ ****************************************************************************/
+
+namespace {
+
+#ifdef ETHOSU
+void ethosuIrqHandler() {
+    ethosu_irq_handler();
+}
+#endif
+
+} // namespace
+
+namespace EthosU {
+
+void targetSetup() {
+    // Initialize UART driver
+    uart_init();
+
+#ifdef ETHOSU
+    // Initialize Ethos-U NPU driver
+    if (ethosu_init_v3(reinterpret_cast<void *>(ETHOSU_BASE_ADDRESS), ethosu_scratch, ETHOSU_FAST_MEMORY_SIZE, 1, 1)) {
+        printf("Failed to initialize NPU.\n");
+        return;
+    }
+
+    /* Assumes SCB->VTOR point to RW memory */
+    NVIC_SetVector(static_cast<IRQn_Type>(ETHOSU_IRQ), (uint32_t)&ethosuIrqHandler);
+    NVIC_EnableIRQ(static_cast<IRQn_Type>(ETHOSU_IRQ));
+#endif
+}
+
+} // namespace EthosU