[rom_ctrl] Initial design RTL for the ROM checker

This has the counter FSM to read from ROM, the mux to arbitrate
between the checker and bus access, and a "compare" block to check
against expected outputs.

The checks that run at the start of time are all disabled by
default (because of the SkipCheck parameter). This means that we can
integrate this code with the top-level and teach pwrmgr to sequence
things properly before turning on the check.

This commit does not yet contain ROM scrambling (which will need a bit
more tooling work).

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/rom_ctrl/data/rom_ctrl.hjson b/hw/ip/rom_ctrl/data/rom_ctrl.hjson
index de973c9..42ed849 100644
--- a/hw/ip/rom_ctrl/data/rom_ctrl.hjson
+++ b/hw/ip/rom_ctrl/data/rom_ctrl.hjson
@@ -31,8 +31,33 @@
       struct:  "rom_cfg",
       act:     "rcv"
       type:    "uni",
-    }
+    },
+
+    // Power manager interface
+    { name:    "pwrmgr_data"
+      package: "rom_ctrl_pkg"
+      struct:  "pwrmgr_data"
+      act:     "req"
+      type:    "uni"
+    },
+
+    // Keymgr interface
+    { name:    "keymgr_data"
+      package: "rom_ctrl_pkg"
+      struct:  "keymgr_data"
+      act:     "req"
+      type:    "uni"
+    },
+
+    // KMAC interface
+    { name:    "kmac_data"
+      package: "kmac_pkg"
+      struct:  "app"
+      act:     "req"
+      type:    "req_rsp"
+    },
   ],
+  regwidth: "32"
   registers: {
     regs: [
       { name: "FATAL_ALERT_CAUSE",
@@ -47,18 +72,52 @@
         hwaccess: "hwo",
         fields: [
           { bits: "0",
+            name: "checker_error",
+            resval: 0,
+            desc: "Set on a fatal error detected by the ROM checker."
+          }
+          { bits: "1",
             name: "integrity_error",
             resval: 0,
-            desc: "Set on an integrity error from the register interface"
-          }
-
-          { bits: "1",
-            name: "dummy",
-            resval: 0,
-            desc: "Dummy index to prevent reggen from hiding the array."
+            desc: "Set on an integrity error from the register interface."
           }
         ]
       }
+
+      {
+        multireg: {
+          cname: "ROM_CTRL",
+          name: "DIGEST",
+          desc: "The digest computed from the contents of ROM"
+          count: "8"
+          swaccess: "ro"
+          hwaccess: "hrw"
+          fields: [
+            {
+              bits: "31:0"
+              name: "DIGEST"
+              desc: "32 bits of the digest"
+            }
+          ]
+        }
+      }
+      {
+        multireg: {
+          cname: "ROM_CTRL",
+          name: "EXP_DIGEST",
+          desc: "The expected digest, stored in the top words of ROM"
+          count: "8"
+          swaccess: "ro"
+          hwaccess: "hrw"
+          fields: [
+            {
+              bits: "31:0"
+              name: "DIGEST"
+              desc: "32 bits of the digest"
+            }
+          ]
+        }
+      }
     ],
 
     rom: [
diff --git a/hw/ip/rom_ctrl/rom_ctrl.core b/hw/ip/rom_ctrl/rom_ctrl.core
index f0aae3b..359e3df 100644
--- a/hw/ip/rom_ctrl/rom_ctrl.core
+++ b/hw/ip/rom_ctrl/rom_ctrl.core
@@ -13,6 +13,7 @@
       - lowrisc:prim:rom_adv
       - lowrisc:prim:subreg
       - lowrisc:prim:util
+      - lowrisc:ip:kmac_pkg
       - lowrisc:ip:tlul
     files:
       - rtl/rom_ctrl_pkg.sv
@@ -20,6 +21,10 @@
       - rtl/rom_ctrl_regs_reg_top.sv
       - rtl/rom_ctrl_rom_reg_top.sv
       - rtl/rom_ctrl.sv
+      - rtl/rom_ctrl_compare.sv
+      - rtl/rom_ctrl_counter.sv
+      - rtl/rom_ctrl_fsm.sv
+      - rtl/rom_ctrl_mux.sv
     file_type: systemVerilogSource
 
   files_verilator_waiver:
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl.sv
index 2339f17..600d51c 100644
--- a/hw/ip/rom_ctrl/rtl/rom_ctrl.sv
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl.sv
@@ -9,7 +9,8 @@
   import prim_rom_pkg::rom_cfg_t;
 #(
   parameter                       BootRomInitFile = "",
-  parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
+  parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
+  parameter bit                   SkipCheck = 1'b1
 ) (
   input  clk_i,
   input  rst_ni,
@@ -25,13 +26,62 @@
 
   // Alerts
   input  prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
-  output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o
+  output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
+
+  // Connections to other blocks
+  output rom_ctrl_pkg::pwrmgr_data_t pwrmgr_data_o,
+  output rom_ctrl_pkg::keymgr_data_t keymgr_data_o,
+  input  kmac_pkg::app_rsp_t         kmac_data_i,
+  output kmac_pkg::app_req_t         kmac_data_o
 );
 
   import rom_ctrl_pkg::*;
   import rom_ctrl_reg_pkg::*;
   import prim_util_pkg::vbits;
 
+  // ROM_CTRL_ROM_SIZE is auto-generated by regtool and comes from the bus window size, measured in
+  // bytes of content (i.e. 4 times the number of 32 bit words).
+  localparam int unsigned RomSizeByte = ROM_CTRL_ROM_SIZE;
+  localparam int unsigned RomSizeWords = RomSizeByte >> 2;
+  localparam int unsigned RomIndexWidth = vbits(RomSizeWords);
+
+  logic                     rom_select;
+
+  logic [RomIndexWidth-1:0] rom_index;
+  logic                     rom_req;
+  logic [39:0]              rom_rdata;
+  logic                     rom_rvalid;
+
+  logic [RomIndexWidth-1:0] bus_rom_index;
+  logic                     bus_rom_req;
+  logic                     bus_rom_gnt;
+  logic [39:0]              bus_rom_rdata;
+  logic                     bus_rom_rvalid;
+
+  logic [RomIndexWidth-1:0] checker_rom_index;
+  logic [39:0]              checker_rom_rdata;
+
+  // Pack / unpack kmac connection data ========================================
+
+  logic [63:0]              kmac_rom_data;
+  logic                     kmac_rom_rdy;
+  logic                     kmac_rom_vld;
+  logic                     kmac_rom_last;
+  logic                     kmac_done;
+  logic [255:0]             kmac_digest;
+
+  assign kmac_data_o = '{valid: kmac_rom_vld,
+                         data: kmac_rom_data,
+                         strb: '1,
+                         last: kmac_rom_last};
+
+  assign kmac_rom_rdy = kmac_data_i.ready;
+  assign kmac_done = kmac_data_i.done;
+  assign kmac_digest = kmac_data_i.digest_share0 ^ kmac_data_i.digest_share1;
+
+  logic unused_kmac_error;
+  assign unused_kmac_error = &{1'b0, kmac_data_i.error};
+
   // TL interface ==============================================================
 
   tlul_pkg::tl_h2d_t tl_rom_h2d [1];
@@ -40,32 +90,21 @@
   logic  rom_reg_integrity_error;
 
   rom_ctrl_rom_reg_top u_rom_top (
-      .clk_i      (clk_i),
-      .rst_ni     (rst_ni),
-      .tl_i       (rom_tl_i),
-      .tl_o       (rom_tl_o),
-      .tl_win_o   (tl_rom_h2d),
-      .tl_win_i   (tl_rom_d2h),
+    .clk_i      (clk_i),
+    .rst_ni     (rst_ni),
+    .tl_i       (rom_tl_i),
+    .tl_o       (rom_tl_o),
+    .tl_win_o   (tl_rom_h2d),
+    .tl_win_i   (tl_rom_d2h),
 
-      .intg_err_o (rom_reg_integrity_error),
+    .intg_err_o (rom_reg_integrity_error),
 
-      .devmode_i  (1'b1)
-    );
+    .devmode_i  (1'b1)
+  );
 
-  // The ROM ===================================================================
+  // Bus -> ROM adapter ========================================================
 
-  // ROM_CTRL_ROM_SIZE is auto-generated by regtool and comes from the bus window size, measured in
-  // bytes of content (i.e. 4 times the number of 32 bit words).
-  localparam int unsigned RomSizeByte = ROM_CTRL_ROM_SIZE;
-  localparam int unsigned RomSizeWords = RomSizeByte >> 2;
-  localparam int unsigned RomIndexWidth = vbits(RomSizeWords);
-
-  logic                     rom_req;
-  logic [RomIndexWidth-1:0] rom_index;
-  logic [39:0]              rom_rdata;
-  logic                     rom_rvalid;
-
-  logic                     rom_integrity_error;
+  logic rom_integrity_error;
 
   tlul_adapter_sram #(
     .SramAw(RomIndexWidth),
@@ -82,25 +121,51 @@
     .tl_i         (tl_rom_h2d[0]),
     .tl_o         (tl_rom_d2h[0]),
     .en_ifetch_i  (tlul_pkg::InstrEn),
-    .req_o        (rom_req),
+    .req_o        (bus_rom_req),
     .req_type_o   (),
-    .gnt_i        (1'b1),
+    .gnt_i        (bus_rom_gnt),
     .we_o         (),
-    .addr_o       (rom_index),
+    .addr_o       (bus_rom_index),
     .wdata_o      (),
     .wmask_o      (),
     .intg_error_o (rom_integrity_error),
-    .rdata_i      (rom_rdata[31:0]),
-    .rvalid_i     (rom_rvalid),
+    .rdata_i      (bus_rom_rdata[31:0]),
+    .rvalid_i     (bus_rom_rvalid),
+    // TODO: Send an error on access when locked
     .rerror_i     (2'b00)
   );
 
+  // The mux ===================================================================
+
+  logic mux_alert;
+
+  rom_ctrl_mux #(
+    .AW (RomIndexWidth)
+  ) u_mux (
+    .clk_i        (clk_i),
+    .rst_ni       (rst_ni),
+    .sel_i        (rom_select),
+    .bus_addr_i   (bus_rom_index),
+    .bus_req_i    (bus_rom_req),
+    .bus_gnt_o    (bus_rom_gnt),
+    .bus_rdata_o  (bus_rom_rdata),
+    .bus_rvalid_o (bus_rom_rvalid),
+    .chk_addr_i   (checker_rom_index),
+    .chk_rdata_o  (checker_rom_rdata),
+    .rom_addr_o   (rom_index),
+    .rom_req_o    (rom_req),
+    .rom_rdata_i  (rom_rdata),
+    .rom_rvalid_i (rom_rvalid),
+    .alert_o      (mux_alert)
+  );
+
+  // The ROM itself ============================================================
+
   prim_rom_adv #(
     .Width       (40),
     .Depth       (RomSizeWords),
     .MemInitFile (BootRomInitFile)
-  ) u_rom
-   (
+  ) u_rom (
     .clk_i    (clk_i),
     .rst_ni   (rst_ni),
     .req_i    (rom_req),
@@ -114,10 +179,13 @@
   //       moment, however, we're actually generating the ECC data in u_tl_adapter_rom. That should
   //       go away soonish but, until then, waive the fact that we're not looking at the top bits of
   //       rom_rdata.
-  logic unused_rom_rdata_top;
-  assign unused_rom_rdata_top = &{1'b0, rom_rdata[39:32]};
+  logic unused_bus_rom_rdata_top;
+  assign unused_bus_rom_rdata_top = &{1'b0, bus_rom_rdata[39:32]};
 
-  // Registers =================================================================
+  // Zero expand checker rdata to pass to KMAC
+  assign kmac_rom_data = {24'd0, checker_rom_rdata};
+
+  // Register block ============================================================
 
   rom_ctrl_regs_reg2hw_t reg2hw;
   rom_ctrl_regs_hw2reg_t hw2reg;
@@ -134,14 +202,94 @@
     .devmode_i  (1'b1)
    );
 
+  // The checker FSM ===========================================================
+
+  logic [255:0] digest_q, exp_digest_q;
+  logic [255:0] digest_d;
+  logic         digest_de;
+  logic [31:0]  exp_digest_word_d;
+  logic         exp_digest_de;
+  logic [2:0]   exp_digest_idx;
+
+  logic         checker_alert;
+
+  if (SkipCheck) begin : gen_no_check
+
+    logic unused_bits_no_check;
+    assign unused_bits_no_check = &{1'b0, digest_q, exp_digest_q,
+                                    kmac_rom_rdy, kmac_done, kmac_digest};
+    assign digest_d = '0;
+    assign digest_de = 1'b0;
+    assign exp_digest_word_d = '0;
+    assign exp_digest_de = 1'b0;
+    assign exp_digest_idx = '0;
+    assign pwrmgr_data_o = '{done: 1'b1, good: 1'b1};
+    assign keymgr_data_o = '{data: '0, valid: 1'b0, last: 1'b0};
+    assign kmac_rom_vld = 1'b0;
+    assign kmac_rom_last = 1'b0;
+    // Setting this to 0 ensures the mux will give access to the TL bus
+    assign rom_select = 1'b0;
+    assign checker_rom_index = '0;
+    assign checker_alert = 1'b0;
+
+  end else begin : gen_with_check
+
+    rom_ctrl_fsm #(
+      .RomDepth (RomSizeWords),
+      .TopCount (8)
+    ) u_checker_fsm (
+      .clk_i                (clk_i),
+      .rst_ni               (rst_ni),
+      .digest_i             (digest_q),
+      .exp_digest_i         (exp_digest_q),
+      .digest_o             (digest_d),
+      .digest_vld_o         (digest_de),
+      .exp_digest_o         (exp_digest_word_d),
+      .exp_digest_vld_o     (exp_digest_de),
+      .exp_digest_idx_o     (exp_digest_idx),
+      .pwrmgr_data_o        (pwrmgr_data_o),
+      .keymgr_data_o        (keymgr_data_o),
+      .kmac_rom_rdy_i       (kmac_rom_rdy),
+      .kmac_rom_vld_o       (kmac_rom_vld),
+      .kmac_rom_last_o      (kmac_rom_last),
+      .kmac_done_i          (kmac_done),
+      .kmac_digest_i        (kmac_digest),
+      .rom_select_o         (rom_select),
+      .rom_addr_o           (checker_rom_index),
+      .rom_data_i           (checker_rom_rdata[31:0]),
+      .alert_o              (checker_alert)
+    );
+
+  end
+
+  // Register data =============================================================
+
+  // DIGEST and EXP_DIGEST registers
+
+  // Repack signals to convert between the view expected by rom_ctrl_reg_pkg for CSRs and the view
+  // expected by rom_ctrl_fsm. Register 0 of a multi-reg appears as the low bits of the packed data.
+  for (genvar i = 0; i < 8; i++) begin: gen_csr_digest
+    localparam int TopBitInt = 32 * i + 31;
+    localparam bit [7:0] TopBit = TopBitInt[7:0];
+
+    assign hw2reg.digest[i].d = digest_d[TopBit -: 32];
+    assign hw2reg.digest[i].de = digest_de;
+
+    assign hw2reg.exp_digest[i].d = exp_digest_word_d;
+    assign hw2reg.exp_digest[i].de = exp_digest_de && (i[2:0] == exp_digest_idx);
+
+    assign digest_q[TopBit -: 32] = reg2hw.digest[i].q;
+    assign exp_digest_q[TopBit -: 32] = reg2hw.exp_digest[i].q;
+  end
+
   logic bus_integrity_error;
   assign bus_integrity_error = rom_reg_integrity_error | rom_integrity_error | reg_integrity_error;
 
   // FATAL_ALERT_CAUSE register
+  assign hw2reg.fatal_alert_cause.checker_error.d  = checker_alert | mux_alert;
+  assign hw2reg.fatal_alert_cause.checker_error.de = checker_alert | mux_alert;
   assign hw2reg.fatal_alert_cause.integrity_error.d  = bus_integrity_error;
   assign hw2reg.fatal_alert_cause.integrity_error.de = bus_integrity_error;
-  assign hw2reg.fatal_alert_cause.dummy.d  = 1'b0;
-  assign hw2reg.fatal_alert_cause.dummy.de = 1'b0;
 
   // Alert generation ==========================================================
 
@@ -150,7 +298,7 @@
                                   reg2hw.alert_test.qe;
 
   logic [NumAlerts-1:0] alerts;
-  assign alerts[AlertFatal] = reg_integrity_error;
+  assign alerts[AlertFatal] = reg_integrity_error | checker_alert | mux_alert;
 
   for (genvar i = 0; i < NumAlerts; i++) begin: gen_alert_tx
     prim_alert_sender #(
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_compare.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_compare.sv
new file mode 100644
index 0000000..08b255d
--- /dev/null
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_compare.sv
@@ -0,0 +1,161 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+//
+// The comparator inside the ROM checker
+//
+// This module is in charge of comparing the digest that was computed over the ROM data with the
+// expected digest stored in the top few words.
+//
+//
+// TODO: Think properly about hardening here. A glitch that messes up the comparison isn't a
+//       show-stopper (because the attacker will still have the wrong CreatorRootKey in the key
+//       manager), but glitching the FSM our counter could probably confuse some of the other blocks
+//       that we communicate with.
+
+`include "prim_assert.sv"
+
+module rom_ctrl_compare #(
+  parameter int NumWords = 2
+) (
+  input logic                        clk_i,
+  input logic                        rst_ni,
+
+  input logic                        start_i,
+  output logic                       done_o,
+  output logic                       good_o,
+
+  // CSR inputs for DIGEST and EXP_DIGEST. Ordered with word 0 as LSB.
+  input logic [NumWords*32-1:0]      digest_i,
+  input logic [NumWords*32-1:0]      exp_digest_i,
+
+  // To keymgr
+  output rom_ctrl_pkg::keymgr_data_t keymgr_data_o,
+
+  // To alert system
+  output logic                       alert_o
+);
+
+  import prim_util_pkg::vbits;
+
+  `ASSERT_INIT(NumWordsPositive_A, 0 < NumWords)
+
+  localparam int AW = vbits(NumWords);
+
+  localparam int          EndAddrInt  = NumWords;
+  localparam int          LastAddrInt = NumWords - 1;
+
+  // Note that if NumWords is a power of 2 then EndAddr will be zero. That's ok: we're just using a
+  // comparison with EndAddr to check that the address counter hasn't started wandering around when
+  // we're in the wrong state.
+  localparam bit [AW-1:0] EndAddr     = EndAddrInt[AW-1:0];
+  localparam bit [AW-1:0] LastAddr    = LastAddrInt[AW-1:0];
+
+  logic [AW-1:0] addr_q;
+
+  // This module must wait until triggered by a write to start_i. At that point, it cycles through
+  // the words of DIGEST and EXP_DIGEST, comparing them to one another and passing each digest word
+  // to the key manager. Finally, it gets to the Done state.
+  //
+  // States:
+  //
+  //    Waiting
+  //    Checking
+  //    Done
+  //
+  // Encoding generated with:
+  // $ util/design/sparse-fsm-encode.py -d 3 -m 3 -n 5 -s 1 --language=sv
+  //
+  // Hamming distance histogram:
+  //
+  //  0: --
+  //  1: --
+  //  2: --
+  //  3: |||||||||||||||||||| (66.67%)
+  //  4: |||||||||| (33.33%)
+  //  5: --
+  //
+  // Minimum Hamming distance: 3
+  // Maximum Hamming distance: 4
+  // Minimum Hamming weight: 1
+  // Maximum Hamming weight: 3
+  //
+  typedef enum logic [4:0] {
+    Waiting  = 5'b00100,
+    Checking = 5'b10010,
+    Done     = 5'b11001
+  } state_e;
+
+  logic [4:0]  state_q, state_d;
+  logic        matches_q, matches_d;
+  logic        fsm_alert;
+
+  prim_flop #(.Width(5), .ResetValue(Waiting))
+  u_state_regs (
+    .clk_i  (clk_i),
+    .rst_ni (rst_ni),
+    .d_i    (state_d),
+    .q_o    (state_q)
+  );
+
+  always_comb begin
+    state_d = state_q;
+    fsm_alert = 1'b0;
+    unique case (state_q)
+      Waiting: begin
+        if (start_i) state_d = Checking;
+      end
+      Checking: begin
+        if (addr_q == LastAddr) state_d = Done;
+      end
+      Done: begin
+        // Final state
+      end
+      default: fsm_alert = 1'b1;
+    endcase
+  end
+
+  // start_i should only be signalled when we're in the Waiting state
+  logic start_alert;
+  assign start_alert = start_i && (state_q != Waiting);
+
+  // addr_q should be zero when we're in the Waiting state
+  logic wait_addr_alert;
+  assign wait_addr_alert = (state_q == Waiting) && (addr_q != '0);
+
+  // addr_q should be EndAddr when we're in the Done state
+  logic done_addr_alert;
+  assign done_addr_alert = (state_q == Done) && (addr_q != EndAddr);
+
+  // Increment addr_q on each cycle when in Checking
+  always @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      addr_q    <= '0;
+      matches_q <= 1'b1;
+    end else begin
+      if (state_q == Checking) begin
+        addr_q    <= addr_q + AW'(1);
+        matches_q <= matches_d;
+      end
+    end
+  end
+
+  logic [AW+5-1:0] digest_idx;
+  logic [31:0]     digest_word, exp_digest_word;
+  assign digest_idx = {addr_q, 5'd31};
+  assign digest_word = digest_i[digest_idx -: 32];
+  assign exp_digest_word = exp_digest_i[digest_idx -: 32];
+  assign matches_d = matches_q && (digest_word == exp_digest_word);
+
+  logic word_vld, word_last;
+  assign word_vld  = (state_q == Checking);
+  assign word_last = (addr_q == LastAddr);
+  assign keymgr_data_o = '{data: digest_word, valid: word_vld, last: word_last};
+
+  assign done_o = (state_q == Done);
+  assign good_o = matches_q;
+
+  assign alert_o = fsm_alert | start_alert | wait_addr_alert | done_addr_alert;
+
+endmodule
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_counter.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_counter.sv
new file mode 100644
index 0000000..217eadb
--- /dev/null
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_counter.sv
@@ -0,0 +1,106 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+//
+// A counter module that drives the ROM accesses from the checker.
+//
+// This module doesn't need state hardening: an attacker that glitches its behaviour can stall the
+// chip or read ROM data in the wrong order. Assuming we've picked a key for the ROM that ensures
+// all words have different values, exploiting a glitch in this module to hide a ROM modification
+// would still need a pre-image attack on SHA-3.
+//
+// RomDepth is the number of words in the ROM. RomTopCount is the number of those words (at the top
+// of the address space) that are considered part of the expected hash.
+//
+// When it comes out of reset, the module starts reading from address zero. Once the reading is
+// done, it will signal done_o. The surrounding (hardened) design should check that done_o never has
+// a high -> low transition.
+//
+// The rom_addr_o signal should be connected to the stateful mux that controls access to ROM. This
+// mux gives access to the rom_ctrl_counter until done_o is asserted.
+//
+// The data_* signals are used to handshake with KMAC, although the surrounding FSM will step in
+// once we've got to the top of memory. The counter uses the output buffer on the ROM instance to
+// hold data and drives rom_addr_o and data_vld_o to make a rdy/vld interface with the ROM output.
+// This interface should signal things correctly until done_o goes high. data_last_nontop_o is set
+// on the last word before the top RomTopCount words.
+//
+
+`include "prim_assert.sv"
+
+module rom_ctrl_counter
+  import prim_util_pkg::vbits;
+#(
+  parameter int RomDepth = 16,
+  parameter int RomTopCount = 2
+) (
+  input                        clk_i,
+  input                        rst_ni,
+
+  output                       done_o,
+
+  output [vbits(RomDepth)-1:0] rom_addr_o,
+
+  input                        data_rdy_i,
+  output                       data_vld_o,
+  output                       data_last_nontop_o
+);
+
+  // The number of ROM entries that should be hashed. We assume there are at least 2, so that we can
+  // register the data_last_nontop_o signal.
+  localparam int RomNonTopCount = RomDepth - RomTopCount;
+
+  `ASSERT_INIT(TopCountValid_A, 1 <= RomTopCount && RomTopCount < RomDepth)
+  `ASSERT_INIT(NonTopCountValid_A, 2 <= RomNonTopCount)
+
+  localparam int AW = vbits(RomDepth);
+
+  localparam int TopAddrInt = RomDepth - 1;
+  localparam int TNTAddrInt = RomNonTopCount - 2;
+
+  localparam bit [AW-1:0] TopAddr = TopAddrInt[AW-1:0];
+  localparam bit [AW-1:0] TNTAddr = TNTAddrInt[AW-1:0];
+
+  logic          go;
+  logic          vld_q;
+  logic [AW-1:0] addr_q, addr_d;
+  logic          done_q, done_d;
+  logic          last_nontop_q, last_nontop_d;
+
+  always @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      vld_q         <= 1'b0;
+      addr_q        <= '0;
+      done_q        <= 1'b0;
+      last_nontop_q <= 1'b0;
+    end else begin
+      // ROM data is valid from one cycle after reset onwards (once we reach the top of ROM, we
+      // signal done_o, after which data_vld_o is unused).
+      vld_q  <= 1'b1;
+
+      // Step the FSM if go is true. This factors in stalls from the data_rdy_i signal and also
+      // waits one cycle after reset (to avoid missing the first word if data_rdy_i is true
+      // immediately after reset).
+      if (go) begin
+        addr_q        <= addr_d;
+        done_q        <= done_d;
+        last_nontop_q <= last_nontop_d;
+      end
+    end
+  end
+
+  always_comb begin
+    go = (data_rdy_i && data_vld_o) && !done_q;
+
+    addr_d        = addr_q + {{AW-1{1'b0}}, 1'b1};
+    done_d        = addr_q == TopAddr;
+    last_nontop_d = addr_q == TNTAddr;
+  end
+
+  assign done_o             = done_q;
+  assign rom_addr_o         = addr_q;
+  assign data_vld_o         = vld_q;
+  assign data_last_nontop_o = last_nontop_q;
+
+endmodule
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_fsm.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_fsm.sv
new file mode 100644
index 0000000..a0f1eb0
--- /dev/null
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_fsm.sv
@@ -0,0 +1,255 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+//
+// The ROM checker FSM module
+//
+
+module rom_ctrl_fsm
+  import prim_util_pkg::vbits;
+#(
+  parameter int RomDepth = 16,
+  parameter int TopCount = 8
+) (
+  input logic                        clk_i,
+  input logic                        rst_ni,
+
+  // CSR inputs for DIGEST and EXP_DIGEST. To make the indexing look nicer, these are ordered so
+  // that DIGEST_0 is the bottom 32 bits (they get reversed while we're shuffling around the wires
+  // in rom_ctrl).
+  input logic [TopCount*32-1:0]      digest_i,
+  input logic [TopCount*32-1:0]      exp_digest_i,
+
+  // CSR outputs for DIGEST and EXP_DIGEST. Ordered with word 0 as LSB.
+  output logic [TopCount*32-1:0]     digest_o,
+  output logic                       digest_vld_o,
+  output logic [31:0]                exp_digest_o,
+  output logic                       exp_digest_vld_o,
+  output logic [vbits(TopCount)-1:0] exp_digest_idx_o,
+
+  // To power manager and key manager
+  output rom_ctrl_pkg::pwrmgr_data_t pwrmgr_data_o,
+  output rom_ctrl_pkg::keymgr_data_t keymgr_data_o,
+
+  // To KMAC (ROM data)
+  input logic                        kmac_rom_rdy_i,
+  output logic                       kmac_rom_vld_o,
+  output logic                       kmac_rom_last_o,
+
+  // To KMAC (digest data)
+  input logic                        kmac_done_i,
+  input logic [TopCount*32-1:0]      kmac_digest_i,
+
+  // To ROM mux
+  output logic                       rom_select_o,
+  output logic [vbits(RomDepth)-1:0] rom_addr_o,
+
+  // Raw bits from ROM
+  input logic [31:0]                 rom_data_i,
+
+  // To alert system
+  output logic                       alert_o
+);
+
+  localparam int AW = vbits(RomDepth);
+  localparam int TAW = vbits(TopCount);
+
+  localparam int          TopStartAddrInt = RomDepth - TopCount;
+  localparam bit [AW-1:0] TopStartAddr    = TopStartAddrInt[AW-1:0];
+
+  // The counter / address generator
+  logic          counter_done;
+  logic [AW-1:0] counter_addr;
+  logic          counter_data_rdy, counter_data_vld;
+  logic          counter_lnt;
+  rom_ctrl_counter #(
+    .RomDepth (RomDepth),
+    .RomTopCount (TopCount)
+  ) u_counter (
+    .clk_i              (clk_i),
+    .rst_ni             (rst_ni),
+    .done_o             (counter_done),
+    .rom_addr_o         (counter_addr),
+    .data_rdy_i         (counter_data_rdy),
+    .data_vld_o         (counter_data_vld),
+    .data_last_nontop_o (counter_lnt)
+  );
+
+  // The compare block (responsible for comparing CSR data and forwarding it to the key manager)
+  logic start_checker_q;
+  logic checker_done, checker_good, checker_alert;
+  rom_ctrl_compare #(
+    .NumWords (TopCount)
+  ) u_compare (
+    .clk_i         (clk_i),
+    .rst_ni        (rst_ni),
+    .start_i       (start_checker_q),
+    .done_o        (checker_done),
+    .good_o        (checker_good),
+    .digest_i      (digest_i),
+    .exp_digest_i  (exp_digest_i),
+    .keymgr_data_o (keymgr_data_o),
+    .alert_o       (checker_alert)
+  );
+
+  // Main FSM
+  //
+  // There are the following logical states
+  //
+  //    ReadingLow:   We're reading the low part of ROM and passing it to KMAC
+  //    ReadingHigh:  We're reading the high part of ROM and waiting for KMAC
+  //    RomAhead:     We've finished reading the high part of ROM, but are still waiting for KMAC
+  //    KmacAhead:    KMAC is done, but we're still reading the high part of ROM
+  //    Checking:     We are comparing DIGEST and EXP_DIGEST and sending data to keymgr
+  //    Done:         Terminal state
+  //
+  // The FSM is linear, except for the branch where reading the high part of ROM races with getting
+  // the result back from KMAC.
+  //
+  //     digraph fsm {
+  //       ReadingLow -> ReadingHigh;
+  //       ReadingHigh -> RomAhead;
+  //       ReadingHigh -> KmacAhead;
+  //       RomAhead -> Checking;
+  //       KmacAhead -> Checking;
+  //       Checking -> Done;
+  //       Done [peripheries=2];
+  //     }
+  //
+  // Encoding generated with:
+  // $ util/design/sparse-fsm-encode.py -d 3 -m 6 -n 6 -s 2 --language=sv
+  //
+  // Hamming distance histogram:
+  //
+  //  0: --
+  //  1: --
+  //  2: --
+  //  3: |||||||||||||||||||| (53.33%)
+  //  4: ||||||||||||||| (40.00%)
+  //  5: || (6.67%)
+  //  6: --
+  //
+  // Minimum Hamming distance: 3
+  // Maximum Hamming distance: 5
+  // Minimum Hamming weight: 1
+  // Maximum Hamming weight: 5
+  //
+  typedef enum logic [5:0] {
+    ReadingLow  = 6'b111101,
+    ReadingHigh = 6'b110110,
+    RomAhead    = 6'b000011,
+    KmacAhead   = 6'b101010,
+    Checking    = 6'b010000,
+    Done        = 6'b001100
+  } state_e;
+
+  logic [5:0]  state_q, state_d;
+  logic        fsm_alert;
+
+  prim_flop #(.Width(6), .ResetValue(ReadingLow))
+  u_state_regs (
+    .clk_i  (clk_i),
+    .rst_ni (rst_ni),
+    .d_i    (state_d),
+    .q_o    (state_q)
+  );
+
+  always_comb begin
+    state_d = state_q;
+    fsm_alert = 1'b0;
+
+    unique case (state_q)
+      ReadingLow: begin
+        // Switch to ReadingHigh when counter_lnt is true and kmac_rom_rdy_i & kmac_rom_vld_o
+        // (implying that the transaction went through)
+        if (counter_lnt && kmac_rom_rdy_i && kmac_rom_vld_o) begin
+          state_d = ReadingHigh;
+        end
+      end
+
+      ReadingHigh: begin
+        unique case ({kmac_done_i, counter_done})
+          2'b01: state_d = RomAhead;
+          2'b10: state_d = KmacAhead;
+          2'b11: state_d = Checking;
+          default: ; // No change
+        endcase
+      end
+
+      RomAhead: begin
+        if (kmac_done_i) state_d = Checking;
+      end
+
+      KmacAhead: begin
+        if (counter_done) state_d = Checking;
+      end
+
+      Checking: begin
+        if (checker_done) state_d = Done;
+      end
+
+      Done: begin
+        // Final state
+      end
+
+      default: begin
+        // Invalid state.
+        fsm_alert = 1'b1;
+      end
+    endcase
+  end
+
+  // Route digest signals coming back from KMAC straight to the CSRs
+  assign digest_o     = kmac_digest_i;
+  assign digest_vld_o = kmac_done_i;
+
+  // Snoop on ROM reads to populate EXP_DIGEST, one word at a time
+  logic reading_top;
+  logic [AW-1:0] rel_addr_wide;
+  logic [TAW-1:0] rel_addr;
+
+  assign reading_top = (state_q == ReadingHigh || state_q == KmacAhead);
+  assign rel_addr_wide = counter_addr - TopStartAddr;
+  assign rel_addr = rel_addr_wide[TAW-1:0];
+
+  // The top bits of rel_addr_wide should always be zero (because TAW bits should be enough to
+  // encode the difference between counter_addr and TopStartAddr)
+  `ASSERT(RelAddrWide_A, ~|rel_addr_wide[AW-1:TAW])
+  logic unused_top_rel_addr_wide;
+  assign unused_top_rel_addr_wide = |rel_addr_wide[AW-1:TAW];
+
+  assign exp_digest_o = rom_data_i;
+  assign exp_digest_vld_o = reading_top;
+  assign exp_digest_idx_o = rel_addr;
+
+  // Pass the 'done' and 'good' signals directly from the checker
+  assign pwrmgr_data_o = '{done: (state_q == Done), good: checker_good};
+
+  // KMAC rom data interface
+  //
+  // This is almost handled by the counter, but we interpose ourselves once all but the top words
+  // have been sent, squashing the extra data beats that come out as the counter reads through the
+  // top words.
+  assign counter_data_rdy = kmac_rom_rdy_i | (state_q != ReadingLow);
+  assign kmac_rom_vld_o = counter_data_vld & (state_q == ReadingLow);
+  assign kmac_rom_last_o = counter_lnt;
+
+  // Start the checker when transitioning into the "Checking" state
+  always @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      start_checker_q <= 1'b0;
+    end else begin
+      start_checker_q <= (state_q != Checking) && (state_d == Checking);
+    end
+  end
+
+  // We keep control of the ROM mux from reset until we're done
+  assign rom_select_o = (state_q != Done);
+  assign rom_addr_o = counter_addr;
+
+  // TODO: There are lots more checks that we could do here (things like spotting vld signals that
+  //       occur when we're in an FSM state that doesn't expect them)
+  assign alert_o = fsm_alert | checker_alert;
+
+endmodule
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv
new file mode 100644
index 0000000..9aec4bb
--- /dev/null
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv
@@ -0,0 +1,64 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+//
+// The mux to select between ROM inputs
+//
+
+module rom_ctrl_mux #(
+  parameter int AW = 8
+) (
+  input logic           clk_i,
+  input logic           rst_ni,
+
+  // select signal. 1 = checker; 0 = bus
+  input logic           sel_i,
+
+  // Interface for bus
+  input logic [AW-1:0]  bus_addr_i,
+  input logic           bus_req_i,
+  output logic          bus_gnt_o,
+  output logic [39:0]   bus_rdata_o,
+  output logic          bus_rvalid_o,
+
+  // Interface for ROM checker
+  input logic [AW-1:0]  chk_addr_i,
+  output logic [39:0]   chk_rdata_o,
+
+  // Interface for ROM
+  output logic [AW-1:0] rom_addr_o,
+  output logic          rom_req_o,
+  input logic [39:0]    rom_rdata_i,
+  input logic           rom_rvalid_i,
+
+  // Alert output
+  output logic          alert_o
+);
+
+  // TODO: sel_q will definitely need to be multi-bit for glitch resistance. We'll probably also
+  // have to chase the "signal bit signals" back a bit further through the logic too.
+  logic sel_q;
+  always @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      sel_q <= 1'b1;
+    end else begin
+      sel_q  <= sel_q & sel_i;
+    end
+  end
+
+  // Spot if the select signal becomes one again after it went to zero
+  assign alert_o = sel_i & ~sel_q;
+
+  // The bus can have access every cycle, once the select signal has gone to zero
+  assign bus_gnt_o    = ~sel_q;
+  assign bus_rdata_o  = rom_rdata_i;
+  // A high rom_rvalid_i is a response to a bus request if sel_i was zero on the previous cycle.
+  assign bus_rvalid_o = ~sel_q & rom_rvalid_i;
+
+  assign chk_rdata_o = rom_rdata_i;
+
+  assign rom_addr_o = sel_i ? chk_addr_i : bus_addr_i;
+  assign rom_req_o  = sel_i ? 1'b1       : bus_req_i;
+
+endmodule
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_pkg.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_pkg.sv
index 05d3786..532274b 100644
--- a/hw/ip/rom_ctrl/rtl/rom_ctrl_pkg.sv
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_pkg.sv
@@ -8,4 +8,15 @@
 
   parameter int AlertFatal = 0;
 
+  typedef struct packed {
+    logic done;
+    logic good;
+  } pwrmgr_data_t;
+
+  typedef struct packed {
+    logic [31:0] data;
+    logic        valid;
+    logic        last;
+  } keymgr_data_t;
+
 endpackage
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_reg_pkg.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_reg_pkg.sv
index 0d95045..e4adcad 100644
--- a/hw/ip/rom_ctrl/rtl/rom_ctrl_reg_pkg.sv
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_reg_pkg.sv
@@ -10,7 +10,7 @@
   parameter int NumAlerts = 1;
 
   // Address widths within the block
-  parameter int RegsAw = 3;
+  parameter int RegsAw = 7;
   parameter int RomAw = 14;
 
   ///////////////////////////////////////////////
@@ -23,29 +23,67 @@
   } rom_ctrl_reg2hw_alert_test_reg_t;
 
   typedef struct packed {
+    logic [31:0] q;
+  } rom_ctrl_reg2hw_digest_mreg_t;
+
+  typedef struct packed {
+    logic [31:0] q;
+  } rom_ctrl_reg2hw_exp_digest_mreg_t;
+
+  typedef struct packed {
+    struct packed {
+      logic        d;
+      logic        de;
+    } checker_error;
     struct packed {
       logic        d;
       logic        de;
     } integrity_error;
-    struct packed {
-      logic        d;
-      logic        de;
-    } dummy;
   } rom_ctrl_hw2reg_fatal_alert_cause_reg_t;
 
+  typedef struct packed {
+    logic [31:0] d;
+    logic        de;
+  } rom_ctrl_hw2reg_digest_mreg_t;
+
+  typedef struct packed {
+    logic [31:0] d;
+    logic        de;
+  } rom_ctrl_hw2reg_exp_digest_mreg_t;
+
   // Register -> HW type for regs interface
   typedef struct packed {
-    rom_ctrl_reg2hw_alert_test_reg_t alert_test; // [1:0]
+    rom_ctrl_reg2hw_alert_test_reg_t alert_test; // [513:512]
+    rom_ctrl_reg2hw_digest_mreg_t [7:0] digest; // [511:256]
+    rom_ctrl_reg2hw_exp_digest_mreg_t [7:0] exp_digest; // [255:0]
   } rom_ctrl_regs_reg2hw_t;
 
   // HW -> register type for regs interface
   typedef struct packed {
-    rom_ctrl_hw2reg_fatal_alert_cause_reg_t fatal_alert_cause; // [3:0]
+    rom_ctrl_hw2reg_fatal_alert_cause_reg_t fatal_alert_cause; // [531:528]
+    rom_ctrl_hw2reg_digest_mreg_t [7:0] digest; // [527:264]
+    rom_ctrl_hw2reg_exp_digest_mreg_t [7:0] exp_digest; // [263:0]
   } rom_ctrl_regs_hw2reg_t;
 
   // Register offsets for regs interface
-  parameter logic [RegsAw-1:0] ROM_CTRL_ALERT_TEST_OFFSET = 3'h 0;
-  parameter logic [RegsAw-1:0] ROM_CTRL_FATAL_ALERT_CAUSE_OFFSET = 3'h 4;
+  parameter logic [RegsAw-1:0] ROM_CTRL_ALERT_TEST_OFFSET = 7'h 0;
+  parameter logic [RegsAw-1:0] ROM_CTRL_FATAL_ALERT_CAUSE_OFFSET = 7'h 4;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_0_OFFSET = 7'h 8;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_1_OFFSET = 7'h c;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_2_OFFSET = 7'h 10;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_3_OFFSET = 7'h 14;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_4_OFFSET = 7'h 18;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_5_OFFSET = 7'h 1c;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_6_OFFSET = 7'h 20;
+  parameter logic [RegsAw-1:0] ROM_CTRL_DIGEST_7_OFFSET = 7'h 24;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_0_OFFSET = 7'h 28;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_1_OFFSET = 7'h 2c;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_2_OFFSET = 7'h 30;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_3_OFFSET = 7'h 34;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_4_OFFSET = 7'h 38;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_5_OFFSET = 7'h 3c;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_6_OFFSET = 7'h 40;
+  parameter logic [RegsAw-1:0] ROM_CTRL_EXP_DIGEST_7_OFFSET = 7'h 44;
 
   // Reset values for hwext registers and their fields for regs interface
   parameter logic [0:0] ROM_CTRL_ALERT_TEST_RESVAL = 1'h 0;
@@ -54,13 +92,45 @@
   // Register index for regs interface
   typedef enum int {
     ROM_CTRL_ALERT_TEST,
-    ROM_CTRL_FATAL_ALERT_CAUSE
+    ROM_CTRL_FATAL_ALERT_CAUSE,
+    ROM_CTRL_DIGEST_0,
+    ROM_CTRL_DIGEST_1,
+    ROM_CTRL_DIGEST_2,
+    ROM_CTRL_DIGEST_3,
+    ROM_CTRL_DIGEST_4,
+    ROM_CTRL_DIGEST_5,
+    ROM_CTRL_DIGEST_6,
+    ROM_CTRL_DIGEST_7,
+    ROM_CTRL_EXP_DIGEST_0,
+    ROM_CTRL_EXP_DIGEST_1,
+    ROM_CTRL_EXP_DIGEST_2,
+    ROM_CTRL_EXP_DIGEST_3,
+    ROM_CTRL_EXP_DIGEST_4,
+    ROM_CTRL_EXP_DIGEST_5,
+    ROM_CTRL_EXP_DIGEST_6,
+    ROM_CTRL_EXP_DIGEST_7
   } rom_ctrl_regs_id_e;
 
   // Register width information to check illegal writes for regs interface
-  parameter logic [3:0] ROM_CTRL_REGS_PERMIT [2] = '{
-    4'b 0001, // index[0] ROM_CTRL_ALERT_TEST
-    4'b 0001  // index[1] ROM_CTRL_FATAL_ALERT_CAUSE
+  parameter logic [3:0] ROM_CTRL_REGS_PERMIT [18] = '{
+    4'b 0001, // index[ 0] ROM_CTRL_ALERT_TEST
+    4'b 0001, // index[ 1] ROM_CTRL_FATAL_ALERT_CAUSE
+    4'b 1111, // index[ 2] ROM_CTRL_DIGEST_0
+    4'b 1111, // index[ 3] ROM_CTRL_DIGEST_1
+    4'b 1111, // index[ 4] ROM_CTRL_DIGEST_2
+    4'b 1111, // index[ 5] ROM_CTRL_DIGEST_3
+    4'b 1111, // index[ 6] ROM_CTRL_DIGEST_4
+    4'b 1111, // index[ 7] ROM_CTRL_DIGEST_5
+    4'b 1111, // index[ 8] ROM_CTRL_DIGEST_6
+    4'b 1111, // index[ 9] ROM_CTRL_DIGEST_7
+    4'b 1111, // index[10] ROM_CTRL_EXP_DIGEST_0
+    4'b 1111, // index[11] ROM_CTRL_EXP_DIGEST_1
+    4'b 1111, // index[12] ROM_CTRL_EXP_DIGEST_2
+    4'b 1111, // index[13] ROM_CTRL_EXP_DIGEST_3
+    4'b 1111, // index[14] ROM_CTRL_EXP_DIGEST_4
+    4'b 1111, // index[15] ROM_CTRL_EXP_DIGEST_5
+    4'b 1111, // index[16] ROM_CTRL_EXP_DIGEST_6
+    4'b 1111  // index[17] ROM_CTRL_EXP_DIGEST_7
   };
 
   // Window parameters for rom interface
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_regs_reg_top.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_regs_reg_top.sv
index 14fc2eb..7401271 100644
--- a/hw/ip/rom_ctrl/rtl/rom_ctrl_regs_reg_top.sv
+++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_regs_reg_top.sv
@@ -25,7 +25,7 @@
 
   import rom_ctrl_reg_pkg::* ;
 
-  localparam int AW = 3;
+  localparam int AW = 7;
   localparam int DW = 32;
   localparam int DBW = DW/8;                    // Byte Width
 
@@ -106,8 +106,24 @@
   //        or <reg>_{wd|we|qs} if field == 1 or 0
   logic alert_test_wd;
   logic alert_test_we;
+  logic fatal_alert_cause_checker_error_qs;
   logic fatal_alert_cause_integrity_error_qs;
-  logic fatal_alert_cause_dummy_qs;
+  logic [31:0] digest_0_qs;
+  logic [31:0] digest_1_qs;
+  logic [31:0] digest_2_qs;
+  logic [31:0] digest_3_qs;
+  logic [31:0] digest_4_qs;
+  logic [31:0] digest_5_qs;
+  logic [31:0] digest_6_qs;
+  logic [31:0] digest_7_qs;
+  logic [31:0] exp_digest_0_qs;
+  logic [31:0] exp_digest_1_qs;
+  logic [31:0] exp_digest_2_qs;
+  logic [31:0] exp_digest_3_qs;
+  logic [31:0] exp_digest_4_qs;
+  logic [31:0] exp_digest_5_qs;
+  logic [31:0] exp_digest_6_qs;
+  logic [31:0] exp_digest_7_qs;
 
   // Register instances
   // R[alert_test]: V(True)
@@ -128,7 +144,32 @@
 
   // R[fatal_alert_cause]: V(False)
 
-  //   F[integrity_error]: 0:0
+  //   F[checker_error]: 0:0
+  prim_subreg #(
+    .DW      (1),
+    .SWACCESS("RO"),
+    .RESVAL  (1'h0)
+  ) u_fatal_alert_cause_checker_error (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.fatal_alert_cause.checker_error.de),
+    .d      (hw2reg.fatal_alert_cause.checker_error.d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (),
+
+    // to register interface (read)
+    .qs     (fatal_alert_cause_checker_error_qs)
+  );
+
+
+  //   F[integrity_error]: 1:1
   prim_subreg #(
     .DW      (1),
     .SWACCESS("RO"),
@@ -153,12 +194,15 @@
   );
 
 
-  //   F[dummy]: 1:1
+
+  // Subregister 0 of Multireg digest
+  // R[digest_0]: V(False)
+
   prim_subreg #(
-    .DW      (1),
+    .DW      (32),
     .SWACCESS("RO"),
-    .RESVAL  (1'h0)
-  ) u_fatal_alert_cause_dummy (
+    .RESVAL  (32'h0)
+  ) u_digest_0 (
     .clk_i   (clk_i    ),
     .rst_ni  (rst_ni  ),
 
@@ -166,25 +210,433 @@
     .wd     ('0  ),
 
     // from internal hardware
-    .de     (hw2reg.fatal_alert_cause.dummy.de),
-    .d      (hw2reg.fatal_alert_cause.dummy.d ),
+    .de     (hw2reg.digest[0].de),
+    .d      (hw2reg.digest[0].d ),
 
     // to internal hardware
     .qe     (),
-    .q      (),
+    .q      (reg2hw.digest[0].q ),
 
     // to register interface (read)
-    .qs     (fatal_alert_cause_dummy_qs)
+    .qs     (digest_0_qs)
+  );
+
+  // Subregister 1 of Multireg digest
+  // R[digest_1]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_1 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[1].de),
+    .d      (hw2reg.digest[1].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[1].q ),
+
+    // to register interface (read)
+    .qs     (digest_1_qs)
+  );
+
+  // Subregister 2 of Multireg digest
+  // R[digest_2]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_2 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[2].de),
+    .d      (hw2reg.digest[2].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[2].q ),
+
+    // to register interface (read)
+    .qs     (digest_2_qs)
+  );
+
+  // Subregister 3 of Multireg digest
+  // R[digest_3]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_3 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[3].de),
+    .d      (hw2reg.digest[3].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[3].q ),
+
+    // to register interface (read)
+    .qs     (digest_3_qs)
+  );
+
+  // Subregister 4 of Multireg digest
+  // R[digest_4]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_4 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[4].de),
+    .d      (hw2reg.digest[4].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[4].q ),
+
+    // to register interface (read)
+    .qs     (digest_4_qs)
+  );
+
+  // Subregister 5 of Multireg digest
+  // R[digest_5]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_5 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[5].de),
+    .d      (hw2reg.digest[5].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[5].q ),
+
+    // to register interface (read)
+    .qs     (digest_5_qs)
+  );
+
+  // Subregister 6 of Multireg digest
+  // R[digest_6]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_6 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[6].de),
+    .d      (hw2reg.digest[6].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[6].q ),
+
+    // to register interface (read)
+    .qs     (digest_6_qs)
+  );
+
+  // Subregister 7 of Multireg digest
+  // R[digest_7]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_digest_7 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.digest[7].de),
+    .d      (hw2reg.digest[7].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.digest[7].q ),
+
+    // to register interface (read)
+    .qs     (digest_7_qs)
+  );
+
+
+
+  // Subregister 0 of Multireg exp_digest
+  // R[exp_digest_0]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_0 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[0].de),
+    .d      (hw2reg.exp_digest[0].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[0].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_0_qs)
+  );
+
+  // Subregister 1 of Multireg exp_digest
+  // R[exp_digest_1]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_1 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[1].de),
+    .d      (hw2reg.exp_digest[1].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[1].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_1_qs)
+  );
+
+  // Subregister 2 of Multireg exp_digest
+  // R[exp_digest_2]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_2 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[2].de),
+    .d      (hw2reg.exp_digest[2].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[2].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_2_qs)
+  );
+
+  // Subregister 3 of Multireg exp_digest
+  // R[exp_digest_3]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_3 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[3].de),
+    .d      (hw2reg.exp_digest[3].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[3].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_3_qs)
+  );
+
+  // Subregister 4 of Multireg exp_digest
+  // R[exp_digest_4]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_4 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[4].de),
+    .d      (hw2reg.exp_digest[4].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[4].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_4_qs)
+  );
+
+  // Subregister 5 of Multireg exp_digest
+  // R[exp_digest_5]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_5 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[5].de),
+    .d      (hw2reg.exp_digest[5].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[5].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_5_qs)
+  );
+
+  // Subregister 6 of Multireg exp_digest
+  // R[exp_digest_6]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_6 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[6].de),
+    .d      (hw2reg.exp_digest[6].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[6].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_6_qs)
+  );
+
+  // Subregister 7 of Multireg exp_digest
+  // R[exp_digest_7]: V(False)
+
+  prim_subreg #(
+    .DW      (32),
+    .SWACCESS("RO"),
+    .RESVAL  (32'h0)
+  ) u_exp_digest_7 (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    .we     (1'b0),
+    .wd     ('0  ),
+
+    // from internal hardware
+    .de     (hw2reg.exp_digest[7].de),
+    .d      (hw2reg.exp_digest[7].d ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.exp_digest[7].q ),
+
+    // to register interface (read)
+    .qs     (exp_digest_7_qs)
   );
 
 
 
 
-  logic [1:0] addr_hit;
+  logic [17:0] addr_hit;
   always_comb begin
     addr_hit = '0;
-    addr_hit[0] = (reg_addr == ROM_CTRL_ALERT_TEST_OFFSET);
-    addr_hit[1] = (reg_addr == ROM_CTRL_FATAL_ALERT_CAUSE_OFFSET);
+    addr_hit[ 0] = (reg_addr == ROM_CTRL_ALERT_TEST_OFFSET);
+    addr_hit[ 1] = (reg_addr == ROM_CTRL_FATAL_ALERT_CAUSE_OFFSET);
+    addr_hit[ 2] = (reg_addr == ROM_CTRL_DIGEST_0_OFFSET);
+    addr_hit[ 3] = (reg_addr == ROM_CTRL_DIGEST_1_OFFSET);
+    addr_hit[ 4] = (reg_addr == ROM_CTRL_DIGEST_2_OFFSET);
+    addr_hit[ 5] = (reg_addr == ROM_CTRL_DIGEST_3_OFFSET);
+    addr_hit[ 6] = (reg_addr == ROM_CTRL_DIGEST_4_OFFSET);
+    addr_hit[ 7] = (reg_addr == ROM_CTRL_DIGEST_5_OFFSET);
+    addr_hit[ 8] = (reg_addr == ROM_CTRL_DIGEST_6_OFFSET);
+    addr_hit[ 9] = (reg_addr == ROM_CTRL_DIGEST_7_OFFSET);
+    addr_hit[10] = (reg_addr == ROM_CTRL_EXP_DIGEST_0_OFFSET);
+    addr_hit[11] = (reg_addr == ROM_CTRL_EXP_DIGEST_1_OFFSET);
+    addr_hit[12] = (reg_addr == ROM_CTRL_EXP_DIGEST_2_OFFSET);
+    addr_hit[13] = (reg_addr == ROM_CTRL_EXP_DIGEST_3_OFFSET);
+    addr_hit[14] = (reg_addr == ROM_CTRL_EXP_DIGEST_4_OFFSET);
+    addr_hit[15] = (reg_addr == ROM_CTRL_EXP_DIGEST_5_OFFSET);
+    addr_hit[16] = (reg_addr == ROM_CTRL_EXP_DIGEST_6_OFFSET);
+    addr_hit[17] = (reg_addr == ROM_CTRL_EXP_DIGEST_7_OFFSET);
   end
 
   assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
@@ -192,8 +644,24 @@
   // Check sub-word write is permitted
   always_comb begin
     wr_err = 1'b0;
-    if (addr_hit[0] && reg_we && (ROM_CTRL_REGS_PERMIT[0] != (ROM_CTRL_REGS_PERMIT[0] & reg_be))) wr_err = 1'b1 ;
-    if (addr_hit[1] && reg_we && (ROM_CTRL_REGS_PERMIT[1] != (ROM_CTRL_REGS_PERMIT[1] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 0] && reg_we && (ROM_CTRL_REGS_PERMIT[ 0] != (ROM_CTRL_REGS_PERMIT[ 0] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 1] && reg_we && (ROM_CTRL_REGS_PERMIT[ 1] != (ROM_CTRL_REGS_PERMIT[ 1] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 2] && reg_we && (ROM_CTRL_REGS_PERMIT[ 2] != (ROM_CTRL_REGS_PERMIT[ 2] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 3] && reg_we && (ROM_CTRL_REGS_PERMIT[ 3] != (ROM_CTRL_REGS_PERMIT[ 3] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 4] && reg_we && (ROM_CTRL_REGS_PERMIT[ 4] != (ROM_CTRL_REGS_PERMIT[ 4] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 5] && reg_we && (ROM_CTRL_REGS_PERMIT[ 5] != (ROM_CTRL_REGS_PERMIT[ 5] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 6] && reg_we && (ROM_CTRL_REGS_PERMIT[ 6] != (ROM_CTRL_REGS_PERMIT[ 6] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 7] && reg_we && (ROM_CTRL_REGS_PERMIT[ 7] != (ROM_CTRL_REGS_PERMIT[ 7] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 8] && reg_we && (ROM_CTRL_REGS_PERMIT[ 8] != (ROM_CTRL_REGS_PERMIT[ 8] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[ 9] && reg_we && (ROM_CTRL_REGS_PERMIT[ 9] != (ROM_CTRL_REGS_PERMIT[ 9] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[10] && reg_we && (ROM_CTRL_REGS_PERMIT[10] != (ROM_CTRL_REGS_PERMIT[10] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[11] && reg_we && (ROM_CTRL_REGS_PERMIT[11] != (ROM_CTRL_REGS_PERMIT[11] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[12] && reg_we && (ROM_CTRL_REGS_PERMIT[12] != (ROM_CTRL_REGS_PERMIT[12] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[13] && reg_we && (ROM_CTRL_REGS_PERMIT[13] != (ROM_CTRL_REGS_PERMIT[13] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[14] && reg_we && (ROM_CTRL_REGS_PERMIT[14] != (ROM_CTRL_REGS_PERMIT[14] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[15] && reg_we && (ROM_CTRL_REGS_PERMIT[15] != (ROM_CTRL_REGS_PERMIT[15] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[16] && reg_we && (ROM_CTRL_REGS_PERMIT[16] != (ROM_CTRL_REGS_PERMIT[16] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[17] && reg_we && (ROM_CTRL_REGS_PERMIT[17] != (ROM_CTRL_REGS_PERMIT[17] & reg_be))) wr_err = 1'b1 ;
   end
 
   assign alert_test_we = addr_hit[0] & reg_we & !reg_error;
@@ -208,8 +676,72 @@
       end
 
       addr_hit[1]: begin
-        reg_rdata_next[0] = fatal_alert_cause_integrity_error_qs;
-        reg_rdata_next[1] = fatal_alert_cause_dummy_qs;
+        reg_rdata_next[0] = fatal_alert_cause_checker_error_qs;
+        reg_rdata_next[1] = fatal_alert_cause_integrity_error_qs;
+      end
+
+      addr_hit[2]: begin
+        reg_rdata_next[31:0] = digest_0_qs;
+      end
+
+      addr_hit[3]: begin
+        reg_rdata_next[31:0] = digest_1_qs;
+      end
+
+      addr_hit[4]: begin
+        reg_rdata_next[31:0] = digest_2_qs;
+      end
+
+      addr_hit[5]: begin
+        reg_rdata_next[31:0] = digest_3_qs;
+      end
+
+      addr_hit[6]: begin
+        reg_rdata_next[31:0] = digest_4_qs;
+      end
+
+      addr_hit[7]: begin
+        reg_rdata_next[31:0] = digest_5_qs;
+      end
+
+      addr_hit[8]: begin
+        reg_rdata_next[31:0] = digest_6_qs;
+      end
+
+      addr_hit[9]: begin
+        reg_rdata_next[31:0] = digest_7_qs;
+      end
+
+      addr_hit[10]: begin
+        reg_rdata_next[31:0] = exp_digest_0_qs;
+      end
+
+      addr_hit[11]: begin
+        reg_rdata_next[31:0] = exp_digest_1_qs;
+      end
+
+      addr_hit[12]: begin
+        reg_rdata_next[31:0] = exp_digest_2_qs;
+      end
+
+      addr_hit[13]: begin
+        reg_rdata_next[31:0] = exp_digest_3_qs;
+      end
+
+      addr_hit[14]: begin
+        reg_rdata_next[31:0] = exp_digest_4_qs;
+      end
+
+      addr_hit[15]: begin
+        reg_rdata_next[31:0] = exp_digest_5_qs;
+      end
+
+      addr_hit[16]: begin
+        reg_rdata_next[31:0] = exp_digest_6_qs;
+      end
+
+      addr_hit[17]: begin
+        reg_rdata_next[31:0] = exp_digest_7_qs;
       end
 
       default: begin
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 17046c5..af28623 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -5056,6 +5056,36 @@
           index: -1
         }
         {
+          name: pwrmgr_data
+          struct: pwrmgr_data
+          package: rom_ctrl_pkg
+          type: uni
+          act: req
+          width: 1
+          inst_name: rom_ctrl
+          index: -1
+        }
+        {
+          name: keymgr_data
+          struct: keymgr_data
+          package: rom_ctrl_pkg
+          type: uni
+          act: req
+          width: 1
+          inst_name: rom_ctrl
+          index: -1
+        }
+        {
+          name: kmac_data
+          struct: app
+          package: kmac_pkg
+          type: req_rsp
+          act: req
+          width: 1
+          inst_name: rom_ctrl
+          index: -1
+        }
+        {
           name: regs_tl
           struct: tl
           package: tlul_pkg
@@ -14142,6 +14172,36 @@
         index: -1
       }
       {
+        name: pwrmgr_data
+        struct: pwrmgr_data
+        package: rom_ctrl_pkg
+        type: uni
+        act: req
+        width: 1
+        inst_name: rom_ctrl
+        index: -1
+      }
+      {
+        name: keymgr_data
+        struct: keymgr_data
+        package: rom_ctrl_pkg
+        type: uni
+        act: req
+        width: 1
+        inst_name: rom_ctrl
+        index: -1
+      }
+      {
+        name: kmac_data
+        struct: app
+        package: kmac_pkg
+        type: req_rsp
+        act: req
+        width: 1
+        inst_name: rom_ctrl
+        index: -1
+      }
+      {
         name: regs_tl
         struct: tl
         package: tlul_pkg
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index c0aa6ca..7e7698f 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -2282,6 +2282,10 @@
 
       // Inter-module signals
       .rom_cfg_i(ast_rom_cfg),
+      .pwrmgr_data_o(),
+      .keymgr_data_o(),
+      .kmac_data_o(),
+      .kmac_data_i(kmac_pkg::APP_RSP_DEFAULT),
       .regs_tl_i(rom_ctrl_regs_tl_req),
       .regs_tl_o(rom_ctrl_regs_tl_rsp),
       .rom_tl_i(rom_ctrl_rom_tl_req),