This is an automated email from the ASF dual-hosted git repository.

mehrdadh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new a1dc4b91a4 [microTVM] Refactor required external functions in CRT to 
platform-template.c (#13885)
a1dc4b91a4 is described below

commit a1dc4b91a4752a0566ae349b5c68af277767b897
Author: Mehrdad Hessar <[email protected]>
AuthorDate: Mon Feb 13 12:56:07 2023 -0800

    [microTVM] Refactor required external functions in CRT to 
platform-template.c (#13885)
    
    This PR refactors all the necessary functions for a CRT project into 
platform-template.c file
    
    In addition, these templates files are also added along with 
crt_config-template.h (which was accidentally removed in #13812) to generated 
model library format (MLF). Here is the updated structure of a MLF file:
    
    .
    ├── codegen
    │   └── host
    ├── metadata.json
    ├── parameters
    │   └── default.params
    ├── runtime
    │   ├── include
    │   ├── Makefile
    │   └── src
    ├── src
    │   └── default.relay
    └── templates
        ├── crt_config-template.h
        ├── platform-template.c
---
 .../template_project/microtvm_api_server.py        |  10 +-
 .../src/example_project/{model.c => platform.c}    |  25 ++-
 .../src/example_project/{model.h => platform.h}    |   4 -
 .../src/example_project/project.ino                |   4 +-
 .../host_driven/{model_support.c => platform.c}    |  12 +-
 .../template_project/CMakeLists.txt.template       |   2 +-
 .../zephyr/template_project/microtvm_api_server.py |  67 +++++--
 .../src/aot_standalone_demo/main.c                 | 193 ++++++++-------------
 .../src/aot_standalone_demo/{main.c => platform.c} | 165 +++---------------
 .../src/aot_standalone_demo/zephyr_uart.c          |  87 ----------
 .../src/aot_standalone_demo/zephyr_uart.h          |  50 ------
 .../src/host_driven/fvp/semihost.c                 |   2 +-
 .../zephyr/template_project/src/host_driven/main.c | 154 +---------------
 .../template_project/src/host_driven/platform.c    | 155 +++++++++++++++++
 .../template_project/src/mlperftiny/platform.cc    |  68 ++++++++
 .../src/mlperftiny/submitter_implemented.cc        | 174 +++++++++++++++++--
 .../template_project/src/mlperftiny/tvmruntime.cc  | 164 -----------------
 .../template_project/src/mlperftiny/tvmruntime.h   |  62 -------
 .../template_project/src/mlperftiny/zephyr_uart.cc |  89 ----------
 .../template_project/src/mlperftiny/zephyr_uart.h  |  51 ------
 cmake/modules/CRT.cmake                            |   7 +-
 cmake/modules/Zephyr.cmake                         |   2 -
 .../how_to/work_with_microtvm/micro_mlperftiny.py  |   2 +-
 gallery/how_to/work_with_microtvm/micro_pytorch.py |   2 +-
 include/tvm/runtime/crt/platform.h                 |   9 +
 python/tvm/micro/model_library_format.py           |  14 +-
 python/tvm/testing/aot.py                          |   4 +-
 src/runtime/crt/host/Makefile.template             |   4 +-
 src/runtime/crt/host/main.cc                       |  78 +--------
 src/runtime/crt/host/microtvm_api_server.py        |  31 ++--
 src/runtime/crt/host/{main.cc => platform.cc}      | 124 ++++---------
 src/runtime/crt/platform-template.c                |  80 +++++++++
 tests/micro/arduino/test_arduino_workflow.py       |  14 +-
 tests/micro/arduino/testdata/project.ino           |   5 +-
 tests/micro/zephyr/test_zephyr.py                  |  36 ++++
 .../zephyr/test_zephyr_aot_exec_standalone.py      |   4 +-
 tests/micro/zephyr/utils.py                        |   8 +-
 .../unittest/test_micro_model_library_format.py    |  25 +++
 38 files changed, 818 insertions(+), 1169 deletions(-)

diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py 
b/apps/microtvm/arduino/template_project/microtvm_api_server.py
index 05c17ee194..f121238a67 100644
--- a/apps/microtvm/arduino/template_project/microtvm_api_server.py
+++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py
@@ -197,8 +197,8 @@ class Handler(server.ProjectAPIHandler):
                 metadata = json.load(f)
         return metadata
 
-    def _template_model_header(self, source_dir, metadata):
-        with open(source_dir / "model.h", "r") as f:
+    def _template_model(self, source_dir, metadata):
+        with open(source_dir / "platform.c", "r") as f:
             model_h_template = Template(f.read())
 
         all_module_names = []
@@ -218,7 +218,7 @@ class Handler(server.ProjectAPIHandler):
             "workspace_size_bytes": workspace_size_bytes,
         }
 
-        with open(source_dir / "model.h", "w") as f:
+        with open(source_dir / "platform.c", "w") as f:
             f.write(model_h_template.substitute(template_values))
 
     # Arduino ONLY recognizes .ino, .ccp, .c, .h
@@ -415,9 +415,9 @@ class Handler(server.ProjectAPIHandler):
         metadata = self._disassemble_mlf(model_library_format_path, source_dir)
         shutil.copy2(model_library_format_path, project_dir / 
MODEL_LIBRARY_FORMAT_RELPATH)
 
-        # For AOT, template model.h with metadata to minimize space usage
+        # For AOT, template platform.c with metadata to minimize space usage
         if project_type == "example_project":
-            self._template_model_header(source_dir, metadata)
+            self._template_model(source_dir, metadata)
 
         self._change_cpp_file_extensions(source_dir)
 
diff --git a/apps/microtvm/arduino/template_project/src/example_project/model.c 
b/apps/microtvm/arduino/template_project/src/example_project/platform.c
similarity index 80%
rename from apps/microtvm/arduino/template_project/src/example_project/model.c
rename to apps/microtvm/arduino/template_project/src/example_project/platform.c
index 46f43752ef..973b8aa18c 100644
--- a/apps/microtvm/arduino/template_project/src/example_project/model.c
+++ b/apps/microtvm/arduino/template_project/src/example_project/platform.c
@@ -17,17 +17,22 @@
  * under the License.
  */
 
-#include "model.h"
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
 
 #include "Arduino.h"
 #include "standalone_crt/include/dlpack/dlpack.h"
 #include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h"
 
+#define TVM_WORKSPACE_SIZE_BYTES $workspace_size_bytes
+
 // AOT memory array, stack allocator wants it aligned
-static uint8_t g_aot_memory[WORKSPACE_SIZE]
+static uint8_t g_aot_memory[TVM_WORKSPACE_SIZE_BYTES]
     __attribute__((aligned(TVM_RUNTIME_ALLOC_ALIGNMENT_BYTES)));
 tvm_workspace_t app_workspace;
 
+// Called when an internal error occurs and execution cannot continue.
 // Blink code for debugging purposes
 void TVMPlatformAbort(tvm_crt_error_t error) {
   TVMLogf("TVMPlatformAbort: 0x%08x\n", error);
@@ -45,19 +50,23 @@ void TVMPlatformAbort(tvm_crt_error_t error) {
   }
 }
 
-void TVMLogf(const char* msg, ...) {}
-
+// Allocate memory for use by TVM.
 tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
   return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr);
 }
 
+// Free memory used by TVM.
 tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
   return StackMemoryManager_Free(&app_workspace, ptr);
 }
 
+// Internal logging API call implementation.
+void TVMLogf(const char* msg, ...) {}
+
 unsigned long g_utvm_start_time_micros;
 int g_utvm_timer_running = 0;
 
+// Start a device timer.
 tvm_crt_error_t TVMPlatformTimerStart() {
   if (g_utvm_timer_running) {
     return kTvmErrorPlatformTimerBadState;
@@ -67,6 +76,7 @@ tvm_crt_error_t TVMPlatformTimerStart() {
   return kTvmErrorNoError;
 }
 
+// Stop the running device timer and get the elapsed time (in microseconds).
 tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
   if (!g_utvm_timer_running) {
     return kTvmErrorPlatformTimerBadState;
@@ -77,6 +87,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* 
elapsed_time_seconds) {
   return kTvmErrorNoError;
 }
 
+// Fill a buffer with random data.
 tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
   for (size_t i = 0; i < num_bytes; i++) {
     buffer[i] = rand();
@@ -84,7 +95,11 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, 
size_t num_bytes) {
   return kTvmErrorNoError;
 }
 
-void TVMInitialize() { StackMemoryManager_Init(&app_workspace, g_aot_memory, 
WORKSPACE_SIZE); }
+// Initialize TVM inference.
+tvm_crt_error_t TVMPlatformInitialize() {
+  StackMemoryManager_Init(&app_workspace, g_aot_memory, sizeof(g_aot_memory));
+  return kTvmErrorNoError;
+}
 
 void TVMExecute(void* input_data, void* output_data) {
   int ret_val = tvmgen_default___tvm_main__(input_data, output_data);
diff --git a/apps/microtvm/arduino/template_project/src/example_project/model.h 
b/apps/microtvm/arduino/template_project/src/example_project/platform.h
similarity index 94%
rename from apps/microtvm/arduino/template_project/src/example_project/model.h
rename to apps/microtvm/arduino/template_project/src/example_project/platform.h
index 7381c97e9b..d6f10e13e9 100644
--- a/apps/microtvm/arduino/template_project/src/example_project/model.h
+++ b/apps/microtvm/arduino/template_project/src/example_project/platform.h
@@ -17,14 +17,10 @@
  * under the License.
  */
 
-#define WORKSPACE_SIZE $workspace_size_bytes
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-void TVMInitialize();
-
 /* TODO template this function signature with the input and output
  * data types and sizes. For example:
  *
diff --git 
a/apps/microtvm/arduino/template_project/src/example_project/project.ino 
b/apps/microtvm/arduino/template_project/src/example_project/project.ino
index 5f5683161e..666396b407 100644
--- a/apps/microtvm/arduino/template_project/src/example_project/project.ino
+++ b/apps/microtvm/arduino/template_project/src/example_project/project.ino
@@ -17,10 +17,10 @@
  * under the License.
  */
 
-#include "src/model.h"
+#include "src/standalone_crt/include/tvm/runtime/crt/platform.h"
 
 void setup() {
-  TVMInitialize();
+  TVMPlatformInitialize();
   // If desired, initialize the RNG with random noise
   // randomSeed(analogRead(0));
 }
diff --git 
a/apps/microtvm/arduino/template_project/src/host_driven/model_support.c 
b/apps/microtvm/arduino/template_project/src/host_driven/platform.c
similarity index 85%
rename from 
apps/microtvm/arduino/template_project/src/host_driven/model_support.c
rename to apps/microtvm/arduino/template_project/src/host_driven/platform.c
index bcc9a109ca..0a276134d4 100644
--- a/apps/microtvm/arduino/template_project/src/host_driven/model_support.c
+++ b/apps/microtvm/arduino/template_project/src/host_driven/platform.c
@@ -17,22 +17,28 @@
  * under the License.
  */
 
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
+
 #include "standalone_crt/include/dlpack/dlpack.h"
 #include "standalone_crt/include/tvm/runtime/crt/error_codes.h"
 #include "stdarg.h"
 
-// Blink code for debugging purposes
+// Called when an internal error occurs and execution cannot continue.
 void TVMPlatformAbort(tvm_crt_error_t error) {
   TVMLogf("TVMPlatformAbort: 0x%08x\n", error);
   for (;;)
     ;
 }
 
+// Called by the microTVM RPC server to implement TVMLogf.
 size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
                                 va_list args) {
   return vsnprintf(out_buf, out_buf_size_bytes, fmt, args);
 }
 
+// Allocate memory for use by TVM.
 tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
   if (num_bytes == 0) {
     num_bytes = sizeof(int);
@@ -41,6 +47,7 @@ tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, 
DLDevice dev, void**
   return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError;
 }
 
+// Free memory used by TVM.
 tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
   free(ptr);
   return kTvmErrorNoError;
@@ -49,6 +56,7 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice 
dev) {
 unsigned long g_utvm_start_time_micros;
 int g_utvm_timer_running = 0;
 
+// Start a device timer.
 tvm_crt_error_t TVMPlatformTimerStart() {
   if (g_utvm_timer_running) {
     return kTvmErrorPlatformTimerBadState;
@@ -58,6 +66,7 @@ tvm_crt_error_t TVMPlatformTimerStart() {
   return kTvmErrorNoError;
 }
 
+// Stop the running device timer and get the elapsed time (in microseconds).
 tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
   if (!g_utvm_timer_running) {
     return kTvmErrorPlatformTimerBadState;
@@ -68,6 +77,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* 
elapsed_time_seconds) {
   return kTvmErrorNoError;
 }
 
+// Fill a buffer with random data.
 tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
   for (size_t i = 0; i < num_bytes; i++) {
     buffer[i] = rand();
diff --git a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template 
b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template
index 1aff9ece6b..cec29e7248 100644
--- a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template
+++ b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template
@@ -83,4 +83,4 @@ endif()
 
 file(GLOB_RECURSE app_srcs src/**.c src/**.cc)
 target_sources(app PRIVATE ${app_srcs} ${cmsis_lib_srcs})
-target_include_directories(app PRIVATE crt_config ${CMAKE_SOURCE_DIR}/include 
crt/include ${cmsis_includes})
+target_include_directories(app PRIVATE crt_config include 
${CMAKE_SOURCE_DIR}/include crt/include ${cmsis_includes})
diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py 
b/apps/microtvm/zephyr/template_project/microtvm_api_server.py
index 17dd991229..227b038944 100644
--- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py
+++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py
@@ -210,14 +210,14 @@ def _get_board_mem_size_bytes(zephyr_base: str, board: 
str):
     return None
 
 
-DEFAULT_HEAP_SIZE_BYTES = 216 * 1024
+DEFAULT_WORKSPACE_SIZE_BYTES = 216 * 1024
 
 
 def _get_recommended_heap_size_bytes(board: str):
     prop = BOARD_PROPERTIES[board]
     if "recommended_heap_size_bytes" in prop:
         return prop["recommended_heap_size_bytes"]
-    return DEFAULT_HEAP_SIZE_BYTES
+    return DEFAULT_WORKSPACE_SIZE_BYTES
 
 
 def generic_find_serial_port(serial_number: str = None):
@@ -358,11 +358,11 @@ PROJECT_OPTIONS = server.default_project_options(
         help="Run on the FVP emulator instead of hardware.",
     ),
     server.ProjectOption(
-        "heap_size_bytes",
+        "workspace_size_bytes",
         optional=["generate_project"],
         type="int",
         default=None,
-        help="Sets the value for HEAP_SIZE_BYTES passed to K_HEAP_DEFINE() to 
service TVM memory allocation requests.",
+        help="Sets the value for TVM_WORKSPACE_SIZE_BYTES passed to 
K_HEAP_DEFINE() to service TVM memory allocation requests.",
     ),
 ]
 
@@ -403,7 +403,13 @@ class Handler(server.ProjectAPIHandler):
     }
 
     def _create_prj_conf(
-        self, project_dir: pathlib.Path, board: str, project_type: str, 
config_main_stack_size
+        self,
+        project_dir: pathlib.Path,
+        board: str,
+        project_type: str,
+        config_main_stack_size: int,
+        config_led: bool,
+        use_fvp: bool,
     ):
         with open(project_dir / "prj.conf", "w") as f:
             f.write(
@@ -413,6 +419,13 @@ class Handler(server.ProjectAPIHandler):
                 "CONFIG_UART_INTERRUPT_DRIVEN=y\n"
                 "\n"
             )
+            if (
+                config_led
+                and not self._is_qemu(board, use_fvp)
+                and not self._is_fvp(board, use_fvp)
+            ):
+                f.write("# For debugging.\n" "CONFIG_LED=y\n" "\n")
+
             f.write("# For TVMPlatformAbort().\n" "CONFIG_REBOOT=y\n" "\n")
 
             if project_type == "host_driven":
@@ -522,6 +535,18 @@ class Handler(server.ProjectAPIHandler):
 
         return cmake_args
 
+    def _copy_src_and_header_files(self, src_dir: pathlib.Path, dst_dir: 
pathlib.Path):
+        """Copy content of src_dir from template project to dst_dir in separate
+        source and header sub-directories.
+        """
+        for file in os.listdir(src_dir):
+            file = src_dir / file
+            if file.is_file():
+                if file.suffix in [".cc", ".c"]:
+                    shutil.copy2(file, dst_dir / "src")
+                elif file.suffix in [".h"]:
+                    shutil.copy2(file, dst_dir / "include" / "tvm")
+
     def generate_project(self, model_library_format_path, standalone_crt_dir, 
project_dir, options):
         zephyr_board = options["board"]
         project_type = options["project_type"]
@@ -533,7 +558,7 @@ class Handler(server.ProjectAPIHandler):
         verbose = options.get("verbose")
 
         recommended_heap_size = _get_recommended_heap_size_bytes(zephyr_board)
-        heap_size_bytes = options.get("heap_size_bytes") or 
recommended_heap_size
+        workspace_size_bytes = options.get("workspace_size_bytes") or 
recommended_heap_size
         board_mem_size = _get_board_mem_size_bytes(zephyr_base, zephyr_board)
 
         compile_definitions = options.get("compile_definitions")
@@ -602,7 +627,7 @@ class Handler(server.ProjectAPIHandler):
             else:
                 shutil.copy2(src_path, dst_path)
 
-        # Populate Makefile.
+        # Populate CMakeLists.
         with open(project_dir / CMAKELIST_FILENAME, "w") as cmake_f:
             with open(API_SERVER_DIR / f"{CMAKELIST_FILENAME}.template", "r") 
as cmake_template_f:
                 for line in cmake_template_f:
@@ -629,10 +654,10 @@ class Handler(server.ProjectAPIHandler):
 
                 if board_mem_size is not None:
                     assert (
-                        heap_size_bytes < board_mem_size
-                    ), f"Heap size {heap_size_bytes} is larger than memory 
size {board_mem_size} on this board."
+                        workspace_size_bytes < board_mem_size
+                    ), f"Workspace size {workspace_size_bytes} is larger than 
memory size {board_mem_size} on this board."
                 cmake_f.write(
-                    f"target_compile_definitions(app PUBLIC 
-DHEAP_SIZE_BYTES={heap_size_bytes})\n"
+                    f"target_compile_definitions(app PUBLIC 
-DTVM_WORKSPACE_SIZE_BYTES={workspace_size_bytes})\n"
                 )
 
                 if compile_definitions:
@@ -649,7 +674,9 @@ class Handler(server.ProjectAPIHandler):
                 if self._is_fvp(zephyr_board, use_fvp):
                     cmake_f.write(f"target_compile_definitions(app PUBLIC 
-DFVP=1)\n")
 
-        self._create_prj_conf(project_dir, zephyr_board, project_type, 
config_main_stack_size)
+        self._create_prj_conf(
+            project_dir, zephyr_board, project_type, config_main_stack_size, 
verbose, use_fvp
+        )
 
         # Populate crt-config.h
         crt_config_dir = project_dir / "crt_config"
@@ -658,13 +685,19 @@ class Handler(server.ProjectAPIHandler):
             API_SERVER_DIR / "crt_config" / "crt_config.h", crt_config_dir / 
"crt_config.h"
         )
 
-        # Populate src/
+        # Populate `src` and `include`
         src_dir = project_dir / "src"
-        if project_type != "host_driven" or self._is_fvp(zephyr_board, 
use_fvp):
-            shutil.copytree(API_SERVER_DIR / "src" / project_type, src_dir)
-        else:
-            src_dir.mkdir()
-            shutil.copy2(API_SERVER_DIR / "src" / project_type / "main.c", 
src_dir)
+        src_dir.mkdir()
+        include_dir = project_dir / "include" / "tvm"
+        include_dir.mkdir(parents=True)
+        src_project_type_dir = API_SERVER_DIR / "src" / project_type
+        self._copy_src_and_header_files(src_project_type_dir, project_dir)
+
+        if self._is_fvp(zephyr_board, use_fvp):
+            self._copy_src_and_header_files(src_project_type_dir / "fvp", 
project_dir)
+
+        if project_type == "mlperftiny":
+            shutil.copytree(src_project_type_dir / "api", src_dir / "api")
 
         # Populate extra_files
         if extra_files_tar:
diff --git 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c 
b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c
index 9ba521ae17..fff8f57875 100644
--- a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c
+++ b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c
@@ -25,23 +25,18 @@
 #include <tvm/runtime/crt/logging.h>
 #include <tvm/runtime/crt/stack_allocator.h>
 #include <unistd.h>
+#include <zephyr/drivers/uart.h>
 #include <zephyr/kernel.h>
-#include <zephyr/sys/reboot.h>
+#include <zephyr/sys/ring_buffer.h>
 
-#include "input_data.h"
-#include "output_data.h"
+#include "tvm/input_data.h"
+#include "tvm/output_data.h"
 #include "tvmgen_default.h"
-#include "zephyr_uart.h"
 
 #ifdef CONFIG_ARCH_POSIX
 #include "posix_board_if.h"
 #endif
 
-// WORKSPACE_SIZE defined in Project API Makefile
-
-static uint8_t g_aot_memory[WORKSPACE_SIZE];
-tvm_workspace_t app_workspace;
-
 // Transport Commands.
 // Commands on host end with `\n`
 // Commands on microTVM device end with `%`
@@ -53,126 +48,84 @@ const unsigned char CMD_INFER[] = "infer";
 #define CMD_SIZE 80u
 #define CMD_TERMINATOR '%'
 
-size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
-                                va_list args) {
-  return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
-}
+static uint8_t main_rx_buf[128];
+static uint8_t g_cmd_buf[128];
+static size_t g_cmd_buf_ind;
 
-void TVMLogf(const char* msg, ...) {
-  char buffer[256];
-  int size;
-  va_list args;
-  va_start(args, msg);
-  size = vsprintf(buffer, msg, args);
-  va_end(args);
-  TVMPlatformWriteSerial(buffer, (uint32_t)size);
-}
+static const struct device* g_microtvm_uart;
+#define RING_BUF_SIZE_BYTES (TVM_CRT_MAX_PACKET_SIZE_BYTES + 100)
 
-void TVMPlatformAbort(tvm_crt_error_t error) {
-  TVMLogf("TVMPlatformAbort: %08x\n", error);
-  sys_reboot(SYS_REBOOT_COLD);
-  for (;;)
-    ;
-}
+// Ring buffer used to store data read from the UART on rx interrupt.
+RING_BUF_DECLARE(uart_rx_rbuf, RING_BUF_SIZE_BYTES);
 
-tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
-  return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr);
+uint32_t UartTxWrite(const char* data, uint32_t size) {
+  for (uint32_t i = 0; i < size; i++) {
+    uart_poll_out(g_microtvm_uart, data[i]);
+  }
+  return size;
 }
 
-tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
-  return StackMemoryManager_Free(&app_workspace, ptr);
+uint32_t UartRxRead(uint8_t* data, uint32_t data_size_bytes) {
+  unsigned int key = irq_lock();
+  uint32_t bytes_read = ring_buf_get(&uart_rx_rbuf, data, data_size_bytes);
+  irq_unlock(key);
+  return bytes_read;
 }
 
-void timer_expiry_function(struct k_timer* timer_id) { return; }
-
-#define MILLIS_TIL_EXPIRY 200
-#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY))
-struct k_timer g_microtvm_timer;
-uint32_t g_microtvm_start_time;
-int g_microtvm_timer_running = 0;
-
-// Called to start system timer.
-tvm_crt_error_t TVMPlatformTimerStart() {
-  if (g_microtvm_timer_running) {
-    TVMLogf("timer already running");
-    return kTvmErrorPlatformTimerBadState;
-  }
-
-  k_timer_start(&g_microtvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY);
-  g_microtvm_start_time = k_cycle_get_32();
-  g_microtvm_timer_running = 1;
-  return kTvmErrorNoError;
+// Initialize UART
+void UartInit() {
+  // Claim console device.
+  g_microtvm_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+  const struct uart_config config = {.baudrate = 115200,
+                                     .parity = UART_CFG_PARITY_NONE,
+                                     .stop_bits = UART_CFG_STOP_BITS_1,
+                                     .data_bits = UART_CFG_DATA_BITS_8,
+                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};
+  uart_configure(g_microtvm_uart, &config);
+  uart_rx_init(&uart_rx_rbuf, g_microtvm_uart);
 }
 
-// Called to stop system timer.
-tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
-  if (!g_microtvm_timer_running) {
-    TVMLogf("timer not running");
-    return kTvmErrorSystemErrorMask | 2;
-  }
-
-  uint32_t stop_time = k_cycle_get_32();
-
-  // compute how long the work took
-  uint32_t cycles_spent = stop_time - g_microtvm_start_time;
-  if (stop_time < g_microtvm_start_time) {
-    // we rolled over *at least* once, so correct the rollover it was *only*
-    // once, because we might still use this result
-    cycles_spent = ~((uint32_t)0) - (g_microtvm_start_time - stop_time);
-  }
-
-  uint32_t ns_spent = (uint32_t)k_cyc_to_ns_floor64(cycles_spent);
-  double hw_clock_res_us = ns_spent / 1000.0;
-
-  // need to grab time remaining *before* stopping. when stopped, this function
-  // always returns 0.
-  int32_t time_remaining_ms = k_timer_remaining_get(&g_microtvm_timer);
-  k_timer_stop(&g_microtvm_timer);
-  // check *after* stopping to prevent extra expiries on the happy path
-  if (time_remaining_ms < 0) {
-    return kTvmErrorSystemErrorMask | 3;
-  }
-  uint32_t num_expiries = k_timer_status_get(&g_microtvm_timer);
-  uint32_t timer_res_ms = ((num_expiries * MILLIS_TIL_EXPIRY) + 
time_remaining_ms);
-  double approx_num_cycles =
-      (double)k_ticks_to_cyc_floor32(1) * 
(double)k_ms_to_ticks_ceil32(timer_res_ms);
-  // if we approach the limits of the HW clock datatype (uint32_t), use the
-  // coarse-grained timer result instead
-  if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) {
-    *elapsed_time_seconds = timer_res_ms / 1000.0;
-  } else {
-    *elapsed_time_seconds = hw_clock_res_us / 1e6;
+static uint8_t uart_data[8];
+// UART interrupt callback.
+void uart_irq_cb(const struct device* dev, void* user_data) {
+  while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
+    struct ring_buf* rbuf = (struct ring_buf*)user_data;
+    if (uart_irq_rx_ready(dev) != 0) {
+      for (;;) {
+        // Read a small chunk of data from the UART.
+        int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data));
+        if (bytes_read < 0) {
+          TVMPlatformAbort((tvm_crt_error_t)(0xbeef1));
+        } else if (bytes_read == 0) {
+          break;
+        }
+        // Write it into the ring buffer.
+        int bytes_written = ring_buf_put(rbuf, uart_data, bytes_read);
+        if (bytes_read != bytes_written) {
+          TVMPlatformAbort((tvm_crt_error_t)(0xbeef2));
+        }
+      }
+    }
   }
-
-  g_microtvm_timer_running = 0;
-  return kTvmErrorNoError;
 }
 
-void* TVMBackendAllocWorkspace(int device_type, int device_id, uint64_t 
nbytes, int dtype_code_hint,
-                               int dtype_bits_hint) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  void* ptr = 0;
-  DLDevice dev = {device_type, device_id};
-  assert(nbytes > 0);
-  err = TVMPlatformMemoryAllocate(nbytes, dev, &ptr);
-  CHECK_EQ(err, kTvmErrorNoError,
-           "TVMBackendAllocWorkspace(%d, %d, %" PRIu64 ", %d, %d) -> %" 
PRId32, device_type,
-           device_id, nbytes, dtype_code_hint, dtype_bits_hint, err);
-  return ptr;
+// Used to initialize the UART receiver.
+void uart_rx_init(struct ring_buf* rbuf, const struct device* dev) {
+  uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)rbuf);
+  uart_irq_rx_enable(dev);
 }
 
-int TVMBackendFreeWorkspace(int device_type, int device_id, void* ptr) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  DLDevice dev = {device_type, device_id};
-  err = TVMPlatformMemoryFree(ptr, dev);
-  return err;
+void TVMLogf(const char* msg, ...) {
+  char buffer[256];
+  int size;
+  va_list args;
+  va_start(args, msg);
+  size = vsprintf(buffer, msg, args);
+  va_end(args);
+  UartTxWrite(buffer, (uint32_t)size);
 }
 
-static uint8_t main_rx_buf[128];
-static uint8_t g_cmd_buf[128];
-static size_t g_cmd_buf_ind;
-
-void TVMInfer() {
+void Infer() {
   struct tvmgen_default_inputs inputs = {
       .input_1 = input_data,
   };
@@ -180,8 +133,6 @@ void TVMInfer() {
       .Identity = output_data,
   };
 
-  StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE);
-
   double elapsed_time = 0;
   TVMPlatformTimerStart();
   int ret_val = tvmgen_default_run(&inputs, &outputs);
@@ -206,11 +157,11 @@ void TVMInfer() {
 // Execute functions based on received command
 void command_ready(char* command) {
   if (strncmp(command, CMD_INIT, CMD_SIZE) == 0) {
-    TVMPlatformWriteSerial(CMD_WAKEUP, sizeof(CMD_WAKEUP));
+    UartTxWrite(CMD_WAKEUP, sizeof(CMD_WAKEUP));
   } else if (strncmp(command, CMD_INFER, CMD_SIZE) == 0) {
-    TVMInfer();
+    Infer();
   } else {
-    TVMPlatformWriteSerial(CMD_READY, sizeof(CMD_READY));
+    UartTxWrite(CMD_READY, sizeof(CMD_READY));
   }
 }
 
@@ -229,13 +180,13 @@ void serial_callback(char* message, int len_bytes) {
 }
 
 void main(void) {
+  TVMPlatformInitialize();
+  UartInit();
   g_cmd_buf_ind = 0;
   memset((char*)g_cmd_buf, 0, sizeof(g_cmd_buf));
-  TVMPlatformUARTInit();
-  k_timer_init(&g_microtvm_timer, NULL, NULL);
 
   while (true) {
-    int bytes_read = TVMPlatformUartRxRead(main_rx_buf, sizeof(main_rx_buf));
+    int bytes_read = UartRxRead(main_rx_buf, sizeof(main_rx_buf));
     if (bytes_read > 0) {
       serial_callback(main_rx_buf, bytes_read);
     }
diff --git 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c 
b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/platform.c
similarity index 50%
copy from apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c
copy to apps/microtvm/zephyr/template_project/src/aot_standalone_demo/platform.c
index 9ba521ae17..c66dad5711 100644
--- a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/main.c
+++ b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/platform.c
@@ -17,57 +17,38 @@
  * under the License.
  */
 
-#include <assert.h>
-#include <float.h>
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
 #include <stdio.h>
-#include <string.h>
-#include <tvm/runtime/c_runtime_api.h>
-#include <tvm/runtime/crt/logging.h>
+#include <stdlib.h>
+#include <tvm/runtime/crt/error_codes.h>
 #include <tvm/runtime/crt/stack_allocator.h>
-#include <unistd.h>
 #include <zephyr/kernel.h>
 #include <zephyr/sys/reboot.h>
 
-#include "input_data.h"
-#include "output_data.h"
+#include "crt_config.h"
+#include "dlpack/dlpack.h"
 #include "tvmgen_default.h"
-#include "zephyr_uart.h"
-
-#ifdef CONFIG_ARCH_POSIX
-#include "posix_board_if.h"
-#endif
 
-// WORKSPACE_SIZE defined in Project API Makefile
-
-static uint8_t g_aot_memory[WORKSPACE_SIZE];
+// TVM_WORKSPACE_SIZE_BYTES defined in Project API Makefile
+static uint8_t g_aot_memory[TVM_WORKSPACE_SIZE_BYTES];
 tvm_workspace_t app_workspace;
 
-// Transport Commands.
-// Commands on host end with `\n`
-// Commands on microTVM device end with `%`
-const unsigned char CMD_WAKEUP[] = "wakeup\n";
-const unsigned char CMD_READY[] = "ready\n";
-const unsigned char CMD_INIT[] = "init";
-const unsigned char CMD_INFER[] = "infer";
-
-#define CMD_SIZE 80u
-#define CMD_TERMINATOR '%'
+#define MILLIS_TIL_EXPIRY 200
+#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY))
+struct k_timer g_microtvm_timer;
+uint32_t g_microtvm_start_time;
+int g_microtvm_timer_running = 0;
 
 size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
                                 va_list args) {
   return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
 }
 
-void TVMLogf(const char* msg, ...) {
-  char buffer[256];
-  int size;
-  va_list args;
-  va_start(args, msg);
-  size = vsprintf(buffer, msg, args);
-  va_end(args);
-  TVMPlatformWriteSerial(buffer, (uint32_t)size);
-}
-
 void TVMPlatformAbort(tvm_crt_error_t error) {
   TVMLogf("TVMPlatformAbort: %08x\n", error);
   sys_reboot(SYS_REBOOT_COLD);
@@ -83,15 +64,12 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice 
dev) {
   return StackMemoryManager_Free(&app_workspace, ptr);
 }
 
-void timer_expiry_function(struct k_timer* timer_id) { return; }
-
-#define MILLIS_TIL_EXPIRY 200
-#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY))
-struct k_timer g_microtvm_timer;
-uint32_t g_microtvm_start_time;
-int g_microtvm_timer_running = 0;
+tvm_crt_error_t TVMPlatformInitialize() {
+  k_timer_init(&g_microtvm_timer, NULL, NULL);
+  StackMemoryManager_Init(&app_workspace, g_aot_memory, sizeof(g_aot_memory));
+  return kTvmErrorNoError;
+}
 
-// Called to start system timer.
 tvm_crt_error_t TVMPlatformTimerStart() {
   if (g_microtvm_timer_running) {
     TVMLogf("timer already running");
@@ -104,7 +82,6 @@ tvm_crt_error_t TVMPlatformTimerStart() {
   return kTvmErrorNoError;
 }
 
-// Called to stop system timer.
 tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
   if (!g_microtvm_timer_running) {
     TVMLogf("timer not running");
@@ -147,101 +124,3 @@ tvm_crt_error_t TVMPlatformTimerStop(double* 
elapsed_time_seconds) {
   g_microtvm_timer_running = 0;
   return kTvmErrorNoError;
 }
-
-void* TVMBackendAllocWorkspace(int device_type, int device_id, uint64_t 
nbytes, int dtype_code_hint,
-                               int dtype_bits_hint) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  void* ptr = 0;
-  DLDevice dev = {device_type, device_id};
-  assert(nbytes > 0);
-  err = TVMPlatformMemoryAllocate(nbytes, dev, &ptr);
-  CHECK_EQ(err, kTvmErrorNoError,
-           "TVMBackendAllocWorkspace(%d, %d, %" PRIu64 ", %d, %d) -> %" 
PRId32, device_type,
-           device_id, nbytes, dtype_code_hint, dtype_bits_hint, err);
-  return ptr;
-}
-
-int TVMBackendFreeWorkspace(int device_type, int device_id, void* ptr) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  DLDevice dev = {device_type, device_id};
-  err = TVMPlatformMemoryFree(ptr, dev);
-  return err;
-}
-
-static uint8_t main_rx_buf[128];
-static uint8_t g_cmd_buf[128];
-static size_t g_cmd_buf_ind;
-
-void TVMInfer() {
-  struct tvmgen_default_inputs inputs = {
-      .input_1 = input_data,
-  };
-  struct tvmgen_default_outputs outputs = {
-      .Identity = output_data,
-  };
-
-  StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE);
-
-  double elapsed_time = 0;
-  TVMPlatformTimerStart();
-  int ret_val = tvmgen_default_run(&inputs, &outputs);
-  TVMPlatformTimerStop(&elapsed_time);
-
-  if (ret_val != 0) {
-    TVMLogf("Error: %d\n", ret_val);
-    TVMPlatformAbort(kTvmErrorPlatformCheckFailure);
-  }
-
-  size_t max_ind = -1;
-  float max_val = -FLT_MAX;
-  for (size_t i = 0; i < output_data_len; i++) {
-    if (output_data[i] >= max_val) {
-      max_ind = i;
-      max_val = output_data[i];
-    }
-  }
-  TVMLogf("result:%d:%d\n", max_ind, (uint32_t)(elapsed_time * 1000));
-}
-
-// Execute functions based on received command
-void command_ready(char* command) {
-  if (strncmp(command, CMD_INIT, CMD_SIZE) == 0) {
-    TVMPlatformWriteSerial(CMD_WAKEUP, sizeof(CMD_WAKEUP));
-  } else if (strncmp(command, CMD_INFER, CMD_SIZE) == 0) {
-    TVMInfer();
-  } else {
-    TVMPlatformWriteSerial(CMD_READY, sizeof(CMD_READY));
-  }
-}
-
-// Append received characters to buffer and check for termination character.
-void serial_callback(char* message, int len_bytes) {
-  for (int i = 0; i < len_bytes; i++) {
-    if (message[i] == CMD_TERMINATOR) {
-      g_cmd_buf[g_cmd_buf_ind] = (char)0;
-      command_ready(g_cmd_buf);
-      g_cmd_buf_ind = 0;
-    } else {
-      g_cmd_buf[g_cmd_buf_ind] = message[i];
-      g_cmd_buf_ind += 1;
-    }
-  }
-}
-
-void main(void) {
-  g_cmd_buf_ind = 0;
-  memset((char*)g_cmd_buf, 0, sizeof(g_cmd_buf));
-  TVMPlatformUARTInit();
-  k_timer_init(&g_microtvm_timer, NULL, NULL);
-
-  while (true) {
-    int bytes_read = TVMPlatformUartRxRead(main_rx_buf, sizeof(main_rx_buf));
-    if (bytes_read > 0) {
-      serial_callback(main_rx_buf, bytes_read);
-    }
-  }
-
-#ifdef CONFIG_ARCH_POSIX
-  posix_exit(0);
-#endif
-}
diff --git 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.c 
b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.c
deleted file mode 100644
index 8d5f912081..0000000000
--- 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-#include "zephyr_uart.h"
-
-#include <zephyr/drivers/uart.h>
-#include <zephyr/sys/ring_buffer.h>
-
-#include "crt_config.h"
-
-static const struct device* g_microtvm_uart;
-#define RING_BUF_SIZE_BYTES (TVM_CRT_MAX_PACKET_SIZE_BYTES + 100)
-
-// Ring buffer used to store data read from the UART on rx interrupt.
-RING_BUF_DECLARE(uart_rx_rbuf, RING_BUF_SIZE_BYTES);
-
-static uint8_t uart_data[8];
-// UART interrupt callback.
-void uart_irq_cb(const struct device* dev, void* user_data) {
-  while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
-    struct ring_buf* rbuf = (struct ring_buf*)user_data;
-    if (uart_irq_rx_ready(dev) != 0) {
-      for (;;) {
-        // Read a small chunk of data from the UART.
-        int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data));
-        if (bytes_read < 0) {
-          TVMPlatformAbort((tvm_crt_error_t)(0xbeef1));
-        } else if (bytes_read == 0) {
-          break;
-        }
-        // Write it into the ring buffer.
-        int bytes_written = ring_buf_put(rbuf, uart_data, bytes_read);
-        if (bytes_read != bytes_written) {
-          TVMPlatformAbort((tvm_crt_error_t)(0xbeef2));
-        }
-      }
-    }
-  }
-}
-
-// Used to initialize the UART receiver.
-void uart_rx_init(struct ring_buf* rbuf, const struct device* dev) {
-  uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)rbuf);
-  uart_irq_rx_enable(dev);
-}
-
-uint32_t TVMPlatformUartRxRead(uint8_t* data, uint32_t data_size_bytes) {
-  unsigned int key = irq_lock();
-  uint32_t bytes_read = ring_buf_get(&uart_rx_rbuf, data, data_size_bytes);
-  irq_unlock(key);
-  return bytes_read;
-}
-
-uint32_t TVMPlatformWriteSerial(const char* data, uint32_t size) {
-  for (uint32_t i = 0; i < size; i++) {
-    uart_poll_out(g_microtvm_uart, data[i]);
-  }
-  return size;
-}
-
-// Initialize UART
-void TVMPlatformUARTInit() {
-  // Claim console device.
-  g_microtvm_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
-  const struct uart_config config = {.baudrate = 115200,
-                                     .parity = UART_CFG_PARITY_NONE,
-                                     .stop_bits = UART_CFG_STOP_BITS_1,
-                                     .data_bits = UART_CFG_DATA_BITS_8,
-                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};
-  uart_configure(g_microtvm_uart, &config);
-  uart_rx_init(&uart_rx_rbuf, g_microtvm_uart);
-}
diff --git 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.h 
b/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.h
deleted file mode 100644
index 771cb490d0..0000000000
--- 
a/apps/microtvm/zephyr/template_project/src/aot_standalone_demo/zephyr_uart.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#ifndef TVM_APPS_MICROTVM_ZEPHYR_AOT_STANDALONE_DEMO_ZEPHYR_UART_H_
-#define TVM_APPS_MICROTVM_ZEPHYR_AOT_STANDALONE_DEMO_ZEPHYR_UART_H_
-
-#include <stdint.h>
-
-// Used to read data from the UART.
-
-/*!
- * \brief Read Uart Rx buffer.
- * \param data Pointer to read data.
- * \param data_size_bytes Read request size in bytes.
- *
- * \return Number of data read in bytes.
- */
-uint32_t TVMPlatformUartRxRead(uint8_t* data, uint32_t data_size_bytes);
-
-/*!
- * \brief Write data in serial.
- * \param data Pointer to data to write.
- * \param size Size of data in bytes.
- *
- * \return Number of write in bytes.
- */
-uint32_t TVMPlatformWriteSerial(const char* data, uint32_t size);
-
-/*!
- * \brief Initialize Uart.
- */
-void TVMPlatformUARTInit();
-
-#endif /* TVM_APPS_MICROTVM_ZEPHYR_AOT_STANDALONE_DEMO_ZEPHYR_UART_H_ */
diff --git 
a/apps/microtvm/zephyr/template_project/src/host_driven/fvp/semihost.c 
b/apps/microtvm/zephyr/template_project/src/host_driven/fvp/semihost.c
index 2e03df0963..f51aa47c9f 100644
--- a/apps/microtvm/zephyr/template_project/src/host_driven/fvp/semihost.c
+++ b/apps/microtvm/zephyr/template_project/src/host_driven/fvp/semihost.c
@@ -22,7 +22,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-#include "semihost.h"
+#include "tvm/semihost.h"
 
 int32_t stdout_fd;
 int32_t stdin_fd;
diff --git a/apps/microtvm/zephyr/template_project/src/host_driven/main.c 
b/apps/microtvm/zephyr/template_project/src/host_driven/main.c
index c01daab6d0..1c63474817 100644
--- a/apps/microtvm/zephyr/template_project/src/host_driven/main.c
+++ b/apps/microtvm/zephyr/template_project/src/host_driven/main.c
@@ -29,7 +29,6 @@
  * this logic into your own application.
  */
 #include <stdio.h>
-#include <string.h>
 #include <tvm/runtime/crt/logging.h>
 #include <tvm/runtime/crt/microtvm_rpc_server.h>
 #include <unistd.h>
@@ -37,15 +36,7 @@
 #include <zephyr/drivers/uart.h>
 #include <zephyr/fatal.h>
 #include <zephyr/kernel.h>
-#include <zephyr/random/rand32.h>
-#include <zephyr/sys/printk.h>
-#include <zephyr/sys/reboot.h>
 #include <zephyr/sys/ring_buffer.h>
-#include <zephyr/timing/timing.h>
-
-#ifdef FVP
-#include "fvp/semihost.h"
-#endif
 
 #ifdef CONFIG_ARCH_POSIX
 #include "posix_board_if.h"
@@ -53,15 +44,11 @@
 
 #include "crt_config.h"
 
-static const struct device* tvm_uart;
+#ifdef FVP
+#include "tvm/semihost.h"
+#endif
 
-#ifdef CONFIG_LED
-#define LED0_NODE DT_ALIAS(led0)
-#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
-#define LED0_PIN DT_GPIO_PIN(LED0_NODE, gpios)
-#define LED0_FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios)
-static const struct device* led0_pin;
-#endif  // CONFIG_LED
+static const struct device* tvm_uart;
 
 static size_t g_num_bytes_requested = 0;
 static size_t g_num_bytes_written = 0;
@@ -69,20 +56,11 @@ static size_t g_num_bytes_in_rx_buffer = 0;
 
 // Called by TVM to write serial data to the UART.
 ssize_t uart_write(void* unused_context, const uint8_t* data, size_t size) {
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 1);
-#endif
   g_num_bytes_requested += size;
-
   for (size_t i = 0; i < size; i++) {
     uart_poll_out(tvm_uart, data[i]);
     g_num_bytes_written++;
   }
-
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 0);
-#endif
-
   return size;
 }
 
@@ -94,105 +72,6 @@ ssize_t serial_write(void* unused_context, const uint8_t* 
data, size_t size) {
 #endif
 }
 
-// This is invoked by Zephyr from an exception handler, which will be invoked
-// if the device crashes. Here, we turn on the LED and spin.
-void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t* esf) {
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 1);
-#endif
-  for (;;)
-    ;
-}
-
-// Called by TVM when a message needs to be formatted.
-size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
-                                va_list args) {
-  return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
-}
-
-// Called by TVM when an internal invariant is violated, and execution cannot 
continue.
-void TVMPlatformAbort(tvm_crt_error_t error) {
-  TVMLogf("TVMError: 0x%x", error);
-  sys_reboot(SYS_REBOOT_COLD);
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 1);
-#endif
-  for (;;)
-    ;
-}
-
-// Called by TVM to generate random data.
-tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
-  uint32_t random;  // one unit of random data.
-
-  // Fill parts of `buffer` which are as large as `random`.
-  size_t num_full_blocks = num_bytes / sizeof(random);
-  for (int i = 0; i < num_full_blocks; ++i) {
-    random = sys_rand32_get();
-    memcpy(&buffer[i * sizeof(random)], &random, sizeof(random));
-  }
-
-  // Fill any leftover tail which is smaller than `random`.
-  size_t num_tail_bytes = num_bytes % sizeof(random);
-  if (num_tail_bytes > 0) {
-    random = sys_rand32_get();
-    memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes);
-  }
-  return kTvmErrorNoError;
-}
-
-// Heap for use by TVMPlatformMemoryAllocate.
-K_HEAP_DEFINE(tvm_heap, HEAP_SIZE_BYTES);
-
-// Called by TVM to allocate memory.
-tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
-  *out_ptr = k_heap_alloc(&tvm_heap, num_bytes, K_NO_WAIT);
-  return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError;
-}
-
-// Called by TVM to deallocate memory.
-tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
-  k_heap_free(&tvm_heap, ptr);
-  return kTvmErrorNoError;
-}
-
-volatile timing_t g_microtvm_start_time, g_microtvm_end_time;
-int g_microtvm_timer_running = 0;
-
-// Called to start system timer.
-tvm_crt_error_t TVMPlatformTimerStart() {
-  if (g_microtvm_timer_running) {
-    TVMLogf("timer already running");
-    return kTvmErrorPlatformTimerBadState;
-  }
-
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 1);
-#endif
-  g_microtvm_start_time = timing_counter_get();
-  g_microtvm_timer_running = 1;
-  return kTvmErrorNoError;
-}
-
-// Called to stop system timer.
-tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
-  if (!g_microtvm_timer_running) {
-    TVMLogf("timer not running");
-    return kTvmErrorSystemErrorMask | 2;
-  }
-
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 0);
-#endif
-
-  g_microtvm_end_time = timing_counter_get();
-  uint64_t cycles = timing_cycles_get(&g_microtvm_start_time, 
&g_microtvm_end_time);
-  uint64_t ns_spent = timing_cycles_to_ns(cycles);
-  *elapsed_time_seconds = ns_spent / (double)1e9;
-  g_microtvm_timer_running = 0;
-  return kTvmErrorNoError;
-}
-
 // Ring buffer used to store data read from the UART on rx interrupt.
 // This ring buffer size is only required for testing with QEMU and not for 
physical hardware.
 #define RING_BUF_SIZE_BYTES (TVM_CRT_MAX_PACKET_SIZE_BYTES + 100)
@@ -223,8 +102,6 @@ void uart_irq_cb(const struct device* dev, void* user_data) 
{
       if (err != 0) {
         TVMPlatformAbort((tvm_crt_error_t)0xbeef2);
       }
-      // CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: 
%d", bytes_read,
-      // bytes_written);
     }
   }
 }
@@ -238,29 +115,12 @@ void uart_rx_init(struct ring_buf* rbuf, const struct 
device* dev) {
 // The main function of this application.
 extern void __stdout_hook_install(int (*hook)(int));
 void main(void) {
-#ifdef CONFIG_LED
-  int ret;
-  led0_pin = device_get_binding(LED0);
-  if (led0_pin == NULL) {
-    for (;;)
-      ;
-  }
-  ret = gpio_pin_configure(led0_pin, LED0_PIN, GPIO_OUTPUT_ACTIVE | 
LED0_FLAGS);
-  if (ret < 0) {
-    TVMPlatformAbort((tvm_crt_error_t)0xbeef4);
-  }
-  gpio_pin_set(led0_pin, LED0_PIN, 1);
-#endif
+  TVMPlatformInitialize();
 
   // Claim console device.
   tvm_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
   uart_rx_init(&uart_rx_rbuf, tvm_uart);
 
-  // Initialize system timing. We could stop and start it every time, but we'll
-  // be using it enough we should just keep it enabled.
-  timing_init();
-  timing_start();
-
 #ifdef FVP
   init_semihosting();
   // send some dummy log to speed up the initialization
@@ -274,10 +134,6 @@ void main(void) {
   microtvm_rpc_server_t server = MicroTVMRpcServerInit(serial_write, NULL);
   TVMLogf("microTVM Zephyr runtime - running");
 
-#ifdef CONFIG_LED
-  gpio_pin_set(led0_pin, LED0_PIN, 0);
-#endif
-
   // The main application loop. We continuously read commands from the UART
   // and dispatch them to MicroTVMRpcServerLoop().
   while (true) {
diff --git a/apps/microtvm/zephyr/template_project/src/host_driven/platform.c 
b/apps/microtvm/zephyr/template_project/src/host_driven/platform.c
new file mode 100644
index 0000000000..8aa9abf235
--- /dev/null
+++ b/apps/microtvm/zephyr/template_project/src/host_driven/platform.c
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
+
+#include <dlpack/dlpack.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tvm/runtime/crt/error_codes.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/kernel.h>
+#include <zephyr/random/rand32.h>
+#include <zephyr/sys/printk.h>
+#include <zephyr/sys/reboot.h>
+#include <zephyr/timing/timing.h>
+
+K_HEAP_DEFINE(tvm_heap, TVM_WORKSPACE_SIZE_BYTES);
+
+volatile timing_t g_microtvm_start_time, g_microtvm_end_time;
+int g_microtvm_timer_running = 0;
+
+#ifdef CONFIG_LED
+#define LED0_NODE DT_ALIAS(led0)
+static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
+#endif  // CONFIG_LED
+
+// This is invoked by Zephyr from an exception handler, which will be invoked
+// if the device crashes. Here, we turn on the LED and spin.
+void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t* esf) {
+#ifdef CONFIG_LED
+  gpio_pin_set_dt(&led0, 1);
+#endif
+  for (;;)
+    ;
+}
+
+void TVMPlatformAbort(tvm_crt_error_t error) {
+  TVMLogf("TVMError: 0x%x", error);
+  sys_reboot(SYS_REBOOT_COLD);
+#ifdef CONFIG_LED
+  gpio_pin_set_dt(&led0, 1);
+#endif
+  for (;;)
+    ;
+}
+
+size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
+                                va_list args) {
+  return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
+}
+
+tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
+  *out_ptr = k_heap_alloc(&tvm_heap, num_bytes, K_NO_WAIT);
+  return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError;
+}
+
+tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
+  k_heap_free(&tvm_heap, ptr);
+  return kTvmErrorNoError;
+}
+
+// Called to start system timer.
+tvm_crt_error_t TVMPlatformTimerStart() {
+  if (g_microtvm_timer_running) {
+    TVMLogf("timer already running");
+    return kTvmErrorPlatformTimerBadState;
+  }
+
+#ifdef CONFIG_LED
+  gpio_pin_set_dt(&led0, 1);
+#endif
+  g_microtvm_start_time = timing_counter_get();
+  g_microtvm_timer_running = 1;
+  return kTvmErrorNoError;
+}
+
+// Called to stop system timer.
+tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
+  if (!g_microtvm_timer_running) {
+    TVMLogf("timer not running");
+    return kTvmErrorSystemErrorMask | 2;
+  }
+
+#ifdef CONFIG_LED
+  gpio_pin_set_dt(&led0, 0);
+#endif
+
+  g_microtvm_end_time = timing_counter_get();
+  uint64_t cycles = timing_cycles_get(&g_microtvm_start_time, 
&g_microtvm_end_time);
+  uint64_t ns_spent = timing_cycles_to_ns(cycles);
+  *elapsed_time_seconds = ns_spent / (double)1e9;
+  g_microtvm_timer_running = 0;
+  return kTvmErrorNoError;
+}
+
+tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
+  uint32_t random;  // one unit of random data.
+
+  // Fill parts of `buffer` which are as large as `random`.
+  size_t num_full_blocks = num_bytes / sizeof(random);
+  for (int i = 0; i < num_full_blocks; ++i) {
+    random = sys_rand32_get();
+    memcpy(&buffer[i * sizeof(random)], &random, sizeof(random));
+  }
+
+  // Fill any leftover tail which is smaller than `random`.
+  size_t num_tail_bytes = num_bytes % sizeof(random);
+  if (num_tail_bytes > 0) {
+    random = sys_rand32_get();
+    memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes);
+  }
+  return kTvmErrorNoError;
+}
+
+tvm_crt_error_t TVMPlatformInitialize() {
+#ifdef CONFIG_LED
+  if (!device_is_ready(led0.port)) {
+    for (;;)
+      ;
+  }
+  int ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE);
+  if (ret < 0) {
+    TVMPlatformAbort((tvm_crt_error_t)0xbeef4);
+  }
+  gpio_pin_set_dt(&led0, 0);
+#endif
+
+  // Initialize system timing. We could stop and start it every time, but we'll
+  // be using it enough we should just keep it enabled.
+  timing_init();
+  timing_start();
+
+  return kTvmErrorNoError;
+}
diff --git a/apps/microtvm/zephyr/template_project/src/mlperftiny/platform.cc 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/platform.cc
new file mode 100644
index 0000000000..9dc4516271
--- /dev/null
+++ b/apps/microtvm/zephyr/template_project/src/mlperftiny/platform.cc
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
+
+#include <dlpack/dlpack.h>
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/crt/error_codes.h>
+#include <tvm/runtime/crt/logging.h>
+#include <tvm/runtime/crt/platform.h>
+#include <tvm/runtime/crt/stack_allocator.h>
+#include <zephyr/sys/printk.h>
+#include <zephyr/sys/reboot.h>
+
+#include "crt_config.h"
+
+// TVM_WORKSPACE_SIZE_BYTES is defined in python
+static uint8_t g_aot_memory[TVM_WORKSPACE_SIZE_BYTES];
+tvm_workspace_t app_workspace;
+
+size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
+                                va_list args) {
+  return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
+}
+
+void TVMPlatformAbort(tvm_crt_error_t error) {
+  TVMLogf("TVMPlatformAbort: %08x\n", error);
+  sys_reboot(SYS_REBOOT_COLD);
+  for (;;)
+    ;
+}
+
+tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
+  return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr);
+}
+
+tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
+  return StackMemoryManager_Free(&app_workspace, ptr);
+}
+
+tvm_crt_error_t TVMPlatformInitialize() {
+  StackMemoryManager_Init(&app_workspace, g_aot_memory, sizeof(g_aot_memory));
+  return kTvmErrorNoError;
+}
diff --git 
a/apps/microtvm/zephyr/template_project/src/mlperftiny/submitter_implemented.cc 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/submitter_implemented.cc
index 91ae0c025c..72b679c640 100644
--- 
a/apps/microtvm/zephyr/template_project/src/mlperftiny/submitter_implemented.cc
+++ 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/submitter_implemented.cc
@@ -19,20 +19,46 @@
 
 #include "api/submitter_implemented.h"
 
+#include <string.h>
+#include <tvm/runtime/crt/logging.h>
 #include <tvm/runtime/crt/platform.h>
 #include <unistd.h>
 #include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/uart.h>
 #include <zephyr/kernel.h>
+#include <zephyr/sys/ring_buffer.h>
 
 #include "api/internally_implemented.h"
-#include "tvmruntime.h"
-#include "zephyr_uart.h"
+#include "crt_config.h"
+#include "tvm/output_data.h"
+#include "tvmgen_default.h"
+
+// ###############################################################
+// Model
+// ###############################################################
+#define MODEL_KWS 1
+#define MODEL_VWW 2
+#define MODEL_AD 3
+#define MODEL_IC 4
 
 static void* g_input_data;
-#if TARGET_MODEL == 3  // AD
+#if TARGET_MODEL == MODEL_AD
 static uint8_t __aligned(4) g_input_data_buffer_aligned[MAX_DB_INPUT_SIZE];
 #endif
 
+// OUT_QUANT_SCALE and OUT_QUANT_ZERO are set in python.
+#if TARGET_MODEL == MODEL_AD
+float* g_output_data = output_data;
+#else
+int8_t* g_output_data = output_data;
+float g_quant_scale = OUT_QUANT_SCALE;
+int8_t g_quant_zero = OUT_QUANT_ZERO;
+#endif
+size_t g_output_data_len = output_data_len;
+
+// ###############################################################
+// GPIO
+// ###############################################################
 #if EE_CFG_ENERGY_MODE == 1 && NRF_BOARD != 1
 // use GPIO PC6 which is on connector CN7 pin 1 on the nucleo_l4r5zi
 static const char* g_gpio_device_name = "GPIOC";
@@ -40,23 +66,141 @@ static const struct device* g_gpio_dev;
 static const gpio_pin_t g_gpio_pin = 6;
 #endif
 
+// ###############################################################
+// UART
+// ###############################################################
+#define TVM_UART_DEFAULT_BAUDRATE 115200
+static const struct device* g_microtvm_uart;
+
+void UartInit(uint32_t baudrate = TVM_UART_DEFAULT_BAUDRATE) {
+  // Claim console device.
+  g_microtvm_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+  const struct uart_config config = {.baudrate = baudrate,
+                                     .parity = UART_CFG_PARITY_NONE,
+                                     .stop_bits = UART_CFG_STOP_BITS_1,
+                                     .data_bits = UART_CFG_DATA_BITS_8,
+                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};
+  uart_configure(g_microtvm_uart, &config);
+}
+
+char UartRxRead() {
+  unsigned char c;
+  int ret = -1;
+  while (ret != 0) {
+    ret = uart_poll_in(g_microtvm_uart, &c);
+  }
+  return (char)c;
+}
+
+uint32_t UartTxWrite(const char* data, uint32_t size) {
+  for (uint32_t i = 0; i < size; i++) {
+    uart_poll_out(g_microtvm_uart, data[i]);
+  }
+  return size;
+}
+
+// ###############################################################
+// TVM
+// ###############################################################
+#ifdef __cplusplus
+extern "C" {
+#endif
+// TODO(mehrdadh): remove and reuse the CRT
+// implementation in src/runtime/crt/common/crt_backend_api.c
+void* TVMBackendAllocWorkspace(int device_type, int device_id, uint64_t 
nbytes, int dtype_code_hint,
+                               int dtype_bits_hint) {
+  tvm_crt_error_t err = kTvmErrorNoError;
+  void* ptr = 0;
+  DLDevice dev = {(DLDeviceType)device_type, device_id};
+  assert(nbytes > 0);
+  err = TVMPlatformMemoryAllocate(nbytes, dev, &ptr);
+  CHECK_EQ(err, kTvmErrorNoError,
+           "TVMBackendAllocWorkspace(%d, %d, %" PRIu64 ", %d, %d) -> %" 
PRId32, device_type,
+           device_id, nbytes, dtype_code_hint, dtype_bits_hint, err);
+  return ptr;
+}
+
+// TODO(mehrdadh): remove and reuse the CRT
+// implementation in src/runtime/crt/common/crt_backend_api.c
+int TVMBackendFreeWorkspace(int device_type, int device_id, void* ptr) {
+  tvm_crt_error_t err = kTvmErrorNoError;
+  DLDevice dev = {(DLDeviceType)device_type, device_id};
+  err = TVMPlatformMemoryFree(ptr, dev);
+  CHECK_EQ(err, kTvmErrorNoError, "TVMBackendFreeWorkspace(%d, %d)", 
device_type, device_id);
+  return err;
+}
+
+void TVMLogf(const char* msg, ...) {
+  char buffer[128];
+  int size;
+  va_list args;
+  va_start(args, msg);
+  size = TVMPlatformFormatMessage(buffer, 128, msg, args);
+  va_end(args);
+  UartTxWrite(buffer, (size_t)size);
+}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+void Infer(void* input_ptr) {
+  struct tvmgen_default_inputs inputs = {
+#if TARGET_MODEL == MODEL_KWS
+    .input_1 = input_ptr,
+#elif TARGET_MODEL == MODEL_IC
+    .input_1_int8 = input_ptr,
+#elif TARGET_MODEL == MODEL_VWW
+    .input_1_int8 = input_ptr,
+#elif TARGET_MODEL == MODEL_AD
+    .input_1 = input_ptr,
+#elif
+#error Wrong model.
+#endif
+  };
+
+  struct tvmgen_default_outputs outputs = {
+#if TARGET_MODEL == MODEL_KWS
+#if COMPILE_WITH_CMSISNN
+    .Identity = output_data,
+#else
+    .output = output_data,
+#endif
+#elif TARGET_MODEL == MODEL_IC
+    .Identity_int8 = output_data,
+#elif TARGET_MODEL == MODEL_VWW
+    .Identity_int8 = output_data,
+#elif TARGET_MODEL == MODEL_AD
+    .Identity = output_data,
+#endif
+  };
+
+  int ret_val = tvmgen_default_run(&inputs, &outputs);
+  if (ret_val != 0) {
+    th_printf("Error: %d\n", ret_val);
+  }
+}
+
+// ###############################################################
+// MLPerftiny APIs
+// ###############################################################
 // Implement this method to prepare for inference and preprocess inputs.
 // Modified from source
 void th_load_tensor() {
-#if TARGET_MODEL == 1  // KWS
+#if TARGET_MODEL == MODEL_KWS
   g_input_data = static_cast<void*>(ee_get_buffer_pointer());
-#elif TARGET_MODEL == 2  // VWW
+#elif TARGET_MODEL == MODEL_VWW
   // Converting uint8 to int8
   int8_t* temp_int = reinterpret_cast<int8_t*>(ee_get_buffer_pointer());
   for (size_t i = 0; i < MAX_DB_INPUT_SIZE; i++) {
     temp_int[i] -= 128;
   }
   g_input_data = static_cast<void*>(temp_int);
-#elif TARGET_MODEL == 3  // AD
+#elif TARGET_MODEL == MODEL_AD
   uint8_t* buffer = ee_get_buffer_pointer();
   memcpy(g_input_data_buffer_aligned, buffer, 
sizeof(g_input_data_buffer_aligned));
   g_input_data = g_input_data_buffer_aligned;
-#elif TARGET_MODEL == 4  // IC
+#elif TARGET_MODEL == MODEL_IC
   uint8_t* temp_uint = reinterpret_cast<uint8_t*>(ee_get_buffer_pointer());
   int8_t* temp_int = reinterpret_cast<int8_t*>(ee_get_buffer_pointer());
   for (size_t i = 0; i < MAX_DB_INPUT_SIZE; i++) {
@@ -71,7 +215,7 @@ void th_load_tensor() {
 #endif
 }
 
-#if TARGET_MODEL == 3  // model AD
+#if TARGET_MODEL == MODEL_AD
 // calculate |output - input| for AD model
 static float calculate_result() {
   size_t feature_size = g_output_data_len;
@@ -95,7 +239,7 @@ void th_results() {
    * The results need to be printed back in exactly this format; if easier
    * to just modify this loop than copy to results[] above, do that.
    */
-#if TARGET_MODEL == 3  // model AD
+#if TARGET_MODEL == MODEL_AD
   th_printf("m-results-[%0.3f]\r\n", calculate_result());
 #else
   size_t kCategoryCount = g_output_data_len;
@@ -114,11 +258,11 @@ void th_results() {
 
 // Implement this method with the logic to perform one inference cycle.
 // Modified from source
-void th_infer() { TVMInfer(g_input_data); }
+void th_infer() { Infer(g_input_data); }
 
 /// \brief optional API.
 // Modified from source
-void th_final_initialize(void) { TVMRuntimeInit(); }
+void th_final_initialize(void) { TVMPlatformInitialize(); }
 
 void th_pre() {}
 void th_post() {}
@@ -156,18 +300,18 @@ void th_printf(const char* p_fmt, ...) {
   va_start(args, p_fmt);
   size = TVMPlatformFormatMessage(buffer, 128, p_fmt, args);
   va_end(args);
-  TVMPlatformWriteSerial(buffer, (size_t)size);
+  UartTxWrite(buffer, (size_t)size);
 }
 
 // Modified from source
-char th_getchar() { return TVMPlatformUartRxRead(); }
+char th_getchar() { return UartRxRead(); }
 
 // Modified from source
 void th_serialport_initialize(void) {
 #if EE_CFG_ENERGY_MODE == 1 && NRF_BOARD != 1
-  TVMPlatformUARTInit(9600);
+  UartInit(9600);
 #else
-  TVMPlatformUARTInit();
+  UartInit();
 #endif
 }
 
diff --git a/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.cc 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.cc
deleted file mode 100644
index 3fb7ccf8eb..0000000000
--- a/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include "tvmruntime.h"
-
-#include <assert.h>
-#include <float.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <tvm/runtime/c_runtime_api.h>
-#include <tvm/runtime/crt/logging.h>
-#include <tvm/runtime/crt/platform.h>
-#include <tvm/runtime/crt/stack_allocator.h>
-#include <zephyr/kernel.h>
-#include <zephyr/sys/reboot.h>
-
-#include "output_data.h"
-#include "tvmgen_default.h"
-#include "zephyr_uart.h"
-
-#ifdef CONFIG_ARCH_POSIX
-#include "posix_board_if.h"
-#endif
-
-// OUT_QUANT_SCALE and OUT_QUANT_ZERO are set in python.
-#if TARGET_MODEL == 3
-float* g_output_data = output_data;
-#else
-int8_t* g_output_data = output_data;
-float g_quant_scale = OUT_QUANT_SCALE;
-int8_t g_quant_zero = OUT_QUANT_ZERO;
-#endif
-size_t g_output_data_len = output_data_len;
-
-// WORKSPACE_SIZE is defined in python
-static uint8_t g_aot_memory[WORKSPACE_SIZE];
-tvm_workspace_t app_workspace;
-
-size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
-                                va_list args) {
-  return vsnprintk(out_buf, out_buf_size_bytes, fmt, args);
-}
-
-void TVMLogf(const char* msg, ...) {
-  char buffer[128];
-  int size;
-  va_list args;
-  va_start(args, msg);
-  size = TVMPlatformFormatMessage(buffer, 128, msg, args);
-  va_end(args);
-  TVMPlatformWriteSerial(buffer, (size_t)size);
-}
-
-void __attribute__((noreturn)) TVMPlatformAbort(tvm_crt_error_t error) {
-  TVMLogf("TVMPlatformAbort: %08x\n", error);
-  sys_reboot(SYS_REBOOT_COLD);
-  for (;;)
-    ;
-}
-
-tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
-  return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr);
-}
-
-tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
-  return StackMemoryManager_Free(&app_workspace, ptr);
-}
-
-void timer_expiry_function(struct k_timer* timer_id) { return; }
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-void* TVMBackendAllocWorkspace(int device_type, int device_id, uint64_t 
nbytes, int dtype_code_hint,
-                               int dtype_bits_hint) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  void* ptr = 0;
-  DLDevice dev = {(DLDeviceType)device_type, device_id};
-  assert(nbytes > 0);
-  err = TVMPlatformMemoryAllocate(nbytes, dev, &ptr);
-  CHECK_EQ(err, kTvmErrorNoError,
-           "TVMBackendAllocWorkspace(%d, %d, %" PRIu64 ", %d, %d) -> %" 
PRId32, device_type,
-           device_id, nbytes, dtype_code_hint, dtype_bits_hint, err);
-  return ptr;
-}
-
-int TVMBackendFreeWorkspace(int device_type, int device_id, void* ptr) {
-  tvm_crt_error_t err = kTvmErrorNoError;
-  DLDevice dev = {(DLDeviceType)device_type, device_id};
-  err = TVMPlatformMemoryFree(ptr, dev);
-  CHECK_EQ(err, kTvmErrorNoError, "TVMBackendFreeWorkspace(%d, %d)", 
device_type, device_id);
-  return err;
-}
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-void TVMRuntimeInit() { StackMemoryManager_Init(&app_workspace, g_aot_memory, 
WORKSPACE_SIZE); }
-
-void TVMInfer(void* input_ptr) {
-  struct tvmgen_default_inputs inputs = {
-#if TARGET_MODEL == MODEL_KWS
-    .input_1 = input_ptr,
-#elif TARGET_MODEL == MODEL_IC
-    .input_1_int8 = input_ptr,
-#elif TARGET_MODEL == MODEL_VWW
-    .input_1_int8 = input_ptr,
-#elif TARGET_MODEL == MODEL_AD
-    .input_1 = input_ptr,
-#elif
-#error Wrong model.
-#endif
-  };
-
-  struct tvmgen_default_outputs outputs = {
-#if TARGET_MODEL == MODEL_KWS
-#if COMPILE_WITH_CMSISNN
-    .Identity = output_data,
-#else
-    .output = output_data,
-#endif
-#elif TARGET_MODEL == MODEL_IC
-    .Identity_int8 = output_data,
-#elif TARGET_MODEL == MODEL_VWW
-    .Identity_int8 = output_data,
-#elif TARGET_MODEL == MODEL_AD
-    .Identity = output_data,
-#endif
-  };
-
-  int ret_val = tvmgen_default_run(&inputs, &outputs);
-  if (ret_val != 0) {
-    TVMLogf("Error: %d\n", ret_val);
-  }
-}
-
-int8_t QuantizeFloatToInt8(float value, float scale, int zero_point) {
-  int32_t result = round(value / scale) + zero_point;
-  if (result < INT8_MIN) {
-    result = INT8_MIN;
-  }
-  if (result > INT8_MAX) {
-    result = INT8_MAX;
-  }
-  return (int8_t)(result);
-}
diff --git a/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.h 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.h
deleted file mode 100644
index 940d64634d..0000000000
--- a/apps/microtvm/zephyr/template_project/src/mlperftiny/tvmruntime.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#ifndef APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_TVMRUNTIME_H_
-#define APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_TVMRUNTIME_H_
-
-#include <stdarg.h>
-#include <tvm/runtime/crt/error_codes.h>
-#include <unistd.h>
-
-#define MODEL_KWS 1
-#define MODEL_VWW 2
-#define MODEL_AD 3
-#define MODEL_IC 4
-
-extern const unsigned char g_wakeup_sequence[];
-extern size_t g_output_data_len;
-
-#if TARGET_MODEL == 3
-extern float* g_output_data;
-#else
-extern int8_t* g_output_data;
-#endif
-
-extern float g_quant_scale;
-extern int8_t g_quant_zero;
-
-/*!
- * \brief Initialize TVM runtime.
- */
-void TVMRuntimeInit();
-
-/*!
- * \brief Run TVM inference.
- */
-void TVMInfer(void* input_ptr);
-
-/*!
- * \brief Quantize float to int8.
- * \param value Input data in float.
- * \param scale Quantization scale factor.
- * \param zero_point Quantization zero point.
- */
-int8_t QuantizeFloatToInt8(float value, float scale, int zero_point);
-
-#endif /* APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_TVMRUNTIME_H_ */
diff --git 
a/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.cc 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.cc
deleted file mode 100644
index 0922c32133..0000000000
--- a/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include "zephyr_uart.h"
-
-#include <tvm/runtime/crt/error_codes.h>
-#include <zephyr/drivers/uart.h>
-#include <zephyr/sys/ring_buffer.h>
-
-#include "crt_config.h"
-
-static const struct device* g_microtvm_uart;
-
-static uint8_t uart_data[8];
-
-// UART interrupt callback.
-void uart_irq_cb(const struct device* dev, void* user_data) {
-  while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
-    struct ring_buf* rbuf = (struct ring_buf*)user_data;
-    if (uart_irq_rx_ready(dev) != 0) {
-      for (;;) {
-        // Read a small chunk of data from the UART.
-        int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data));
-        if (bytes_read < 0) {
-          TVMPlatformAbort((tvm_crt_error_t)(0xbeef1));
-        } else if (bytes_read == 0) {
-          break;
-        }
-        // Write it into the ring buffer.
-        int bytes_written = ring_buf_put(rbuf, uart_data, bytes_read);
-        if (bytes_read != bytes_written) {
-          TVMPlatformAbort((tvm_crt_error_t)(0xbeef2));
-        }
-      }
-    }
-  }
-}
-
-// Initialize the UART receiver.
-void uart_rx_init(struct ring_buf* rbuf, const struct device* dev) {
-  uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)rbuf);
-  uart_irq_rx_enable(dev);
-}
-
-// UART read.
-char TVMPlatformUartRxRead() {
-  unsigned char c;
-  int ret = -1;
-  while (ret != 0) {
-    ret = uart_poll_in(g_microtvm_uart, &c);
-  }
-  return (char)c;
-}
-
-// UART write.
-uint32_t TVMPlatformWriteSerial(const char* data, uint32_t size) {
-  for (uint32_t i = 0; i < size; i++) {
-    uart_poll_out(g_microtvm_uart, data[i]);
-  }
-  return size;
-}
-
-// Initialize UART.
-void TVMPlatformUARTInit(uint32_t baudrate /* = TVM_UART_DEFAULT_BAUDRATE */) {
-  // Claim console device.
-  g_microtvm_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
-  const struct uart_config config = {.baudrate = baudrate,
-                                     .parity = UART_CFG_PARITY_NONE,
-                                     .stop_bits = UART_CFG_STOP_BITS_1,
-                                     .data_bits = UART_CFG_DATA_BITS_8,
-                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};
-  uart_configure(g_microtvm_uart, &config);
-}
diff --git a/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.h 
b/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.h
deleted file mode 100644
index f10cf02622..0000000000
--- a/apps/microtvm/zephyr/template_project/src/mlperftiny/zephyr_uart.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#ifndef APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_ZEPHYR_UART_H_
-#define APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_ZEPHYR_UART_H_
-
-#include <stdint.h>
-
-#define TVM_UART_DEFAULT_BAUDRATE 115200
-
-/*!
- * \brief Read Uart Rx buffer.
- * \param data Pointer to read data.
- * \param data_size_bytes Read request size in bytes.
- *
- * \return Number of data read in bytes.
- */
-char TVMPlatformUartRxRead();
-
-/*!
- * \brief Write data in serial.
- * \param data Pointer to data to write.
- * \param size Size of data in bytes.
- *
- * \return Number of write in bytes.
- */
-uint32_t TVMPlatformWriteSerial(const char* data, uint32_t size);
-
-/*!
- * \brief Initialize Uart.
- * \param baudrate Desired UART baudrate.
- */
-void TVMPlatformUARTInit(uint32_t baudrate = TVM_UART_DEFAULT_BAUDRATE);
-
-#endif /* APPS_MICROTVM_ZEPHYR_TEMPLATE_PROJECT_SRC_MLPERFTINY_ZEPHYR_UART_H_ 
*/
diff --git a/cmake/modules/CRT.cmake b/cmake/modules/CRT.cmake
index 518a613dc1..3ddbb5298f 100644
--- a/cmake/modules/CRT.cmake
+++ b/cmake/modules/CRT.cmake
@@ -22,8 +22,9 @@ if(USE_MICRO)
       CRT_TEMPLATE_FILE_COPY_JOBS
       "src/runtime/crt/host microtvm_api_server.py -> crt"
       "src/runtime/crt/host Makefile.template -> crt"
-      "src/runtime/crt crt_config-template.h -> crt"
       "src/runtime/crt/host main.cc -> crt/src"
+      "src/runtime/crt/host platform.cc -> crt/src"
+      "src/runtime/crt crt_config-template.h -> crt/crt_config"
     )
 
     foreach(job_spec IN LISTS CRT_TEMPLATE_FILE_COPY_JOBS)
@@ -66,6 +67,10 @@ if(USE_MICRO)
       endforeach()
     endforeach()
 
+    # Add template files for Model Library Format
+    configure_file("src/runtime/crt/crt_config-template.h" 
"${MICROTVM_TEMPLATE_PROJECTS}/crt/templates/crt_config.h.template" COPYONLY)
+    configure_file("src/runtime/crt/platform-template.c" 
"${MICROTVM_TEMPLATE_PROJECTS}/crt/templates/platform.c.template" COPYONLY)
+
     add_custom_target(crt DEPENDS ${crt_template_deps})
   endfunction()
 
diff --git a/cmake/modules/Zephyr.cmake b/cmake/modules/Zephyr.cmake
index a13aef3319..38551f1dd4 100644
--- a/cmake/modules/Zephyr.cmake
+++ b/cmake/modules/Zephyr.cmake
@@ -26,11 +26,9 @@ if(USE_MICRO)
       "apps/microtvm/zephyr/template_project boards.json -> zephyr"
       "apps/microtvm/zephyr/template_project CMakeLists.txt.template -> zephyr"
       "apps/microtvm/zephyr/template_project/src/aot_standalone_demo *.c -> 
zephyr/src/aot_standalone_demo"
-      "apps/microtvm/zephyr/template_project/src/aot_standalone_demo *.h -> 
zephyr/src/aot_standalone_demo"
       "apps/microtvm/zephyr/template_project/src/host_driven *.c -> 
zephyr/src/host_driven"
       "apps/microtvm/zephyr/template_project/src/host_driven *.h -> 
zephyr/src/host_driven"
       "apps/microtvm/zephyr/template_project/src/mlperftiny *.cc -> 
zephyr/src/mlperftiny"
-      "apps/microtvm/zephyr/template_project/src/mlperftiny *.h -> 
zephyr/src/mlperftiny"
       "3rdparty/mlperftiny/api * -> zephyr/src/mlperftiny/api"
       "apps/microtvm/zephyr/template_project/fvp-hack * -> zephyr/fvp-hack"
       "apps/microtvm/zephyr/template_project/qemu-hack * -> zephyr/qemu-hack"
diff --git a/gallery/how_to/work_with_microtvm/micro_mlperftiny.py 
b/gallery/how_to/work_with_microtvm/micro_mlperftiny.py
index e8c6a253ad..bb7abddddc 100644
--- a/gallery/how_to/work_with_microtvm/micro_mlperftiny.py
+++ b/gallery/how_to/work_with_microtvm/micro_mlperftiny.py
@@ -226,7 +226,7 @@ with tarfile.open(extra_tar_file, "w:gz") as tf:
             shape=output_shape,
             dtype=output_dtype,
         ),
-        "include",
+        "include/tvm",
         tf,
     )
 
diff --git a/gallery/how_to/work_with_microtvm/micro_pytorch.py 
b/gallery/how_to/work_with_microtvm/micro_pytorch.py
index a7f5f10280..a0f4ebddee 100644
--- a/gallery/how_to/work_with_microtvm/micro_pytorch.py
+++ b/gallery/how_to/work_with_microtvm/micro_pytorch.py
@@ -131,7 +131,7 @@ with tvm.transform.PassContext(
 #
 
 template_project_path = 
pathlib.Path(tvm.micro.get_microtvm_template_projects("crt"))
-project_options = {"verbose": False, "memory_size_bytes": 6 * 1024 * 1024}
+project_options = {"verbose": False, "workspace_size_bytes": 6 * 1024 * 1024}
 
 temp_dir = tvm.contrib.utils.tempdir() / "project"
 project = tvm.micro.generate_project(
diff --git a/include/tvm/runtime/crt/platform.h 
b/include/tvm/runtime/crt/platform.h
index 1bc610e6cc..85121fd0f5 100644
--- a/include/tvm/runtime/crt/platform.h
+++ b/include/tvm/runtime/crt/platform.h
@@ -139,6 +139,15 @@ tvm_crt_error_t TVMPlatformAfterMeasurement();
  */
 tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes);
 
+/*! \brief Initialize TVM inference.
+ *
+ * Placeholder function for TVM inference initializations on a specific 
platform.
+ * A common use of this function is setting up workspace memory for TVM 
inference.
+ *
+ * \return kTvmErrorNoError if successful.
+ */
+tvm_crt_error_t TVMPlatformInitialize();
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/python/tvm/micro/model_library_format.py 
b/python/tvm/micro/model_library_format.py
index fc32fe34d6..b16877b256 100644
--- a/python/tvm/micro/model_library_format.py
+++ b/python/tvm/micro/model_library_format.py
@@ -26,7 +26,7 @@ import tarfile
 import typing
 
 import tvm
-from tvm.micro import get_standalone_crt_dir
+from tvm.micro import get_standalone_crt_dir, get_microtvm_template_projects
 
 from .._ffi import get_global_func
 from ..contrib import utils
@@ -39,6 +39,7 @@ from ..tir import expr
 # This should be kept identical to runtime::symbol::tvm_module_main
 MAIN_FUNC_NAME_STR = "__tvm_main__"
 STANDALONE_CRT_URL = "./runtime"
+CRT_TEMPLATE_FILES_URL = "./templates"
 METADATA_FILE = "metadata.json"
 
 
@@ -373,7 +374,18 @@ def _make_tar(source_dir, tar_file_path, modules):
         for mod in modules:
             is_aot = isinstance(mod, executor_factory.AOTExecutorFactoryModule)
             if is_aot and str(mod.runtime) == "crt":
+                crt_template_path = 
pathlib.Path(get_microtvm_template_projects("crt"))
                 tar_f.add(get_standalone_crt_dir(), arcname=STANDALONE_CRT_URL)
+
+                # Add template files from CRT template project
+                for file in [
+                    "templates/crt_config.h.template",
+                    "templates/platform.c.template",
+                ]:
+                    tar_f.add(
+                        crt_template_path / pathlib.Path(file),
+                        
arcname=f"{CRT_TEMPLATE_FILES_URL}/{pathlib.Path(file).name}",
+                    )
                 break
 
 
diff --git a/python/tvm/testing/aot.py b/python/tvm/testing/aot.py
index 5ddbdcabac..a975eb95bc 100644
--- a/python/tvm/testing/aot.py
+++ b/python/tvm/testing/aot.py
@@ -725,8 +725,8 @@ def run_and_check(
         os.mkdir(include_path)
         crt_root = tvm.micro.get_microtvm_template_projects("crt")
         shutil.copy2(
-            os.path.join(crt_root, "crt_config-template.h"),
-            os.path.join(include_path, "crt_config.h"),
+            pathlib.Path(crt_root) / "crt_config" / "crt_config-template.h",
+            pathlib.Path(include_path) / "crt_config.h",
         )
 
         workspace_bytes = 0
diff --git a/src/runtime/crt/host/Makefile.template 
b/src/runtime/crt/host/Makefile.template
index 2caf7ba0bc..526b17deb7 100644
--- a/src/runtime/crt/host/Makefile.template
+++ b/src/runtime/crt/host/Makefile.template
@@ -16,9 +16,9 @@
 # under the License.
 
 INCLUDES ?= -isystem crt/include -Icrt_config
-MEMORY_SIZE_BYTES := <MEMORY_SIZE_BYTES>
+TVM_WORKSPACE_SIZE_BYTES := <TVM_WORKSPACE_SIZE_BYTES>
 CFLAGS ?= -Werror -Wall
-CXXFLAGS ?= -Werror -Wall -std=c++11 -DTVM_HOST_USE_GRAPH_EXECUTOR_MODULE 
-DMEMORY_SIZE_BYTES=$(MEMORY_SIZE_BYTES)
+CXXFLAGS ?= -Werror -Wall -std=c++11 -DTVM_HOST_USE_GRAPH_EXECUTOR_MODULE 
-DTVM_WORKSPACE_SIZE_BYTES=$(TVM_WORKSPACE_SIZE_BYTES)
 LDFLAGS ?= -Werror -Wall
 
 # Codegen produces spurious lines like: int32_t arg2_code = 
((int32_t*)arg_type_ids)[(2)];
diff --git a/src/runtime/crt/host/main.cc b/src/runtime/crt/host/main.cc
index e9f6813f9b..0607d4b287 100644
--- a/src/runtime/crt/host/main.cc
+++ b/src/runtime/crt/host/main.cc
@@ -22,14 +22,12 @@
  * \brief main entry point for host subprocess-based CRT
  */
 #include <inttypes.h>
-#include <time.h>
 #include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/crt/aot_executor_module.h>
 #include <tvm/runtime/crt/logging.h>
 #include <tvm/runtime/crt/microtvm_rpc_server.h>
-#include <tvm/runtime/crt/page_allocator.h>
 #include <unistd.h>
 
-#include <chrono>
 #include <iostream>
 
 #include "crt_config.h"
@@ -38,10 +36,6 @@
 #include <tvm/runtime/crt/graph_executor_module.h>
 #endif
 
-#include <tvm/runtime/crt/aot_executor_module.h>
-
-using namespace std::chrono;
-
 extern "C" {
 
 ssize_t MicroTVMWriteFunc(void* context, const uint8_t* data, size_t 
num_bytes) {
@@ -50,70 +44,8 @@ ssize_t MicroTVMWriteFunc(void* context, const uint8_t* 
data, size_t num_bytes)
   fsync(STDOUT_FILENO);
   return to_return;
 }
-
-size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
-                                va_list args) {
-  return vsnprintf(out_buf, out_buf_size_bytes, fmt, args);
-}
-
-void TVMPlatformAbort(tvm_crt_error_t error_code) {
-  std::cerr << "TVMPlatformAbort: " << error_code << std::endl;
-  throw "Aborted";
 }
 
-MemoryManagerInterface* memory_manager;
-
-tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
-  return memory_manager->Allocate(memory_manager, num_bytes, dev, out_ptr);
-}
-
-tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
-  return memory_manager->Free(memory_manager, ptr, dev);
-}
-
-steady_clock::time_point g_microtvm_start_time;
-int g_microtvm_timer_running = 0;
-
-tvm_crt_error_t TVMPlatformTimerStart() {
-  if (g_microtvm_timer_running) {
-    std::cerr << "timer already running" << std::endl;
-    return kTvmErrorPlatformTimerBadState;
-  }
-  g_microtvm_start_time = std::chrono::steady_clock::now();
-  g_microtvm_timer_running = 1;
-  return kTvmErrorNoError;
-}
-
-tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
-  if (!g_microtvm_timer_running) {
-    std::cerr << "timer not running" << std::endl;
-    return kTvmErrorPlatformTimerBadState;
-  }
-  auto microtvm_stop_time = std::chrono::steady_clock::now();
-  std::chrono::microseconds time_span = 
std::chrono::duration_cast<std::chrono::microseconds>(
-      microtvm_stop_time - g_microtvm_start_time);
-  *elapsed_time_seconds = static_cast<double>(time_span.count()) / 1e6;
-  g_microtvm_timer_running = 0;
-  return kTvmErrorNoError;
-}
-
-static_assert(RAND_MAX >= (1 << 8), "RAND_MAX is smaller than acceptable");
-unsigned int random_seed = 0;
-tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
-  if (random_seed == 0) {
-    random_seed = (unsigned int)time(NULL);
-  }
-  for (size_t i = 0; i < num_bytes; ++i) {
-    int random = rand_r(&random_seed);
-    buffer[i] = (uint8_t)random;
-  }
-
-  return kTvmErrorNoError;
-}
-}
-
-uint8_t memory[MEMORY_SIZE_BYTES];
-
 static char** g_argv = NULL;
 
 int testonly_reset_server(TVMValue* args, int* type_codes, int num_args, 
TVMValue* out_ret_value,
@@ -125,13 +57,7 @@ int testonly_reset_server(TVMValue* args, int* type_codes, 
int num_args, TVMValu
 
 int main(int argc, char** argv) {
   g_argv = argv;
-  int status =
-      PageMemoryManagerCreate(&memory_manager, memory, sizeof(memory), 8 /* 
page_size_log2 */);
-  if (status != 0) {
-    fprintf(stderr, "error initiailizing memory manager\n");
-    return 2;
-  }
-
+  TVMPlatformInitialize();
   microtvm_rpc_server_t rpc_server = MicroTVMRpcServerInit(&MicroTVMWriteFunc, 
nullptr);
 
 #ifdef TVM_HOST_USE_GRAPH_EXECUTOR_MODULE
diff --git a/src/runtime/crt/host/microtvm_api_server.py 
b/src/runtime/crt/host/microtvm_api_server.py
index e5b82f96b0..57b7506b87 100644
--- a/src/runtime/crt/host/microtvm_api_server.py
+++ b/src/runtime/crt/host/microtvm_api_server.py
@@ -38,7 +38,7 @@ MODEL_LIBRARY_FORMAT_RELPATH = "model.tar"
 IS_TEMPLATE = not os.path.exists(os.path.join(PROJECT_DIR, 
MODEL_LIBRARY_FORMAT_RELPATH))
 
 # Used this size to pass most CRT tests in TVM.
-MEMORY_SIZE_BYTES = 2 * 1024 * 1024
+WORKSPACE_SIZE_BYTES = 2 * 1024 * 1024
 
 MAKEFILE_FILENAME = "Makefile"
 
@@ -67,11 +67,11 @@ class Handler(server.ProjectAPIHandler):
                     help="Run make with verbose output",
                 ),
                 server.ProjectOption(
-                    "memory_size_bytes",
+                    "workspace_size_bytes",
                     optional=["generate_project"],
                     type="int",
-                    default=MEMORY_SIZE_BYTES,
-                    help="Sets the value of MEMORY_SIZE_BYTES.",
+                    default=WORKSPACE_SIZE_BYTES,
+                    help="Sets the value of TVM_WORKSPACE_SIZE_BYTES.",
                 ),
             ],
         )
@@ -90,7 +90,7 @@ class Handler(server.ProjectAPIHandler):
     ):
         """Generate Makefile from template."""
         flags = {
-            "MEMORY_SIZE_BYTES": str(memory_size),
+            "TVM_WORKSPACE_SIZE_BYTES": str(memory_size),
         }
 
         regex = re.compile(r"([A-Z_]+) := (<[A-Z_]+>)")
@@ -106,6 +106,7 @@ class Handler(server.ProjectAPIHandler):
     def generate_project(self, model_library_format_path, standalone_crt_dir, 
project_dir, options):
         # Make project directory.
         project_dir.mkdir(parents=True)
+        current_dir = pathlib.Path(__file__).parent.absolute()
 
         # Copy ourselves to the generated project. TVM may perform further 
build steps on the generated project
         # by launching the copy.
@@ -135,25 +136,29 @@ class Handler(server.ProjectAPIHandler):
 
         # Populate Makefile
         self._populate_makefile(
-            pathlib.Path(__file__).parent / f"{MAKEFILE_FILENAME}.template",
+            current_dir / f"{MAKEFILE_FILENAME}.template",
             project_dir / MAKEFILE_FILENAME,
-            options.get("memory_size_bytes", MEMORY_SIZE_BYTES),
+            options.get("workspace_size_bytes", WORKSPACE_SIZE_BYTES),
         )
 
         # Populate crt-config.h
         crt_config_dir = project_dir / "crt_config"
         crt_config_dir.mkdir()
         shutil.copy2(
-            os.path.join(os.path.dirname(__file__), "crt_config-template.h"),
-            os.path.join(crt_config_dir, "crt_config.h"),
+            current_dir / "crt_config" / "crt_config-template.h",
+            crt_config_dir / "crt_config.h",
         )
 
         # Populate src/
-        src_dir = os.path.join(project_dir, "src")
-        os.mkdir(src_dir)
+        src_dir = project_dir / "src"
+        src_dir.mkdir()
         shutil.copy2(
-            os.path.join(os.path.dirname(__file__), "src", "main.cc"),
-            os.path.join(src_dir, "main.cc"),
+            current_dir / "src" / "main.cc",
+            src_dir / "main.cc",
+        )
+        shutil.copy2(
+            current_dir / "src" / "platform.cc",
+            src_dir / "platform.cc",
         )
 
     def build(self, options):
diff --git a/src/runtime/crt/host/main.cc b/src/runtime/crt/host/platform.cc
similarity index 56%
copy from src/runtime/crt/host/main.cc
copy to src/runtime/crt/host/platform.cc
index e9f6813f9b..f5af08a9be 100644
--- a/src/runtime/crt/host/main.cc
+++ b/src/runtime/crt/host/platform.cc
@@ -18,62 +18,56 @@
  */
 
 /*!
- * \file main.cc
- * \brief main entry point for host subprocess-based CRT
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
  */
+
+#include <dlpack/dlpack.h>
 #include <inttypes.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <time.h>
-#include <tvm/runtime/c_runtime_api.h>
-#include <tvm/runtime/crt/logging.h>
-#include <tvm/runtime/crt/microtvm_rpc_server.h>
+#include <tvm/runtime/crt/error_codes.h>
 #include <tvm/runtime/crt/page_allocator.h>
 #include <unistd.h>
 
 #include <chrono>
 #include <iostream>
 
-#include "crt_config.h"
-
-#ifdef TVM_HOST_USE_GRAPH_EXECUTOR_MODULE
-#include <tvm/runtime/crt/graph_executor_module.h>
-#endif
-
-#include <tvm/runtime/crt/aot_executor_module.h>
-
 using namespace std::chrono;
 
 extern "C" {
 
-ssize_t MicroTVMWriteFunc(void* context, const uint8_t* data, size_t 
num_bytes) {
-  ssize_t to_return = write(STDOUT_FILENO, data, num_bytes);
-  fflush(stdout);
-  fsync(STDOUT_FILENO);
-  return to_return;
-}
+uint8_t memory[TVM_WORKSPACE_SIZE_BYTES];
+MemoryManagerInterface* memory_manager;
 
-size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
-                                va_list args) {
-  return vsnprintf(out_buf, out_buf_size_bytes, fmt, args);
-}
+steady_clock::time_point g_microtvm_start_time;
+int g_microtvm_timer_running = 0;
 
+// Called when an internal error occurs and execution cannot continue.
 void TVMPlatformAbort(tvm_crt_error_t error_code) {
   std::cerr << "TVMPlatformAbort: " << error_code << std::endl;
   throw "Aborted";
 }
 
-MemoryManagerInterface* memory_manager;
+// Called by the microTVM RPC server to implement TVMLogf.
+size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
+                                va_list args) {
+  return vsprintf(out_buf, fmt, args);
+}
 
+// Allocate memory for use by TVM.
 tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
   return memory_manager->Allocate(memory_manager, num_bytes, dev, out_ptr);
 }
 
+// Free memory used by TVM.
 tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
   return memory_manager->Free(memory_manager, ptr, dev);
 }
 
-steady_clock::time_point g_microtvm_start_time;
-int g_microtvm_timer_running = 0;
-
+// Start a device timer.
 tvm_crt_error_t TVMPlatformTimerStart() {
   if (g_microtvm_timer_running) {
     std::cerr << "timer already running" << std::endl;
@@ -84,6 +78,7 @@ tvm_crt_error_t TVMPlatformTimerStart() {
   return kTvmErrorNoError;
 }
 
+// Stop the running device timer and get the elapsed time (in microseconds).
 tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) {
   if (!g_microtvm_timer_running) {
     std::cerr << "timer not running" << std::endl;
@@ -97,8 +92,15 @@ tvm_crt_error_t TVMPlatformTimerStop(double* 
elapsed_time_seconds) {
   return kTvmErrorNoError;
 }
 
+// Platform-specific before measurement call.
+tvm_crt_error_t TVMPlatformBeforeMeasurement() { return kTvmErrorNoError; }
+
+// Platform-specific after measurement call.
+tvm_crt_error_t TVMPlatformAfterMeasurement() { return kTvmErrorNoError; }
+
 static_assert(RAND_MAX >= (1 << 8), "RAND_MAX is smaller than acceptable");
 unsigned int random_seed = 0;
+// Fill a buffer with random data.
 tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
   if (random_seed == 0) {
     random_seed = (unsigned int)time(NULL);
@@ -107,74 +109,18 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* 
buffer, size_t num_bytes) {
     int random = rand_r(&random_seed);
     buffer[i] = (uint8_t)random;
   }
-
   return kTvmErrorNoError;
 }
-}
-
-uint8_t memory[MEMORY_SIZE_BYTES];
-
-static char** g_argv = NULL;
-
-int testonly_reset_server(TVMValue* args, int* type_codes, int num_args, 
TVMValue* out_ret_value,
-                          int* out_ret_tcode, void* resource_handle) {
-  execvp(g_argv[0], g_argv);
-  perror("microTVM runtime: error restarting");
-  return -1;
-}
 
-int main(int argc, char** argv) {
-  g_argv = argv;
+// Initialize TVM inference.
+tvm_crt_error_t TVMPlatformInitialize() {
   int status =
       PageMemoryManagerCreate(&memory_manager, memory, sizeof(memory), 8 /* 
page_size_log2 */);
   if (status != 0) {
     fprintf(stderr, "error initiailizing memory manager\n");
-    return 2;
-  }
-
-  microtvm_rpc_server_t rpc_server = MicroTVMRpcServerInit(&MicroTVMWriteFunc, 
nullptr);
-
-#ifdef TVM_HOST_USE_GRAPH_EXECUTOR_MODULE
-  CHECK_EQ(TVMGraphExecutorModule_Register(), kTvmErrorNoError,
-           "failed to register GraphExecutor TVMModule");
-#endif
-
-  int error = TVMFuncRegisterGlobal("tvm.testing.reset_server",
-                                    (TVMFunctionHandle)&testonly_reset_server, 
0);
-  if (error) {
-    fprintf(
-        stderr,
-        "microTVM runtime: internal error (error#: %x) registering global 
packedfunc; exiting\n",
-        error);
-    return 2;
+    return kTvmErrorPlatformMemoryManagerInitialized;
   }
-
-  setbuf(stdin, NULL);
-  setbuf(stdout, NULL);
-
-  for (;;) {
-    uint8_t c;
-    int ret_code = read(STDIN_FILENO, &c, 1);
-    if (ret_code < 0) {
-      perror("microTVM runtime: read failed");
-      return 2;
-    } else if (ret_code == 0) {
-      fprintf(stderr, "microTVM runtime: 0-length read, exiting!\n");
-      return 2;
-    }
-    uint8_t* cursor = &c;
-    size_t bytes_to_process = 1;
-    while (bytes_to_process > 0) {
-      tvm_crt_error_t err = MicroTVMRpcServerLoop(rpc_server, &cursor, 
&bytes_to_process);
-      if (err == kTvmErrorPlatformShutdown) {
-        break;
-      } else if (err != kTvmErrorNoError) {
-        char buf[1024];
-        snprintf(buf, sizeof(buf), "microTVM runtime: MicroTVMRpcServerLoop 
error: %08x", err);
-        perror(buf);
-        return 2;
-      }
-    }
-  }
-  return 0;
+  return kTvmErrorNoError;
 }
+
+}  // extern C
diff --git a/src/runtime/crt/platform-template.c 
b/src/runtime/crt/platform-template.c
new file mode 100644
index 0000000000..b93fd1459b
--- /dev/null
+++ b/src/runtime/crt/platform-template.c
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/*!
+ * \brief Implementation of TVMPlatform functions in tvm/runtime/crt/platform.h
+ */
+
+#include <dlpack/dlpack.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tvm/runtime/crt/error_codes.h>
+#include <tvm/runtime/crt/page_allocator.h>
+
+uint8_t memory[TVM_WORKSPACE_SIZE_BYTES];
+MemoryManagerInterface* memory_manager;
+
+// Called when an internal error occurs and execution cannot continue.
+void TVMPlatformAbort(tvm_crt_error_t error_code) { exit(1); }
+
+// Called by the microTVM RPC server to implement TVMLogf.
+size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, 
const char* fmt,
+                                va_list args) {
+  return vsprintf(out_buf, fmt, args);
+}
+
+// Allocate memory for use by TVM.
+tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, 
void** out_ptr) {
+  return memory_manager->Allocate(memory_manager, num_bytes, dev, out_ptr);
+}
+
+// Free memory used by TVM.
+tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) {
+  return memory_manager->Free(memory_manager, ptr, dev);
+}
+
+// Start a device timer.
+tvm_crt_error_t TVMPlatformTimerStart() { return kTvmErrorNoError; }
+
+// Stop the running device timer and get the elapsed time (in microseconds).
+tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { return 
kTvmErrorNoError; }
+
+// Platform-specific before measurement call.
+tvm_crt_error_t TVMPlatformBeforeMeasurement() { return kTvmErrorNoError; }
+
+// Platform-specific after measurement call.
+tvm_crt_error_t TVMPlatformAfterMeasurement() { return kTvmErrorNoError; }
+
+// Fill a buffer with random data.
+tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) {
+  return kTvmErrorNoError;
+}
+
+// Initialize TVM inference.
+tvm_crt_error_t TVMPlatformInitialize() {
+  int status =
+      PageMemoryManagerCreate(&memory_manager, memory, sizeof(memory), 8 /* 
page_size_log2 */);
+  if (status != 0) {
+    fprintf(stderr, "error initiailizing memory manager\n");
+    return kTvmErrorPlatformMemoryManagerInitialized;
+  }
+  return kTvmErrorNoError;
+}
diff --git a/tests/micro/arduino/test_arduino_workflow.py 
b/tests/micro/arduino/test_arduino_workflow.py
index 42874ad6c3..73cdd9b85d 100644
--- a/tests/micro/arduino/test_arduino_workflow.py
+++ b/tests/micro/arduino/test_arduino_workflow.py
@@ -71,7 +71,7 @@ def test_project_folder_structure(project_dir, project):
 
     source_dir = project_dir / "src"
     assert _get_directory_elements(source_dir) == set(
-        ["model", "standalone_crt", "model.c", "model.h"]
+        ["model", "standalone_crt", "platform.c", "platform.h"]
     )
 
 
@@ -82,15 +82,15 @@ def test_project_model_integrity(project_dir, project):
     )
 
 
-def test_model_header_templating(project_dir, project):
-    # Ensure model.h was templated with correct WORKSPACE_SIZE
-    with (project_dir / "src" / "model.h").open() as f:
-        model_h = f.read()
-        workspace_size_defs = re.findall(r"\#define WORKSPACE_SIZE ([0-9]*)", 
model_h)
+def test_model_platform_templating(project_dir, project):
+    # Ensure platform.c was templated with correct TVM_WORKSPACE_SIZE_BYTES
+    with (project_dir / "src" / "platform.c").open() as f:
+        platform_c = f.read()
+        workspace_size_defs = re.findall(r"\#define TVM_WORKSPACE_SIZE_BYTES 
([0-9]*)", platform_c)
         assert workspace_size_defs
         assert len(workspace_size_defs) == 1
 
-        # Make sure the WORKSPACE_SIZE we define is a reasonable size. We 
don't want
+        # Make sure the TVM_WORKSPACE_SIZE_BYTES we define is a reasonable 
size. We don't want
         # to set an exact value, as this test shouldn't break if an 
improvement to
         # TVM causes the amount of memory needed to decrease.
         workspace_size = int(workspace_size_defs[0])
diff --git a/tests/micro/arduino/testdata/project.ino 
b/tests/micro/arduino/testdata/project.ino
index ebd1c5e0e6..d7ef155b33 100644
--- a/tests/micro/arduino/testdata/project.ino
+++ b/tests/micro/arduino/testdata/project.ino
@@ -17,11 +17,12 @@
  * under the License.
  */
 
-#include "src/model.h"
+#include "src/platform.h"
 #include "src/data/yes.c"
 #include "src/data/no.c"
 #include "src/data/unknown.c"
 #include "src/data/silence.c"
+#include "src/standalone_crt/include/tvm/runtime/crt/platform.h"
 
 void performInference(int8_t input_data[1960], char *data_name) {
   int8_t output_data[4];
@@ -41,7 +42,7 @@ void performInference(int8_t input_data[1960], char 
*data_name) {
 }
 
 void setup() {
-  TVMInitialize();
+  TVMPlatformInitialize();
   Serial.begin(115200);
 }
 
diff --git a/tests/micro/zephyr/test_zephyr.py 
b/tests/micro/zephyr/test_zephyr.py
index 59c4cab881..79e4f46e0f 100644
--- a/tests/micro/zephyr/test_zephyr.py
+++ b/tests/micro/zephyr/test_zephyr.py
@@ -608,5 +608,41 @@ def 
test_schedule_build_with_cmsis_dependency(workspace_dir, board, microtvm_deb
     assert "CMSIS-NN/Include" in cmake_content
 
 
[email protected]_micro
+def test_debugging_enabled(workspace_dir):
+    """Test debugging enabled for LED. `verbose=True` in project option enables
+    debugging. For this test a physical board(nucleo_l4r5zi) is used instead of
+    QEMU since LED config is not available on QEMU.
+    """
+    board = "nucleo_l4r5zi"
+    project_options = {
+        "project_type": "host_driven",
+        "board": board,
+        "verbose": True,
+    }
+    shape = (10,)
+    dtype = "int8"
+    x = relay.var("x", relay.TensorType(shape=shape, dtype=dtype))
+    xx = relay.multiply(x, x)
+    z = relay.add(xx, relay.const(np.ones(shape=shape, dtype=dtype)))
+    func = relay.Function([x], z)
+    ir_mod = tvm.IRModule.from_expr(func)
+
+    runtime = Runtime("crt", {"system-lib": True})
+    executor = Executor("aot")
+    target = tvm.micro.testing.get_target("zephyr", board)
+
+    with tvm.transform.PassContext(opt_level=3, 
config={"tir.disable_vectorize": True}):
+        mod = tvm.relay.build(ir_mod, target=target, runtime=runtime, 
executor=executor)
+
+    project = tvm.micro.generate_project(
+        str(utils.TEMPLATE_PROJECT_DIR),
+        mod,
+        workspace_dir / "project",
+        project_options,
+    )
+    project.build()
+
+
 if __name__ == "__main__":
     tvm.testing.main()
diff --git a/tests/micro/zephyr/test_zephyr_aot_exec_standalone.py 
b/tests/micro/zephyr/test_zephyr_aot_exec_standalone.py
index 16c1f9e308..8c6bc272f0 100644
--- a/tests/micro/zephyr/test_zephyr_aot_exec_standalone.py
+++ b/tests/micro/zephyr/test_zephyr_aot_exec_standalone.py
@@ -63,7 +63,9 @@ def test_tflite(workspace_dir, board, microtvm_debug, 
serial_number):
         "aot", {"unpacked-api": True, "interface-api": "c", 
"workspace-byte-alignment": 4}
     )
     runtime = Runtime("crt")
-    with tvm.transform.PassContext(opt_level=3, 
config={"tir.disable_vectorize": True}):
+    with tvm.transform.PassContext(
+        opt_level=3, config={"tir.disable_vectorize": True, "tir.usmp.enable": 
True}
+    ):
         lowered = relay.build(relay_mod, target, params=params, 
runtime=runtime, executor=executor)
 
     sample_url = 
"https://github.com/tlc-pack/web-data/raw/967fc387dadb272c5a7f8c3461d34c060100dbf1/testdata/microTVM/data/keyword_spotting_int8_6.pyc.npy";
diff --git a/tests/micro/zephyr/utils.py b/tests/micro/zephyr/utils.py
index 26f9d6a10e..fdd873c8e8 100644
--- a/tests/micro/zephyr/utils.py
+++ b/tests/micro/zephyr/utils.py
@@ -153,8 +153,8 @@ def generate_project(
     with tempfile.NamedTemporaryFile() as tar_temp_file:
         with tarfile.open(tar_temp_file.name, "w:gz") as tf:
             with tempfile.TemporaryDirectory() as tar_temp_dir:
-                model_files_path = os.path.join(tar_temp_dir, "include")
-                os.mkdir(model_files_path)
+                model_files_path = pathlib.Path(tar_temp_dir) / "include"
+                model_files_path.mkdir(parents=True)
                 if load_cmsis:
                     loadCMSIS(model_files_path)
                     tf.add(
@@ -174,9 +174,9 @@ def generate_project(
                 )
                 tf.add(header_path, arcname=os.path.relpath(header_path, 
tar_temp_dir))
 
-            create_header_file("input_data", sample, "include", tf)
+            create_header_file("input_data", sample, "include/tvm", tf)
             create_header_file(
-                "output_data", np.zeros(shape=output_shape, 
dtype=output_type), "include", tf
+                "output_data", np.zeros(shape=output_shape, 
dtype=output_type), "include/tvm", tf
             )
 
         project, project_dir = build_project(
diff --git a/tests/python/unittest/test_micro_model_library_format.py 
b/tests/python/unittest/test_micro_model_library_format.py
index 734404fb34..6f79723de4 100644
--- a/tests/python/unittest/test_micro_model_library_format.py
+++ b/tests/python/unittest/test_micro_model_library_format.py
@@ -715,5 +715,30 @@ def test_output_names_many():
     }
 
 
[email protected]_micro
+def test_template_files():
+    """Check template files in generated model library format."""
+    mod = get_conv2d_relay_module()
+
+    executor = Executor("aot", {"unpacked-api": True, "interface-api": "c"})
+    runtime = Runtime("crt")
+    target = tvm.target.target.micro("host")
+
+    with tvm.transform.PassContext(opt_level=3, 
config={"tir.disable_vectorize": True}):
+        factory = tvm.relay.build(mod, target, runtime=runtime, 
executor=executor, mod_name="mod")
+
+    temp_dir = utils.tempdir()
+    mlf_tar_path = temp_dir / "lib.tar"
+    micro.export_model_library_format(factory, mlf_tar_path)
+
+    tf = tarfile.open(mlf_tar_path)
+    extract_dir = temp_dir / "extract"
+    os.mkdir(extract_dir)
+    tf.extractall(extract_dir)
+
+    assert (extract_dir / "templates" / "crt_config.h.template").is_file()
+    assert (extract_dir / "templates" / "platform.c.template").is_file()
+
+
 if __name__ == "__main__":
     tvm.testing.main()

Reply via email to