You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2022/07/25 20:27:32 UTC

[GitHub] [tvm] areusch commented on a diff in pull request #12125: Zephyr fvp support

areusch commented on code in PR #12125:
URL: https://github.com/apache/tvm/pull/12125#discussion_r929268089


##########
apps/microtvm/zephyr/template_project/microtvm_api_server.py:
##########
@@ -885,5 +945,186 @@ def _wait_for_qemu(self):
             raise ValueError(f"{item} not expected.")
 
 
+class ZephyrFvpMakeResult(enum.Enum):
+    FVP_STARTED = "fvp_started"
+    FVP_INIT = "fvp_initialized"
+    MAKE_FAILED = "make_failed"
+    EOF = "eof"
+
+
+class BlockingStream:
+    """Reimplementation of Stream class from Iris with blocking semantics."""
+
+    def __init__(self):
+        self.q = queue.Queue()
+        self.unread = None
+
+    def read(self, n=-1, timeout_sec=None):
+        assert (
+            n != -1
+        ), "expect firmware to open stdin using raw mode, and therefore expect sized read requests"
+
+        data = b""
+        if self.unread:
+            data = data + self.unread
+            self.unread = None
+
+        while len(data) < n:
+            try:
+                # When there is some data to return, fetch as much as possible, then return what we can.
+                # When there is no data yet to return, block.
+                data += self.q.get(block=not len(data), timeout=timeout_sec)
+            except queue.Empty:
+                break
+
+        if len(data) > n:
+            self.unread = data[n:]
+            data = data[:n]
+
+        return data
+
+    readline = read
+
+    def write(self, data):
+        self.q.put(data)
+
+
+class ZephyrFvpTransport:
+    """A transport class that communicates with the ARM FVP via Iris server."""
+
+    def __init__(self, options):
+        self.options = options
+        self.proc = None
+        self._queue = queue.Queue()
+
+        self._import_iris()
+
+    def _import_iris(self):
+        # Location as seen in the FVP_Corstone_SSE-300_11.15_24 tar.
+        iris_lib_path = (
+            pathlib.Path(self.options["arm_fvp_path"]).parent.parent.parent
+            / "Iris"
+            / "Python"
+            / "iris"
+        )
+
+        sys.path.insert(0, str(iris_lib_path.parent))
+        try:
+            import iris.NetworkModelInitializer
+        finally:
+            sys.path.pop(0)
+
+        self._iris_lib = iris
+
+        def _convertStringToU64Array(strValue):
+            numBytes = len(strValue)
+            if numBytes == 0:
+                return []
+
+            numU64 = (numBytes + 7) // 8
+            # Extend the string ending with '\0', so that the string length is multiple of 8.
+            # E.g. 'hello' is extended to: 'hello'+\0\0\0
+            strExt = strValue.ljust(8 * numU64, b"\0")
+            # Convert the string to a list of uint64_t in little endian
+            return struct.unpack("<{}Q".format(numU64), strExt)
+
+        iris.iris.convertStringToU64Array = _convertStringToU64Array
+
+    def open(self):
+        args = ["ninja"]
+        if self.options.get("verbose"):
+            args.append("-v")
+        args.append("run")
+        env = dict(os.environ)
+        env["FVP_BIN_PATH"] = str(pathlib.Path(self.options["arm_fvp_path"]).parent)
+        env["ARMFVP_BIN_PATH"] = str(API_SERVER_DIR / "fvp-hack")
+        self.proc = subprocess.Popen(
+            args,
+            cwd=BUILD_DIR,
+            env=env,
+            stdout=subprocess.PIPE,
+        )
+        threading.Thread(target=self._fvp_check_stdout, daemon=True).start()
+
+        self.iris_port = self._wait_for_fvp()
+        _LOG.info("IRIS started on port %d", self.iris_port)
+        NetworkModelInitializer = self._iris_lib.NetworkModelInitializer.NetworkModelInitializer
+        self._model_init = NetworkModelInitializer(
+            host="localhost", port=self.iris_port, timeout_in_ms=1000
+        )
+        self._model = self._model_init.start()
+        self._target = self._model.get_target("component.FVP_MPS3_Corstone_SSE_300.cpu0")
+
+        self._target.handle_semihost_io()
+        self._target._stdout = BlockingStream()
+        self._target._stdin = BlockingStream()
+        self._model.run(blocking=False, timeout=100)
+        self._wait_for_semihost_init()
+
+        return server.TransportTimeouts(
+            session_start_retry_timeout_sec=2.0,
+            session_start_timeout_sec=10.0,
+            session_established_timeout_sec=10.0,
+        )
+
+    def _fvp_check_stdout(self):
+        for line in self.proc.stdout:
+            line = str(line, "utf-8")
+            m = re.match(r"Iris server started listening to port ([0-9]+)\n", line)
+            n = re.match("microTVM Zephyr runtime - running", line)
+            if m:
+                self._queue.put((ZephyrFvpMakeResult.FVP_STARTED, int(m.group(1))))
+            elif n:
+                self._queue.put((ZephyrFvpMakeResult.FVP_INIT, None))
+            else:
+                line = re.sub("[^a-zA-Z0-9 \n]", "", line)
+                pattern = r"recipe for target (\w*) failed"
+                if re.search(pattern, line, re.IGNORECASE):
+                    self._queue.put((ZephyrFvpMakeResult.MAKE_FAILED, None))
+
+        self._queue.put((ZephyrFvpMakeResult.EOF, None))
+
+    def _wait_for_fvp(self):
+        while True:
+            try:
+                item = self._queue.get(timeout=120)
+            except Exception:
+                raise TimeoutError("FVP setup timeout.")
+
+            if item[0] == ZephyrFvpMakeResult.FVP_STARTED:
+                return item[1]
+
+            if item[0] in [ZephyrFvpMakeResult.MAKE_FAILED, ZephyrFvpMakeResult.EOF]:
+                raise RuntimeError("FVP setup failed.")
+
+            raise ValueError(f"{item} not expected.")
+
+    def _wait_for_semihost_init(self):
+        while True:
+            try:
+                item = self._queue.get(timeout=120)
+            except Exception:
+                raise TimeoutError("semihost init timeout.")
+
+            if item[0] == ZephyrFvpMakeResult.FVP_INIT:
+                return
+
+            raise ValueError(f"{item} not expected.")

Review Comment:
   could you add some context to these two errors? i.e. what is the API server doing right now and what does it expect?



##########
apps/microtvm/zephyr/template_project/microtvm_api_server.py:
##########
@@ -885,5 +945,186 @@ def _wait_for_qemu(self):
             raise ValueError(f"{item} not expected.")
 
 
+class ZephyrFvpMakeResult(enum.Enum):
+    FVP_STARTED = "fvp_started"
+    FVP_INIT = "fvp_initialized"

Review Comment:
   how about we call this MICROTVM_API_SERVER_INIT?



##########
apps/microtvm/zephyr/template_project/src/host_driven/main.c:
##########
@@ -218,11 +303,42 @@ void uart_irq_cb(const struct device* dev, void* user_data) {
   }
 }
 
+#ifdef FVP

Review Comment:
   is this code needed any longer?



##########
apps/microtvm/zephyr/template_project/src/host_driven/main.c:
##########
@@ -64,8 +67,89 @@ static size_t g_num_bytes_requested = 0;
 static size_t g_num_bytes_written = 0;
 static size_t g_num_bytes_in_rx_buffer = 0;
 
+#ifdef FVP

Review Comment:
   what if we break this out into a separate file and modify CMakeLists.txt to include it (we can still e.g.
   ```
   #ifdef FVP
   #include "semihost.h"
   #endif
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@tvm.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org