[uartdpi] Close devices at simulation shutdown

Close the pseudo-terminal devices at simulation shutdown, ensuring that
all buffers get properly flushed.

Signed-off-by: Philipp Wagner <phw@lowrisc.org>
diff --git a/hw/dv/dpi/uartdpi/uartdpi.c b/hw/dv/dpi/uartdpi/uartdpi.c
index eecd0c1..6e85aa4 100644
--- a/hw/dv/dpi/uartdpi/uartdpi.c
+++ b/hw/dv/dpi/uartdpi/uartdpi.c
@@ -46,6 +46,18 @@
   return (void *)ctx;
 }
 
+void uartdpi_close(void *ctx_void) {
+  struct uartdpi_ctx *ctx = (struct uartdpi_ctx *)ctx_void;
+  if (!ctx) {
+    return;
+  }
+
+  close(ctx->host);
+  close(ctx->device);
+
+  free(ctx);
+}
+
 int uartdpi_can_read(void *ctx_void) {
   struct uartdpi_ctx *ctx = (struct uartdpi_ctx *)ctx_void;
 
diff --git a/hw/dv/dpi/uartdpi/uartdpi.h b/hw/dv/dpi/uartdpi/uartdpi.h
index e4c6473..1203fee 100644
--- a/hw/dv/dpi/uartdpi/uartdpi.h
+++ b/hw/dv/dpi/uartdpi/uartdpi.h
@@ -14,6 +14,7 @@
 };
 
 void *uartdpi_create(const char *name);
+void uartdpi_close(void *ctx_void);
 int uartdpi_can_read(void *ctx_void);
 char uartdpi_read(void *ctx_void);
 void uartdpi_write(void *ctx_void, char c);
diff --git a/hw/dv/dpi/uartdpi/uartdpi.sv b/hw/dv/dpi/uartdpi/uartdpi.sv
index 0694753..6dcf799 100644
--- a/hw/dv/dpi/uartdpi/uartdpi.sv
+++ b/hw/dv/dpi/uartdpi/uartdpi.sv
@@ -20,6 +20,9 @@
     chandle uartdpi_create(input string name);
 
   import "DPI-C" function
+    void uartdpi_close(input chandle ctx);
+
+  import "DPI-C" function
     byte uartdpi_read(input chandle ctx);
 
   import "DPI-C" function
@@ -38,6 +41,11 @@
     file_handle = $fopen(file_name, "w");
   end
 
+  final begin
+    uartdpi_close(ctx);
+    ctx = 0;
+  end
+
   // TX
   reg txactive;
   int  txcount;