[prim] Make ROM_INIT_FILE a parameter

Instead of having a global define ROM_INIT_FILE, which contains a path
to a VMEM file with the boot ROM contents, use a parameter passed
through the hierarchy. This enables multiple instantiations of the ROM,
and avoids naming clashes between the ROM_INIT_FILE/SRAM_INIT_FILE
define, which can be used only once in the whole toplevel design.

This requires changes in various places to properly pass through the
parameter.

Instead of updating the prim_xilinx_rom variant, remove it. This
primitive is identical to the generic one, with the addition of
generating an empty ROM if no ROM initialization file is present during
the synthesis run. We capture this problem now in a more user-friendly
way with a TCL script after synthesis; removing this primitive here
avoids updating it first to the new way of passing in the init file.

The Arty S7 changes were done blindly, I don't have that board. I also
note that the board was very likely broken before, as the ROM init file
name was not called SRAM_INIT_FILE for a long time.

Fixes #2225

Signed-off-by: Philipp Wagner <phw@lowrisc.org>
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1cb5092..5255c5e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -269,7 +269,7 @@
         run --target=synth --setup --build \
         --build-root="$OBJ_DIR/hw" \
         lowrisc:systems:top_earlgrey_nexysvideo \
-        --ROM_INIT_FILE="$BOOTROM_VMEM"
+        --BootRomInitFile="$BOOTROM_VMEM"
 
       cp "$OBJ_DIR/hw/synth-vivado/lowrisc_systems_top_earlgrey_nexysvideo_0.1.bit" \
         "$BIN_DIR/hw/top_earlgrey"
diff --git a/hw/ip/prim/rtl/prim_util_memload.sv b/hw/ip/prim/rtl/prim_util_memload.sv
index d5ebf3f..db77102 100644
--- a/hw/ip/prim/rtl/prim_util_memload.sv
+++ b/hw/ip/prim/rtl/prim_util_memload.sv
@@ -12,7 +12,10 @@
  * - A memory array named `mem`.
  * - A parameter `Width` giving the memory width (word size) in bit.
  * - A parameter `Depth` giving the memory depth in words.
+ * - A parameter `MemInitFile` with a file path of a VMEM file to be loaded into
+*    the memory if not empty.
  */
+
 `ifdef VERILATOR
   // Task for loading 'mem' with SystemVerilog system task $readmemh()
   export "DPI-C" task simutil_verilator_memload;
@@ -50,10 +53,9 @@
   end
 `endif
 
-`ifdef SRAM_INIT_FILE
-  localparam MEM_FILE = `PRIM_STRINGIFY(`SRAM_INIT_FILE);
-  initial begin
-    $display("Initializing memory from %s", MEM_FILE);
-    $readmemh(MEM_FILE, mem);
+initial begin
+  if (MemInitFile != "") begin : gen_meminit
+      $display("Initializing memory %m from file '%s'.", MemInitFile);
+      $readmemh(MemInitFile, mem);
   end
-`endif
+end
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 4c8085f..e81bf84 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_ram_1p.sv
@@ -10,6 +10,8 @@
   parameter  int Width           = 32, // bit
   parameter  int Depth           = 128,
   parameter  int DataBitsPerMask = 1, // Number of data bits per bit of write mask
+  parameter      MemInitFile     = "", // VMEM file to initialize the memory with
+
   localparam int Aw              = $clog2(Depth)  // derived parameter
 ) (
   input  logic             clk_i,
diff --git a/hw/ip/prim_generic/rtl/prim_generic_ram_2p.sv b/hw/ip/prim_generic/rtl/prim_generic_ram_2p.sv
index 36a90d4..2666879 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_ram_2p.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_ram_2p.sv
@@ -7,11 +7,12 @@
 //   Implementing ECC should be done inside wrapper not this model.
 
 module prim_generic_ram_2p #(
-  parameter  int Width = 32, // bit
-  parameter  int Depth = 128,
+  parameter  int Width           = 32, // bit
+  parameter  int Depth           = 128,
   parameter  int DataBitsPerMask = 1, // Number of data bits per bit of write mask
+  parameter      MemInitFile     = "", // VMEM file to initialize the memory with
 
-  localparam int Aw    = $clog2(Depth)  // derived parameter
+  localparam int Aw              = $clog2(Depth)  // derived parameter
 ) (
   input clk_a_i,
   input clk_b_i,
diff --git a/hw/ip/prim_generic/rtl/prim_generic_rom.sv b/hw/ip/prim_generic/rtl/prim_generic_rom.sv
index 3bd1c2a..9bfe88b 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_rom.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_rom.sv
@@ -5,9 +5,11 @@
 `include "prim_assert.sv"
 
 module prim_generic_rom #(
-  parameter  int Width     = 32,
-  parameter  int Depth     = 2048, // 8kB default
-  localparam int Aw        = $clog2(Depth)
+  parameter  int Width       = 32,
+  parameter  int Depth       = 2048, // 8kB default
+  parameter      MemInitFile = "", // VMEM file to initialize the memory with
+
+  localparam int Aw          = $clog2(Depth)
 ) (
   input                        clk_i,
   input                        rst_ni,
diff --git a/hw/ip/prim_xilinx/lint/prim_xilinx_rom.vlt b/hw/ip/prim_xilinx/lint/prim_xilinx_rom.vlt
deleted file mode 100644
index 544a7b7..0000000
--- a/hw/ip/prim_xilinx/lint/prim_xilinx_rom.vlt
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-//
diff --git a/hw/ip/prim_xilinx/lint/prim_xilinx_rom.waiver b/hw/ip/prim_xilinx/lint/prim_xilinx_rom.waiver
deleted file mode 100644
index af90009..0000000
--- a/hw/ip/prim_xilinx/lint/prim_xilinx_rom.waiver
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-#
diff --git a/hw/ip/prim_xilinx/prim_xilinx_rom.core b/hw/ip/prim_xilinx/prim_xilinx_rom.core
deleted file mode 100644
index 9e4a053..0000000
--- a/hw/ip/prim_xilinx/prim_xilinx_rom.core
+++ /dev/null
@@ -1,44 +0,0 @@
-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:prim_xilinx:rom"
-description: "Xilinx implementation of a Read-Only Memory (ROM) primitive"
-filesets:
-  files_rtl:
-    depend:
-      - lowrisc:prim:all
-    files:
-      - rtl/prim_xilinx_rom.sv
-    file_type: systemVerilogSource
-
-  files_verilator_waiver:
-    depend:
-      # common waivers
-      - lowrisc:lint:common
-    files:
-      - lint/prim_xilinx_rom.vlt
-    file_type: vlt
-
-  files_ascentlint_waiver:
-    depend:
-      # common waivers
-      - lowrisc:lint:common
-    files:
-      - lint/prim_xilinx_rom.waiver
-    file_type: waiver
-
-  files_veriblelint_waiver:
-    depend:
-      # common waivers
-      - lowrisc:lint:common
-      - lowrisc:lint:comportable
-
-targets:
-  default:
-    filesets:
-      - tool_verilator   ? (files_verilator_waiver)
-      - tool_ascentlint  ? (files_ascentlint_waiver)
-      - tool_veriblelint ? (files_veriblelint_waiver)
-      - files_rtl
diff --git a/hw/ip/prim_xilinx/rtl/prim_xilinx_rom.sv b/hw/ip/prim_xilinx/rtl/prim_xilinx_rom.sv
deleted file mode 100644
index 5703270..0000000
--- a/hw/ip/prim_xilinx/rtl/prim_xilinx_rom.sv
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-/**
- * Implementation of a Read-Only Memory (ROM) primitive for Xilinx FPGAs
- *
- * This implementation of a ROM primitive is coded as outlined in UG 901 to
- * enable Xilinx Vivado infer Block RAM (BRAM) or LUT RAM from it. No mapping
- * target is forced; depending on the Width, Depth and other factors Vivado
- * chooses a mapping target.
- *
- * It is possible to force the mapping to BRAM or distributed RAM by using the
- * ROM_STYLE directive in an XDC file.
- */
-
-`include "prim_assert.sv"
-
-module prim_xilinx_rom #(
-  parameter  int Width     = 32,
-  parameter  int Depth     = 2048, // 8kB default
-  parameter  int Aw        = $clog2(Depth)
-) (
-  input                        clk_i,
-  input        [Aw-1:0]        addr_i,
-  input                        cs_i,
-  output logic [Width-1:0]     dout_o,
-  output logic                 dvalid_o
-
-);
-
-  `ifdef ROM_INIT_FILE
-   // Only create an actual ROM block if ROM data is specified
-   //
-   // Xilinx Vivado infers a BRAM or LUTRAM from the ROM code below only if mem
-   // is actually being written to through $readmemh(). If no ROM_INIT_FILE is
-   // given and hence the mem initialization is missing, registers are inferred
-   // instead. This severely degrades the synthesis quality for no good reason.
-   logic [Width-1:0] mem [Depth];
-
-   localparam MEM_FILE = `PRIM_STRINGIFY(`ROM_INIT_FILE);
-   initial
-   begin
-      $display("Initializing ROM from %s", MEM_FILE);
-      $readmemh(MEM_FILE, mem);
-   end
-
-  always_ff @(posedge clk_i) begin
-    if (cs_i) begin
-      dout_o <= mem[addr_i];
-    end
-  end
-  `else
-    // ROM is not initialized
-    always_ff @(posedge clk_i) begin
-      dout_o <= '0;
-    end
-  `endif
-
-  always_ff @(posedge clk_i) begin
-    dvalid_o <= cs_i;
-  end
-
-
-  ////////////////
-  // ASSERTIONS //
-  ////////////////
-
-  // Control Signals should never be X
-  `ASSERT(noXOnCsI, !$isunknown(cs_i), clk_i, '0)
-
-endmodule
diff --git a/hw/top_earlgrey/data/top_earlgrey.sv.tpl b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
index c8021c9..046b80a 100644
--- a/hw/top_earlgrey/data/top_earlgrey.sv.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
@@ -30,7 +30,8 @@
 dm_rst = top["reset_paths"]["lc"]
 %>\
 module top_${top["name"]} #(
-  parameter bit IbexPipeLine = 0
+  parameter bit IbexPipeLine = 0,
+  parameter     BootRomInitFile = ""
 ) (
   // Clock and Reset
   input               clk_i,
@@ -421,7 +422,8 @@
   ## TODO: Replace emulated ROM to real ROM in ASIC SoC
   prim_rom #(
     .Width(${data_width}),
-    .Depth(${rom_depth})
+    .Depth(${rom_depth}),
+    .MemInitFile(BootRomInitFile)
   ) u_rom_${m["name"]} (
     % for key in clocks:
     .${key}   (${clocks[key]}),
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index 7abb4d8..ff6e4dc 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -3,7 +3,8 @@
 // SPDX-License-Identifier: Apache-2.0
 
 module top_earlgrey #(
-  parameter bit IbexPipeLine = 0
+  parameter bit IbexPipeLine = 0,
+  parameter     BootRomInitFile = ""
 ) (
   // Clock and Reset
   input               clk_i,
@@ -373,7 +374,8 @@
 
   prim_rom #(
     .Width(32),
-    .Depth(4096)
+    .Depth(4096),
+    .MemInitFile(BootRomInitFile)
   ) u_rom_rom (
     .clk_i   (clkmgr_clocks.clk_main_infra),
     .rst_ni   (rstmgr_resets.rst_sys_n),
diff --git a/hw/top_earlgrey/rtl/top_earlgrey_artys7.sv b/hw/top_earlgrey/rtl/top_earlgrey_artys7.sv
index 16ab392..fc3a897 100644
--- a/hw/top_earlgrey/rtl/top_earlgrey_artys7.sv
+++ b/hw/top_earlgrey/rtl/top_earlgrey_artys7.sv
@@ -2,7 +2,11 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-module top_earlgrey_artys7 (
+module top_earlgrey_artys7  #(
+  // Path to a VMEM file containing the contents of the boot ROM, which will be
+  // baked into the FPGA bitstream.
+  parameter BootRomInitFile = "boot_rom_fpga_artys7.32.vmem"
+) (
   // Clock and Reset
   inout               IO_CLK,
   inout               IO_RST_N,
@@ -161,7 +165,8 @@
   //////////////////////
 
   top_earlgrey #(
-    .IbexPipeLine(1)
+    .IbexPipeLine(1),
+    .BootRomInitFile(BootRomInitFile)
   ) top_earlgrey (
     // Clocks, resets
     .clk_i           ( clk           ),
diff --git a/hw/top_earlgrey/rtl/top_earlgrey_nexysvideo.sv b/hw/top_earlgrey/rtl/top_earlgrey_nexysvideo.sv
index ff3ddec..3bc70e0 100644
--- a/hw/top_earlgrey/rtl/top_earlgrey_nexysvideo.sv
+++ b/hw/top_earlgrey/rtl/top_earlgrey_nexysvideo.sv
@@ -2,7 +2,11 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-module top_earlgrey_nexysvideo (
+module top_earlgrey_nexysvideo #(
+  // Path to a VMEM file containing the contents of the boot ROM, which will be
+  // baked into the FPGA bitstream.
+  parameter BootRomInitFile = "boot_rom_fpga_nexysvideo.32.vmem"
+) (
   // Clock and Reset
   inout               IO_CLK,
   inout               IO_RST_N,
@@ -202,7 +206,8 @@
   //////////////////////
 
   top_earlgrey #(
-    .IbexPipeLine(1)
+    .IbexPipeLine(1),
+    .BootRomInitFile(BootRomInitFile)
   ) top_earlgrey (
     // Clocks, resets
     .clk_i           ( clk           ),
diff --git a/hw/top_earlgrey/top_earlgrey_artys7-50.core b/hw/top_earlgrey/top_earlgrey_artys7-50.core
index 71e258a..5f2b188 100644
--- a/hw/top_earlgrey/top_earlgrey_artys7-50.core
+++ b/hw/top_earlgrey/top_earlgrey_artys7-50.core
@@ -21,15 +21,15 @@
 parameters:
   # XXX: This parameter needs to be absolute, or relative to the *.runs/synth_1
   # directory. It's best to pass it as absolute path when invoking fusesoc, e.g.
-  # --SRAM_INIT_FILE=$PWD/sw/device/examples/hello_world/hello_world.vmem
+  # --BootRomInitFile=$PWD/build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem
   # XXX: The VMEM file should be added to the sources of the Vivado project to
   # make the Vivado dependency tracking work. However this requires changes to
   # fusesoc first.
-  SRAM_INIT_FILE:
+  BootRomInitFile:
     datatype: str
-    description: SRAM initialization file in vmem hex format
+    description: Boot ROM initialization file in 32 bit vmem hex format
     default: "../../../../../build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem"
-    paramtype: vlogdefine
+    paramtype: vlogparam
   # For value definition, please see ip/prim/rtl/prim_pkg.sv
   PRIM_DEFAULT_IMPL:
     datatype: str
@@ -44,7 +44,7 @@
       - files_constraints
     toplevel: top_earlgrey_artys7
     parameters:
-      - SRAM_INIT_FILE
+      - BootRomInitFile
       - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx
     tools:
       vivado:
diff --git a/hw/top_earlgrey/top_earlgrey_nexysvideo.core b/hw/top_earlgrey/top_earlgrey_nexysvideo.core
index 84460ea..8d6cf89 100644
--- a/hw/top_earlgrey/top_earlgrey_nexysvideo.core
+++ b/hw/top_earlgrey/top_earlgrey_nexysvideo.core
@@ -30,15 +30,15 @@
 parameters:
   # XXX: This parameter needs to be absolute, or relative to the *.runs/synth_1
   # directory. It's best to pass it as absolute path when invoking fusesoc, e.g.
-  # --ROM_INIT_FILE=$PWD/build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.vmem
+  # --BootRomInitFile=$PWD/build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem
   # XXX: The VMEM file should be added to the sources of the Vivado project to
   # make the Vivado dependency tracking work. However this requires changes to
   # fusesoc first.
-  ROM_INIT_FILE:
+  BootRomInitFile:
     datatype: str
-    description: ROM initialization file in vmem hex format
+    description: Boot ROM initialization file in 32 bit vmem hex format
     default: "../../../../../build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem"
-    paramtype: vlogdefine
+    paramtype: vlogparam
   # For value definition, please see ip/prim/rtl/prim_pkg.sv
   PRIM_DEFAULT_IMPL:
     datatype: str
@@ -59,7 +59,7 @@
       - files_tcl
     toplevel: top_earlgrey_nexysvideo
     parameters:
-      - ROM_INIT_FILE
+      - BootRomInitFile
       - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx
     tools:
       vivado: