[dv,sw] C messaging flow: part 2 (SV side)

- added sw_msg_monitor_if interface that detects writes to special address
  in RAM
- interface is written in pure SV code and is likely to work with Verilator efforts as
  well
- interface allows passage of handle to the dynamic side for runtime
manipulation
diff --git a/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.core b/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.core
new file mode 100644
index 0000000..462ef46
--- /dev/null
+++ b/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.core
@@ -0,0 +1,17 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:sw_msg_monitor_if"
+description: "SW msg monitor interface (convert SW msg prints into SV)"
+
+filesets:
+  files_dv:
+    files:
+      - sw_msg_monitor_if.sv
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.sv b/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.sv
new file mode 100644
index 0000000..23de18f
--- /dev/null
+++ b/hw/dv/sv/sw_msg_monitor_if/sw_msg_monitor_if.sv
@@ -0,0 +1,508 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// shortcuts for use in switching # of args to insert in formatted string
+`define _0_ARGS(a)
+`define _1_ARGS(a)      , a[0]
+`define _2_ARGS(a)      `_1_ARGS(a), a[1]
+`define _3_ARGS(a)      `_2_ARGS(a), a[2]
+`define _4_ARGS(a)      `_3_ARGS(a), a[3]
+`define _5_ARGS(a)      `_4_ARGS(a), a[4]
+`define _6_ARGS(a)      `_5_ARGS(a), a[5]
+`define _7_ARGS(a)      `_6_ARGS(a), a[6]
+`define _8_ARGS(a)      `_7_ARGS(a), a[7]
+`define _9_ARGS(a)      `_8_ARGS(a), a[8]
+`define _10_ARGS(a)     `_9_ARGS(a), a[9]
+`define _11_ARGS(a)     `_10_ARGS(a), a[10]
+`define _12_ARGS(a)     `_11_ARGS(a), a[11]
+`define _13_ARGS(a)     `_12_ARGS(a), a[12]
+`define _14_ARGS(a)     `_13_ARGS(a), a[13]
+`define _15_ARGS(a)     `_14_ARGS(a), a[14]
+`define _16_ARGS(a)     `_15_ARGS(a), a[15]
+`define _17_ARGS(a)     `_16_ARGS(a), a[16]
+`define _18_ARGS(a)     `_17_ARGS(a), a[17]
+`define _19_ARGS(a)     `_18_ARGS(a), a[18]
+`define _20_ARGS(a)     `_19_ARGS(a), a[19]
+`define _21_ARGS(a)     `_20_ARGS(a), a[20]
+`define _22_ARGS(a)     `_21_ARGS(a), a[21]
+`define _23_ARGS(a)     `_22_ARGS(a), a[22]
+`define _24_ARGS(a)     `_23_ARGS(a), a[23]
+`define _25_ARGS(a)     `_24_ARGS(a), a[24]
+`define _26_ARGS(a)     `_25_ARGS(a), a[25]
+`define _27_ARGS(a)     `_26_ARGS(a), a[26]
+`define _28_ARGS(a)     `_27_ARGS(a), a[27]
+`define _29_ARGS(a)     `_28_ARGS(a), a[28]
+`define _30_ARGS(a)     `_29_ARGS(a), a[29]
+`define _31_ARGS(a)     `_30_ARGS(a), a[30]
+`define _32_ARGS(a)     `_31_ARGS(a), a[31]
+`define _ADD_ARGS(a, n) `_``n``_ARGS(a)
+`define NARGS_CASE(n)   n: msg = $sformatf(msg `_ADD_ARGS(sw_msg.arg, n));
+
+interface sw_msg_monitor_if #(
+  // width of the data bus
+  parameter int unsigned DATA_WIDTH = 32
+) (
+  input logic                   clk,        // clock
+  input logic                   rst_n,      // active low reset
+  input logic                   valid,      // qualification for addr_data
+  input logic [DATA_WIDTH-1:0]  addr_data,  // addr/data written to sw_msg_addr
+  output logic [DATA_WIDTH-1:0] sw_msg_addr // used by external logic to qualify valid
+);
+
+  // types
+  // struct to hold sw msg data file and name
+  typedef struct {
+    string name;
+    string file;
+  } sw_msg_data_file_t;
+
+  // typedef addr / data values
+  typedef bit [DATA_WIDTH-1:0] addr_data_t;
+
+  // struct to hold the complete msg data
+  typedef struct {
+    string      msg_type;   // info, warning, error or fatal
+    string      verbosity;  // none, low, medium, high, full, debug
+    string      name;       // sw name
+    string      file;       // name of C file
+    int         line;       // line no
+    string      header;     // custom header (else its [name](file:line))
+    int         nargs;      // no of args
+    addr_data_t arg[];      // actual arg values
+    string      format;     // formatted string
+  } sw_msg_t;
+
+  // index of elements parsed from each line of msg data file
+  typedef enum {
+    AddrIndex,
+    MsgTypeIndex,
+    VerbosityIndex,
+    FileIndex,
+    LineIndex,
+    NArgsIndex,
+    FormatIndex,
+    ArgsIndex
+  } sw_msg_fields_index_e;
+
+  // msg scheme
+  typedef enum {
+    MsgSchemeNone,        // addr\msg (msg might contain additional metadata)
+    MsgSchmeCustomHeader, // addr\header\nargs\format
+    MsgSchemeDv           // addr\msg_type\verbosity\file\line\nargs\format
+  } msg_scheme_e;
+
+  // signal indicating all initializations are done (this is set by calling ready() function)
+  bit _ready;
+
+  // single char string delimiter used to segregate the msg data fields
+  // by default, this is set to '\' which is 5c in hex
+  byte delimiter = 8'h5c;
+  string delimiter_str = "";
+
+  // q of input file sources
+  sw_msg_data_file_t sw_msg_data_files[$];
+
+  // hash of msg with addr key
+  sw_msg_t sw_msgs[addr_data_t];
+
+  // q of values obtained from the bus
+  addr_data_t addr_data_q[$];
+
+  /****************************/
+  /* Initialization functions */
+  /****************************/
+  // function to set the delimiter value
+  function automatic void set_delimiter(byte value);
+    if (_ready) msg_fatal(.msg("this function cannot be called after calling ready()"));
+    delimiter = value;
+    // update the string version
+    // if delimiter is '\' (8'h5c) then the string version
+    // needs to be empty since SV treats '\' as null
+    delimiter_str.putc(0, delimiter);
+    delimiter_str = (delimiter == 8'h5c) ? "" : delimiter_str;
+  endfunction
+
+  // function to add the msg dat files
+  function automatic void add_sw_msg_data_files(string img_name, string img_file);
+    if (_ready) msg_fatal(.msg("this function cannot be called after calling ready()"));
+    sw_msg_data_files.push_back({name:img_name, file:img_file});
+  endfunction
+
+  // signal to indicate all monitor is good to go - all initializations are done
+  function automatic void ready();
+    _ready = 1'b1;
+  endfunction
+
+  /********************/
+  /* Monitoring logic */
+  /********************/
+  initial begin
+    wait(_ready);
+    if (parse_sw_msg_data_files()) begin
+      fork
+        get_addr_data_from_bus();
+        construct_msg_and_print();
+      join_none
+    end
+  end
+
+  /******************/
+  /* helper methods */
+  /******************/
+  // function that parses the msg data file
+  // returns 1 if msg data is avaiable, else false
+  function automatic bit parse_sw_msg_data_files();
+    foreach (sw_msg_data_files[i]) begin
+      int fd;
+      fd = $fopen(sw_msg_data_files[i].file, "r");
+      if (!fd) continue;
+      else begin
+        addr_data_t addr;
+
+        while (!$feof(fd)) begin
+          string       line;
+          string       fields[$];
+          int          merge_fields_idx_start = 0;
+          int          merge_fields_idx_end = 0;
+          msg_scheme_e scheme;
+
+          // read line and split into fields based on delimiter
+          void'($fgets(line, fd));
+          if (line inside {"", "\n", "\r"}) continue;
+          split_msg_data_line_to_fields(line, fields);
+
+          // print fields for debug
+          foreach (fields[i]) begin
+            msg_info(.verbosity("d"), .msg($sformatf("fields[%0d] = %0s", i, fields[i])));
+          end
+
+          // get addr (first field)
+          addr = fields[AddrIndex].atohex();
+          if (sw_msgs.exists(addr)) begin
+            msg_warning(.msg($sformatf("msg entry for addr %0x already exists:\n%p",
+                                       addr, sw_msgs[addr])));
+          end
+
+          // detect msg scheme based on fields size and values
+          // if > 7 fields AND fields[MsgTypeIndex] is either "i", "w", "e" or "f"
+          // then its DV scheme, otherwise it is something else
+          if (fields.size() >= 7) scheme = MsgSchemeDv;
+          // Note: If user adds null termination in the msg that is not supported
+          if (fields[MsgTypeIndex].tolower() inside {"i", "info"}) begin
+            sw_msgs[addr].msg_type = "i";
+          end else if (fields[MsgTypeIndex].tolower() inside {"w", "warn", "warning"}) begin
+            sw_msgs[addr].msg_type = "w";
+          end else if (fields[MsgTypeIndex].tolower() inside {"e", "err", "error"}) begin
+            sw_msgs[addr].msg_type = "e";
+          end else if (fields[MsgTypeIndex].tolower() inside {"f", "fatal"}) begin
+            sw_msgs[addr].msg_type = "f";
+          end else begin
+            // if fields size >= 4, its a msg with custom header
+            // if fields size < 4, there no scheme detected
+            if (fields.size() >= 4) scheme = MsgSchmeCustomHeader;
+            else                    scheme = MsgSchemeNone;
+          end
+
+          case (scheme)
+            MsgSchemeNone: begin
+              sw_msgs[addr].msg_type = "i";
+              sw_msgs[addr].verbosity = "n";
+              sw_msgs[addr].file = "";
+              sw_msgs[addr].line = 0;
+              sw_msgs[addr].nargs = 0;
+              sw_msgs[addr].format =  fields[1];
+              sw_msgs[addr].name = sw_msg_data_files[i].name;
+              merge_fields_idx_start = 2;
+            end
+            MsgSchmeCustomHeader: begin
+              sw_msgs[addr].verbosity = "n";
+              sw_msgs[addr].file = "";
+              sw_msgs[addr].line = 0;
+              sw_msgs[addr].header = fields[1];
+              sw_msgs[addr].nargs = fields[2].atoi();
+              sw_msgs[addr].arg = new[sw_msgs[addr].nargs];
+              sw_msgs[addr].format =  fields[3];
+              sw_msgs[addr].name = sw_msg_data_files[i].name;
+              merge_fields_idx_start = 4;
+            end
+            MsgSchemeDv: begin
+              // 7 entries: addr, type, verbosity, file, line, nargs, format
+              sw_msgs[addr].verbosity = fields[VerbosityIndex];
+              sw_msgs[addr].file = fields[FileIndex];
+              sw_msgs[addr].line = fields[LineIndex].atoi();
+              sw_msgs[addr].nargs = fields[NArgsIndex].atoi();
+              sw_msgs[addr].arg = new[sw_msgs[addr].nargs];
+              sw_msgs[addr].format = fields[FormatIndex];
+              sw_msgs[addr].name = sw_msg_data_files[i].name;
+              merge_fields_idx_start = 7;
+            end
+          endcase
+          // its possible that user might have added the delimiter in the msg itself
+          // in that case append the remaining ones back into formatted string
+          for (int i = merge_fields_idx_start; i < fields.size(); i++) begin
+            sw_msgs[addr].format = {sw_msgs[addr].format, delimiter_str, fields[i]};
+          end
+        end
+
+        // cleanup the format of all msgs
+        cleanup_format();
+
+        // print parsed msgs
+        foreach (sw_msgs[addr]) begin
+          msg_info(.verbosity("h"), .msg($sformatf("sw_msgs[%0h] = %p", addr, sw_msgs[addr])));
+        end
+      end
+      $fclose(fd);
+    end
+    return (sw_msgs.size() > 0);
+  endfunction
+
+    // split string using single character delimiter (as byte)
+  function automatic void split_msg_data_line_to_fields(input string  str,
+                                                        output string split[$]);
+    int start = 0;
+    for (int i = 0; i < str.len(); i++) begin
+      if (str.getc(i) == delimiter) begin
+        split.push_back(str.substr(start, i - 1));
+        start = i + 1;
+      end
+    end
+    // last one
+    split.push_back(str.substr(start, str.len() - 1));
+  endfunction
+
+  // function to cleanup the string formatting
+  function automatic void cleanup_format();
+    foreach (sw_msgs[addr]) begin
+      string str = sw_msgs[addr].format;
+      if (str.len() >= 2) begin
+        // replace ^M with \n and ^J also with \n (CR is not supported in SV)
+        for (int i = 0; i < str.len() - 1; i++) begin
+          if (str.getc(i) == "^" && str.getc(i + 1) inside {"M", "J"}) begin
+            str = {str.substr(0, i - 1), "\n", str.substr(i + 2, str.len() - 1)};
+            i++;
+          end
+          // replace %x, %d, %h with %0x, %0d and %0h
+          if (str.getc(i) == "%" && str.getc(i + 1) inside {"x", "h", "d"}) begin
+            str = {str.substr(0, i), "0", str.substr(i + 1, str.len() - 1)};
+            i += 2;
+          end
+        end
+      end
+      // remove the last \n added by $fgets
+      if (str.getc(str.len() - 1) == "\n") begin
+        str = str.substr(0, str.len() - 2);
+      end
+      // remove the last \n added in the print msg
+      if (str.getc(str.len() - 1) == "\n") begin
+        str = str.substr(0, str.len() - 2);
+      end
+      sw_msgs[addr].format = str;
+    end
+  endfunction
+
+    // retrieve addr or data from the bus
+  task automatic get_addr_data_from_bus();
+    forever begin
+      @(posedge clk);
+      if (valid === 1'b1 && rst_n !== 0) begin
+        addr_data_q.push_back(addr_data);
+      end
+    end
+  endtask
+
+  //construct msg and print when complete data is available
+  task automatic construct_msg_and_print();
+    forever begin
+      addr_data_t addr;
+      // get addr
+      wait(addr_data_q.size() > 0);
+      addr = addr_data_q.pop_front();
+
+      // lookup addr in sw_msgs
+      if (sw_msgs.exists(addr)) begin
+        bit rst_occurred;
+        fork
+          begin
+            fork
+              // get args
+              for (int i = 0; i < sw_msgs[addr].nargs; i++) begin
+                wait(addr_data_q.size() > 0);
+                sw_msgs[addr].arg[i] = addr_data_q.pop_front();
+              end
+              begin
+                // check if rst_n occurred - in that case discard and start over
+                wait(rst_n === 1'b0);
+                rst_occurred = 1'b1;
+              end
+            join_any
+            disable fork;
+          end
+        join
+        if (rst_occurred) continue;
+        sw_msg_print(sw_msgs[addr]);
+      end
+    end
+  endtask
+
+  // print the msg
+  function automatic void sw_msg_print(sw_msg_t sw_msg);
+    string msg_header = sw_msg.header;
+    string msg = sw_msg.format;
+
+    // if header not available, then construct from other fields: name(file:line)
+    if (msg_header == "") begin
+      msg_header = sw_msg.name;
+      if (sw_msg.file != "") begin
+        msg_header = {msg_header, "(", sw_msg.file, ":", $sformatf("%0d", sw_msg.line), ")"};
+      end
+    end
+
+    // construct formatted string based on args
+    case (sw_msg.nargs)
+      `NARGS_CASE(0)
+      `NARGS_CASE(1)
+      `NARGS_CASE(2)
+      `NARGS_CASE(3)
+      `NARGS_CASE(4)
+      `NARGS_CASE(5)
+      `NARGS_CASE(6)
+      `NARGS_CASE(7)
+      `NARGS_CASE(8)
+      `NARGS_CASE(9)
+      `NARGS_CASE(10)
+      `NARGS_CASE(11)
+      `NARGS_CASE(12)
+      `NARGS_CASE(13)
+      `NARGS_CASE(14)
+      `NARGS_CASE(15)
+      `NARGS_CASE(16)
+      `NARGS_CASE(17)
+      `NARGS_CASE(18)
+      `NARGS_CASE(19)
+      `NARGS_CASE(20)
+      `NARGS_CASE(21)
+      `NARGS_CASE(22)
+      `NARGS_CASE(23)
+      `NARGS_CASE(24)
+      `NARGS_CASE(25)
+      `NARGS_CASE(26)
+      `NARGS_CASE(27)
+      `NARGS_CASE(28)
+      `NARGS_CASE(29)
+      `NARGS_CASE(30)
+      `NARGS_CASE(31)
+      `NARGS_CASE(32)
+      default: msg_fatal("UNSUPPORTED", $sformatf("nargs = %0d (only 0:32 allowed)", sw_msg.nargs));
+    endcase
+
+    // print the msg
+    msg_print(sw_msg.msg_type, sw_msg.verbosity, msg_header, msg);
+  endfunction
+
+  // methods
+  // msg_print api that switches between system call and UVM call
+  function automatic void msg_print(string msg_type = "i",
+                                    string verbosity = "n",
+                                    string msg_header = "",
+                                    string msg);
+`ifdef UVM_PKG_SV
+    import uvm_pkg::*;
+    `include "uvm_macros.svh"
+
+    uvm_verbosity level;
+    case (verbosity)
+      "n", "none":          level = UVM_NONE;
+      "l", "lo", "low":     level = UVM_LOW;
+      "m", "med", "medium": level = UVM_MEDIUM;
+      "h", "hi", "high":    level = UVM_HIGH;
+      "f", "full":          level = UVM_FULL;
+      "d", "dbg", "debug":  level = UVM_DEBUG;
+      default:              level = UVM_NONE;
+    endcase
+
+    // additional cleanup: if msg_header is already enclosed in [],
+    // then remove it, since uvm macros also add them
+    if (msg_header.len() >= 2) begin
+      if (msg_header[0] == "[" && msg_header[msg_header.len() - 1] == "]") begin
+        msg_header = msg_header.substr(1, msg_header.len() - 2);
+      end
+    end
+
+    case (msg_type.tolower())
+      "i", "info":            `uvm_info(msg_header, msg, level)
+      "w", "warn", "warning": `uvm_warning(msg_header, msg)
+      "e", "err", "error":    `uvm_error(msg_header, msg)
+      "f", "fatal":           `uvm_fatal(msg_header, msg)
+      default:                `uvm_info(msg_header, msg, level)
+    endcase
+`else
+    case (msg_type.tolower())
+      "i", "info":            $info("%0t: %0s %0s", $time, msg_header, msg);
+      "w", "warn", "warning": $warning("%0t: %0s %0s", $time, msg_header, msg);
+      "e", "err", "error":    $error("%0t: %0s %0s", $time, msg_header, msg);
+      "f", "fatal":           $fatal("%0t: %0s %0s", $time, msg_header, msg);
+      default:                $info("%0t: %0s %0s", $time, msg_header, msg);
+    endcase
+`endif
+  endfunction
+
+  // print info msg
+  function automatic void msg_info(string verbosity = "l", string msg_header = "", string msg);
+    msg_print(.verbosity(verbosity), .msg_header(msg_header), .msg(msg));
+  endfunction
+
+  // print warning msg
+  function automatic void msg_warning(string msg_header = "", string msg);
+    msg_print(.msg_type("w"), .msg_header(msg_header), .msg(msg));
+  endfunction
+
+  // print error msg
+  function automatic void msg_error(string msg_header = "", string msg);
+    msg_print(.msg_type("e"), .msg_header(msg_header), .msg(msg));
+  endfunction
+
+  // print fatal msg
+  function automatic void msg_fatal(string msg_header = "", string msg);
+    msg_print(.msg_type("f"), .msg_header(msg_header), .msg(msg));
+  endfunction
+
+endinterface
+
+// undefine previously defined macros
+`undef _0_ARGS
+`undef _1_ARGS
+`undef _2_ARGS
+`undef _3_ARGS
+`undef _4_ARGS
+`undef _5_ARGS
+`undef _6_ARGS
+`undef _7_ARGS
+`undef _8_ARGS
+`undef _9_ARGS
+`undef _10_ARGS
+`undef _11_ARGS
+`undef _12_ARGS
+`undef _13_ARGS
+`undef _14_ARGS
+`undef _15_ARGS
+`undef _16_ARGS
+`undef _17_ARGS
+`undef _18_ARGS
+`undef _19_ARGS
+`undef _20_ARGS
+`undef _21_ARGS
+`undef _22_ARGS
+`undef _23_ARGS
+`undef _24_ARGS
+`undef _25_ARGS
+`undef _26_ARGS
+`undef _27_ARGS
+`undef _28_ARGS
+`undef _29_ARGS
+`undef _30_ARGS
+`undef _31_ARGS
+`undef _32_ARGS
+`undef _ADD_ARGS
+`undef NARGS_CASE
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index 6c3eb2a..2c8f3b4 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -16,6 +16,7 @@
       - lowrisc:dv:hmac_env
       - lowrisc:dv:rv_timer_env
       - lowrisc:dv:spi_device_env
+      - lowrisc:dv:sw_msg_monitor_if
     files:
       - chip_env_pkg.sv
       - chip_env_cfg.sv: {is_include_file: true}
diff --git a/hw/top_earlgrey/dv/env/chip_env.sv b/hw/top_earlgrey/dv/env/chip_env.sv
index cd4f090..683b647 100644
--- a/hw/top_earlgrey/dv/env/chip_env.sv
+++ b/hw/top_earlgrey/dv/env/chip_env.sv
@@ -43,6 +43,11 @@
       end
     end
 
+    if (!uvm_config_db#(sw_msg_monitor_vif)::get(this, "", "sw_msg_monitor_vif",
+                                                 cfg.sw_msg_monitor_vif)) begin
+      `uvm_fatal(`gfn, "failed to get sw_msg_monitor_vif from uvm_config_db")
+    end
+
     // create components
     m_uart_agent = uart_agent::type_id::create("m_uart_agent", this);
     uvm_config_db#(uart_agent_cfg)::set(this, "m_uart_agent*", "cfg", cfg.m_uart_agent_cfg);
diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
index 2fffac5..4f35632 100644
--- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
@@ -8,7 +8,16 @@
 
   // chip top interfaces
   gpio_vif            gpio_vif;
-  virtual mem_bkdr_if mem_bkdr_vifs[chip_mem_e];
+  mem_bkdr_vif        mem_bkdr_vifs[chip_mem_e];
+
+  // sw msg monitor related
+  sw_msg_monitor_vif  sw_msg_monitor_vif;
+  // below values are constants, but made variables in case some test has different requirements
+  string              rom_image         = "sw_build/rom/rom.vmem";
+  string              rom_msg_data_file = "sw_build/rom/msg_data.txt";
+  string              sw_image          = "sw_build/sw/sw.vmem";
+  string              sw_msg_data_file  = "sw_build/sw/msg_data.txt";
+  bit [TL_AW-1:0]     sw_msg_addr       = 32'h1000fff4;
 
   // ext component cfgs
   rand uart_agent_cfg m_uart_agent_cfg;
diff --git a/hw/top_earlgrey/dv/env/chip_env_pkg.sv b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
index 17b532c..47f8e7b 100644
--- a/hw/top_earlgrey/dv/env/chip_env_pkg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
@@ -27,11 +27,10 @@
 
   // local parameters and types
   parameter         NUM_GPIOS   = 16;
-  parameter string  ROM_MEM_IMG = "sw_build/rom/rom.vmem";
-  parameter string  SW_MEM_IMG  = "sw_build/sw/sw.vmem";
 
   typedef virtual pins_if #(NUM_GPIOS)  gpio_vif;
   typedef virtual mem_bkdr_if           mem_bkdr_vif;
+  typedef virtual sw_msg_monitor_if     sw_msg_monitor_vif;
 
   // enum to indicate cpu test pass / fail status
   typedef enum bit [15:0] {
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
index ab06770..76c411f 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
@@ -46,13 +46,19 @@
   // routine to backdoor load cpu test hex image and bring the cpu out of reset (if required)
   // TODO(sriyerg): for future implementation
   virtual task cpu_init();
-    cfg.mem_bkdr_vifs[Rom].load_mem_from_file(ROM_MEM_IMG);
+    cfg.mem_bkdr_vifs[Rom].load_mem_from_file(cfg.rom_image);
     cfg.mem_bkdr_vifs[FlashBank0].set_mem();
     cfg.mem_bkdr_vifs[FlashBank1].set_mem();
 
     // TODO: the location of the main execution image should be randomized for either bank in future
-    cfg.mem_bkdr_vifs[FlashBank0].load_mem_from_file(SW_MEM_IMG);
+    cfg.mem_bkdr_vifs[FlashBank0].load_mem_from_file(cfg.sw_image);
     cpu_test_state = CpuTestRunning;
+
+    // initialize the sw msg monitor
+    cfg.sw_msg_monitor_vif.sw_msg_addr = cfg.sw_msg_addr;
+    cfg.sw_msg_monitor_vif.add_sw_msg_data_files("rom", cfg.rom_msg_data_file);
+    cfg.sw_msg_monitor_vif.add_sw_msg_data_files("sw", cfg.sw_msg_data_file);
+    cfg.sw_msg_monitor_vif.ready();
   endtask
 
   virtual task dut_shutdown();
@@ -76,7 +82,7 @@
         `uvm_info(`gfn, $sformatf("cpu_test_state = %0s", cpu_test_state), UVM_LOW)
         case (cpu_test_state)
           CpuUnderReset: begin
-            wait (cpu_test_state == CpuTestRunning);
+            wait(cpu_test_state == CpuTestRunning);
           end
 
           CpuTestRunning: begin
@@ -92,8 +98,8 @@
             end
           end
 
-          {CpuTestPass, CpuTestFail}: begin
-            wait (cpu_test_state == CpuUnderReset);
+          CpuTestPass, CpuTestFail: begin
+            wait(cpu_test_state == CpuUnderReset);
           end
         endcase
       end
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index d358d4c..3aed42e 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -8,6 +8,7 @@
   import dv_utils_pkg::*;
   import tl_agent_pkg::*;
   import chip_env_pkg::*;
+  import top_pkg::*;
 
   // macro includes
   `include "uvm_macros.svh"
@@ -68,6 +69,20 @@
     .IO_GP15    (gpio_pins[15])
   );
 
+  // connect sw_msg_monitor
+  bit                 sw_msg_monitor_valid;
+  bit [TL_AW-1:0]     sw_msg_monitor_sw_msg_addr;
+
+  sw_msg_monitor_if sw_msg_monitor_if (
+    .clk              (`RAM_MAIN_HIER.clk_i),
+    .rst_n            (`RAM_MAIN_HIER.rst_ni),
+    .valid            (sw_msg_monitor_valid),
+    .addr_data        (`RAM_MAIN_HIER.wdata_i),
+    .sw_msg_addr      (sw_msg_monitor_sw_msg_addr)
+  );
+  assign sw_msg_monitor_valid = `RAM_MAIN_HIER.req_i && `RAM_MAIN_HIER.write_i &&
+                                (`RAM_MAIN_HIER.addr_i == sw_msg_monitor_sw_msg_addr);
+
   // connect signals
   assign jtag_tck         = jtag_if.tck;
   assign jtag_tms         = jtag_if.tms;
@@ -90,6 +105,8 @@
         `FLASH0_MEM_HIER.flash0_mem_bkdr_if);
     uvm_config_db#(virtual mem_bkdr_if)::set(null, "*.env", "mem_bkdr_vifs[FlashBank1]",
         `FLASH1_MEM_HIER.flash1_mem_bkdr_if);
+    uvm_config_db#(virtual sw_msg_monitor_if)::set(null, "*.env", "sw_msg_monitor_vif",
+        sw_msg_monitor_if);
     $timeformat(-12, 0, " ps", 12);
     run_test();
   end