Adjust to newer simutil_verilator

simutil_verilator was improved in various ways. The main improvements
are:

- Memories can be loaded with ELF files directly, instead of VMEM files.
- A generic way to specify memories was added.
- Reworked interface to be easier to use (less duplicated code).

These changes require adjustemnts to our code.

- The memory primitives need to support a new
  `simutil_verilator_memload()` DPI function, which is used to write the
  converted ELF file.
- libelf is required for ELF parsing. Add this dependency to the
  core files, documentation and to the CI setup.
- VerilatorSimCtrl is now much easier to use by calling the `Exec()`
  function. Everything else is now handled by VerilatorSimCtrl itself.
  By using stack-allocated objects we can simplify error/free handling.
- VerilatorSimCtrl doesn't expose a static variable `simctrl` any more,
  but instead implements a singleton accessible through
  `VerilatorSimCtrl::GetInstance()`. Update spidpi to use this.

Signed-off-by: Philipp Wagner <phw@lowrisc.org>
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index eb56edc..2fcd1dd 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -174,7 +174,7 @@
   pool: Default
   steps:
   - bash: |
-      sudo apt-get install -y python3 python3-pip build-essential srecord python3-setuptools zlib1g-dev libusb-1.0 \
+      sudo apt-get install -y python3 python3-pip build-essential srecord python3-setuptools zlib1g-dev libusb-1.0 libelf-dev \
         && sudo pip3 install -U -r python-requirements.txt \
         && sudo apt-get install git make autoconf g++ flex bison curl
     displayName: 'Install dependencies'
@@ -265,7 +265,7 @@
     - sw_build
   steps:
   - bash: |
-      sudo apt-get install -y python3 python3-pip build-essential python3-setuptools tree
+      sudo apt-get install -y python3 python3-pip build-essential python3-setuptools libelf1 tree
       sudo pip3 install -r python-requirements.txt
     displayName: 'Install dependencies'
   - template: 'ci/download-artifacts-template.yml'
diff --git a/doc/ug/install_instructions/index.md b/doc/ug/install_instructions/index.md
index b64face..5355acd 100644
--- a/doc/ug/install_instructions/index.md
+++ b/doc/ug/install_instructions/index.md
@@ -59,7 +59,7 @@
 $ sudo apt-get install git python3 python3-pip python3-setuptools \
     build-essential autoconf flex bison ninja-build pkgconf \
     srecord zlib1g-dev libftdi1-dev libftdi1-2 libssl-dev \
-    libusb-1.0-0-dev libtool
+    libusb-1.0-0-dev libtool libelf-dev
 ```
 
 Some tools in this repository are written in Python 3 and require Python dependencies to be installed through `pip`.
diff --git a/doc/ug/quickstart.md b/doc/ug/quickstart.md
index 69d8c81..c3a1ab7 100644
--- a/doc/ug/quickstart.md
+++ b/doc/ug/quickstart.md
@@ -12,10 +12,11 @@
 * libusb 1.x
 * libftdi1 1.x
 * usbutils
+* libelf1
 
 Under Ubuntu these can be installed with:
 ```console
-$ sudo apt-get install xz-utils screen fdisk libftdi1-2 libusb-1.0-0 usbutils
+$ sudo apt-get install xz-utils screen fdisk libftdi1-2 libusb-1.0-0 usbutils libelf1
 ```
 
 ## Extract the release
diff --git a/hw/dv/dpi/spidpi/spidpi.c b/hw/dv/dpi/spidpi/spidpi.c
index c1dc7d0..08a80d7 100644
--- a/hw/dv/dpi/spidpi/spidpi.c
+++ b/hw/dv/dpi/spidpi/spidpi.c
@@ -26,28 +26,6 @@
 // and resume at the first SPI packet
 // #define CONTROL_TRACE
 
-extern VerilatorSimCtrl *simctrl;
-
-static void finish(void) {
-  if (!simctrl) {
-    return;
-  }
-  simctrl->RequestStop(true);
-}
-
-#ifdef CONTROL_TRACE
-static void trace(int enable) {
-  if (!simctrl) {
-    return;
-  }
-  if (enable) {
-    simctrl->TraceOn();
-  } else {
-    simctrl->TraceOff();
-  }
-}
-#endif
-
 void *spidpi_create(const char *name, int mode, int loglevel) {
   int i;
   struct spidpi_ctx *ctx =
@@ -126,7 +104,7 @@
 
 #ifdef CONTROL_TRACE
   if (ctx->tick == 4) {
-    trace(0);
+    VerilatorSimCtrl::GetInstance().TraceOff();
   }
 #endif
 
@@ -149,7 +127,7 @@
         ctx->din = 0;
         ctx->state = SP_CSFALL;
 #ifdef CONTROL_TRACE
-        trace(1);
+        VerilatorSimCtrl::GetInstance().TraceOn();
 #endif
       }
     }
@@ -218,7 +196,7 @@
         ctx->state = SP_IDLE;
         break;
       case SP_FINISH:
-        finish();
+        VerilatorSimCtrl::GetInstance().RequestStop(true);
         break;
       default:
         ctx->driving = set_sck | (ctx->driving & ~P2D_SCK);
diff --git a/hw/dv/dpi/usbdpi/usbdpi.c b/hw/dv/dpi/usbdpi/usbdpi.c
index 5ba8909..5ce8265 100644
--- a/hw/dv/dpi/usbdpi/usbdpi.c
+++ b/hw/dv/dpi/usbdpi/usbdpi.c
@@ -32,15 +32,6 @@
     "HS_SENDACK 8",    "HS_WAIT_PKT 9",  "HS_ACKIFDATA 10",    "HS_SENDHI 11",
     "HS_EMPTYDATA 12", "HS_WAITACK2 13", "HS_NEXTFRAME 14"};
 
-extern VerilatorSimCtrl *simctrl;
-
-static void usbdpi_finish(int simulation_success) {
-  if (!simctrl) {
-    return;
-  }
-  simctrl->RequestStop(simulation_success);
-}
-
 void *usbdpi_create(const char *name, int loglevel) {
   int i;
   struct usbdpi_ctx *ctx =
@@ -553,7 +544,8 @@
   }
   switch (ctx->state) {
     case ST_IDLE:
-      if ((simctrl && simctrl->TracingEnabled() && (ctx->frame == 20)) ||
+      if ((VerilatorSimCtrl::GetInstance().TracingEnabled() &&
+           (ctx->frame == 20)) ||
           (ctx->frame > 50)) {
         printf("USB: usbdpi done, frame: %d, success: %d, state: %d\n",
                ctx->frame, ctx->baudrate_set_successfully, ctx->state);
@@ -561,7 +553,8 @@
         // If we were able to set the BAUD rate sucessfully, the DUT
         // provided reasonable responses to our requests. Ideally, we
         // would have a more advanced test here.
-        usbdpi_finish(ctx->baudrate_set_successfully);
+        VerilatorSimCtrl::GetInstance().RequestStop(
+            ctx->baudrate_set_successfully);
       }
 
       switch (ctx->frame) {
diff --git a/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv b/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv
index b860da5..ad03e43 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv
@@ -64,18 +64,32 @@
   end
 
   `ifdef VERILATOR
+    // Task for loading 'mem' with SystemVerilog system task $readmemh()
     export "DPI-C" task simutil_verilator_memload;
+    // Function for setting a specific 32 bit element in |mem|
+    // Returns 1 (true) for success, 0 (false) for errors.
+    export "DPI-C" function simutil_verilator_set_mem;
 
     task simutil_verilator_memload;
       input string file;
       $readmemh(file, mem);
     endtask
+
+    // TODO: Allow 'val' to have other widths than 32 bit
+    function int simutil_verilator_set_mem(input int index,
+                                           input logic[31:0] val);
+      if (index >= Depth) begin
+        return 0;
+      end
+
+      mem[index] = val;
+      return 1;
+    endfunction
   `endif
 
   `ifdef SRAM_INIT_FILE
-      localparam MEM_FILE = `"`SRAM_INIT_FILE`";
-    initial
-    begin
+    localparam MEM_FILE = `"`SRAM_INIT_FILE`";
+    initial begin
       $display("Initializing SRAM from %s", MEM_FILE);
       $readmemh(MEM_FILE, mem);
     end
diff --git a/hw/ip/prim_generic/rtl/prim_generic_rom.sv b/hw/ip/prim_generic/rtl/prim_generic_rom.sv
index 6228622..30bed51 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_rom.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_rom.sv
@@ -39,16 +39,30 @@
   `ASSERT(noXOnCsI, !$isunknown(cs_i), clk_i, '0)
 
   `ifdef VERILATOR
+    // Task for loading 'mem' with SystemVerilog system task $readmemh()
     export "DPI-C" task simutil_verilator_memload;
+    // Function for setting a specific 32 bit element in |mem|
+    // Returns 1 (true) for success, 0 (false) for errors.
+    export "DPI-C" function simutil_verilator_set_mem;
 
     task simutil_verilator_memload;
       input string file;
       $readmemh(file, mem);
     endtask
+
+    // TODO: Allow 'val' to have other widths than 32 bit
+    function int simutil_verilator_set_mem(input int index,
+                                           input logic[31:0] val);
+      if (index >= Depth) begin
+        return 0;
+      end
+
+      mem[index] = val;
+      return 1;
+    endfunction
   `endif
 
   `ifdef ROM_INIT_FILE
-
     localparam MEM_FILE = `"`ROM_INIT_FILE`";
     initial begin
       $display("Initializing ROM from %s", MEM_FILE);
diff --git a/hw/top_earlgrey/top_earlgrey_usb_verilator.cc b/hw/top_earlgrey/top_earlgrey_usb_verilator.cc
index a27653b..8ff36a4 100644
--- a/hw/top_earlgrey/top_earlgrey_usb_verilator.cc
+++ b/hw/top_earlgrey/top_earlgrey_usb_verilator.cc
@@ -2,101 +2,35 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-#include <signal.h>
-
 #include <iostream>
 
-#include "Vtop_earlgrey_usb_verilator.h"
 #include "verilated_toplevel.h"
 #include "verilator_sim_ctrl.h"
 
-top_earlgrey_usb_verilator *top;
-VerilatorSimCtrl *simctrl;
-
-static void SignalHandler(int sig) {
-  if (!simctrl) {
-    return;
-  }
-
-  switch (sig) {
-    case SIGINT:
-      simctrl->RequestStop(true);
-      break;
-    case SIGUSR1:
-      if (simctrl->TracingEnabled()) {
-        simctrl->TraceOff();
-      } else {
-        simctrl->TraceOn();
-      }
-      break;
-  }
-}
-
-static void SetupSignalHandler() {
-  struct sigaction sigIntHandler;
-
-  sigIntHandler.sa_handler = SignalHandler;
-  sigemptyset(&sigIntHandler.sa_mask);
-  sigIntHandler.sa_flags = 0;
-
-  sigaction(SIGINT, &sigIntHandler, NULL);
-  sigaction(SIGUSR1, &sigIntHandler, NULL);
-}
-
 int main(int argc, char **argv) {
-  int retcode;
-  top = new top_earlgrey_usb_verilator;
-  simctrl = new VerilatorSimCtrl(top, top->clk_i, top->rst_ni,
-                                 VerilatorSimCtrlFlags::ResetPolarityNegative);
+  top_earlgrey_usb_verilator top;
+  VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
+  simctrl.SetTop(&top, &top.clk_i, &top.rst_ni,
+                 VerilatorSimCtrlFlags::ResetPolarityNegative);
 
-  SetupSignalHandler();
-
-  if (!simctrl->ParseCommandArgs(argc, argv, retcode)) {
-    goto free_return;
-  }
-
-  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
-            << "=================================" << std::endl
-            << std::endl;
-
-  if (simctrl->TracingPossible()) {
-    std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
-              << std::endl
-              << "$ kill -USR1 " << getpid() << std::endl;
-  }
-
-  // Initialize ROM
-  simctrl->InitRom(
-      "TOP.top_earlgrey_usb_verilator.top_earlgrey_usb.u_rom_rom"
-      ".gen_mem_generic.u_impl_generic");
-
-  // Initialize Ram
-  simctrl->InitRam(
+  simctrl.RegisterMemoryArea("rom",
+                             "TOP.top_earlgrey_usb_verilator.top_earlgrey_usb."
+                             "u_rom_rom.gen_mem_generic."
+                             "u_impl_generic");
+  simctrl.RegisterMemoryArea(
+      "ram",
       "TOP.top_earlgrey_usb_verilator.top_earlgrey_usb.u_ram1p_ram_main"
       ".gen_mem_generic.u_impl_generic");
-
-  // Initialize Flash
-  //  simctrl->InitFlash(
-  //      "TOP.top_earlgrey_usb_verilator.top_earlgrey_usb.u_flash_eflash.gen_flash."
-  //      "u_impl_generic.gen_flash_banks[0].u_impl_generic.gen_mem_generic.u_impl_"
-  //      "generic");
-  simctrl->InitFlash(
+  simctrl.RegisterMemoryArea(
+      "flash",
       "TOP.top_earlgrey_usb_verilator.top_earlgrey_usb.u_flash_eflash."
       "gen_flash_banks[0].u_flash.gen_flash.u_impl_generic.u_mem.gen_mem_"
       "generic.u_impl_"
       "generic");
 
-  simctrl->Run();
-  simctrl->PrintStatistics();
+  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
+            << "=================================" << std::endl
+            << std::endl;
 
-  if (simctrl->TracingEverEnabled()) {
-    std::cout << std::endl
-              << "You can view the simulation traces by calling" << std::endl
-              << "$ gtkwave " << simctrl->GetSimulationFileName() << std::endl;
-  }
-
-free_return:
-  delete top;
-  delete simctrl;
-  return retcode;
+  return simctrl.Exec(argc, argv);
 }
diff --git a/hw/top_earlgrey/top_earlgrey_usb_verilator.core b/hw/top_earlgrey/top_earlgrey_usb_verilator.core
index 28555ab..d951058 100644
--- a/hw/top_earlgrey/top_earlgrey_usb_verilator.core
+++ b/hw/top_earlgrey/top_earlgrey_usb_verilator.core
@@ -46,7 +46,7 @@
           - '--trace-params'
           - '--trace-max-array 1024'
           - '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=top_earlgrey_usb_verilator -g"'
-          - '-LDFLAGS "-pthread -lutil"'
+          - '-LDFLAGS "-pthread -lutil -lelf"'
           - "-Wall"
           - "-Wno-PINCONNECTEMPTY"
           # XXX: Cleanup all warnings and remove this option
diff --git a/hw/top_earlgrey/top_earlgrey_verilator.cc b/hw/top_earlgrey/top_earlgrey_verilator.cc
index d18a53f..03efc1c 100644
--- a/hw/top_earlgrey/top_earlgrey_verilator.cc
+++ b/hw/top_earlgrey/top_earlgrey_verilator.cc
@@ -2,103 +2,35 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-#include <signal.h>
-
 #include <iostream>
 
-
-#include "Vtop_earlgrey_verilator.h"
 #include "verilated_toplevel.h"
 #include "verilator_sim_ctrl.h"
 
-
-top_earlgrey_verilator *top;
-VerilatorSimCtrl *simctrl;
-
-static void SignalHandler(int sig) {
-  if (!simctrl) {
-    return;
-  }
-
-  switch (sig) {
-    case SIGINT:
-      simctrl->RequestStop(true);
-      break;
-    case SIGUSR1:
-      if (simctrl->TracingEnabled()) {
-        simctrl->TraceOff();
-      } else {
-        simctrl->TraceOn();
-      }
-      break;
-  }
-}
-
-static void SetupSignalHandler() {
-  struct sigaction sigIntHandler;
-
-  sigIntHandler.sa_handler = SignalHandler;
-  sigemptyset(&sigIntHandler.sa_mask);
-  sigIntHandler.sa_flags = 0;
-
-  sigaction(SIGINT, &sigIntHandler, NULL);
-  sigaction(SIGUSR1, &sigIntHandler, NULL);
-}
-
 int main(int argc, char **argv) {
-  int retcode;
-  top = new top_earlgrey_verilator;
-  simctrl = new VerilatorSimCtrl(top, top->clk_i, top->rst_ni,
-                                 VerilatorSimCtrlFlags::ResetPolarityNegative);
+  top_earlgrey_verilator top;
+  VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
+  simctrl.SetTop(&top, &top.clk_i, &top.rst_ni,
+                 VerilatorSimCtrlFlags::ResetPolarityNegative);
 
-  SetupSignalHandler();
-
-  if (!simctrl->ParseCommandArgs(argc, argv, retcode)) {
-    goto free_return;
-  }
-
-  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
-            << "=================================" << std::endl
-            << std::endl;
-
-  if (simctrl->TracingPossible()) {
-    std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
-              << std::endl
-              << "$ kill -USR1 " << getpid() << std::endl;
-  }
-
-  // Initialize ROM
-  simctrl->InitRom(
-      "TOP.top_earlgrey_verilator.top_earlgrey.u_rom_rom"
-      ".gen_mem_generic.u_impl_generic");
-
-  // Initialize Ram
-  simctrl->InitRam(
+  simctrl.RegisterMemoryArea(
+      "rom",
+      "TOP.top_earlgrey_verilator.top_earlgrey.u_rom_rom.gen_mem_generic."
+      "u_impl_generic");
+  simctrl.RegisterMemoryArea(
+      "ram",
       "TOP.top_earlgrey_verilator.top_earlgrey.u_ram1p_ram_main"
       ".gen_mem_generic.u_impl_generic");
-
-  // Initialize Flash
-  //  simctrl->InitFlash(
-  //      "TOP.top_earlgrey_verilator.top_earlgrey.u_flash_eflash.gen_flash."
-  //      "u_impl_generic.gen_flash_banks[0].u_impl_generic.gen_mem_generic.u_impl_"
-  //      "generic");
-  simctrl->InitFlash(
+  simctrl.RegisterMemoryArea(
+      "flash",
       "TOP.top_earlgrey_verilator.top_earlgrey.u_flash_eflash."
       "gen_flash_banks[0].u_flash.gen_flash.u_impl_generic.u_mem.gen_mem_"
       "generic.u_impl_"
       "generic");
 
-  simctrl->Run();
-  simctrl->PrintStatistics();
+  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
+            << "=================================" << std::endl
+            << std::endl;
 
-  if (simctrl->TracingEverEnabled()) {
-    std::cout << std::endl
-              << "You can view the simulation traces by calling" << std::endl
-              << "$ gtkwave " << simctrl->GetSimulationFileName() << std::endl;
-  }
-
-free_return:
-  delete top;
-  delete simctrl;
-  return retcode;
+  return simctrl.Exec(argc, argv);
 }
diff --git a/hw/top_earlgrey/top_earlgrey_verilator.core b/hw/top_earlgrey/top_earlgrey_verilator.core
index 371bb13..a83cfae 100644
--- a/hw/top_earlgrey/top_earlgrey_verilator.core
+++ b/hw/top_earlgrey/top_earlgrey_verilator.core
@@ -73,7 +73,7 @@
           - '--trace-params'
           - '--trace-max-array 1024'
           - '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=top_earlgrey_verilator -g"'
-          - '-LDFLAGS "-pthread -lutil"'
+          - '-LDFLAGS "-pthread -lutil -lelf"'
           - "-Wall"
           - "-Wno-PINCONNECTEMPTY"
           # XXX: Cleanup all warnings and remove this option