[rstmgr] First draft of reset manager

First draft of reset manager.

This module is mostly inert without an associated clock and power manager.
It is also not integrated yet, as it is dependent on the pwrmgr.

Signed-off-by: Timothy Chen <timothytim@google.com>

[rstmgr] - Various clean-up from comments

- clean up naming
- clarified info recording cause

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/Makefile b/hw/Makefile
index 5ecca1a..36b2379 100644
--- a/hw/Makefile
+++ b/hw/Makefile
@@ -16,6 +16,7 @@
        padctrl       \
        pinmux        \
        pwrmgr        \
+       rstmgr        \
        rv_plic       \
        rv_timer      \
        spi_device    \
diff --git a/hw/ip/rstmgr/data/rstmgr.hjson b/hw/ip/rstmgr/data/rstmgr.hjson
new file mode 100644
index 0000000..32c7721
--- /dev/null
+++ b/hw/ip/rstmgr/data/rstmgr.hjson
@@ -0,0 +1,159 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+// TODO: This module is only a draft implementation that covers most of the rstmgr
+// functoinality but is incomplete
+
+# RSTMGR register template
+#
+{
+  name: "RSTMGR",
+  clock_primary: "clk_i",
+  other_clock_list: [
+    "clk_main_i",
+    "clk_fixed_i",
+    "clk_usb_i"
+  ],
+  bus_device: "tlul",
+  regwidth: "32",
+  param_list: [
+  ],
+
+  // Define rstmgr struct package
+  inter_signal_list: [
+    { struct:  "pwr_rst",    // rstmgr_req_t, rstmgr_rsp_t
+      type:    "req_rsp",
+      name:    "pwr",        // resets_o (req), resets_i (rsp)
+      act:     "responder",
+      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+    },
+
+    { struct:  "rstmgr_out",
+      type:    "broadcast",
+      name:    "rstmgr",
+      act:     "requester",
+      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+    },
+
+    { struct:  "rstmgr_ast",
+      type:    "broadcast",
+      name:    "ast",
+      act:     "receiver",
+      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+    },
+
+    { struct:  "rstmgr_cpu",
+      type:    "broadcast",
+      name:    "cpu",
+      act:     "receiver",
+      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+    },
+
+    { struct:  "rstmgr_peri",
+      type:    "broadcast",
+      name:    "peri",
+      act:     "receiver",
+      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+    }
+  ],
+
+  registers: [
+    { name: "RESET_INFO",
+      desc: '''
+            Device reset reason.
+            ''',
+      swaccess: "rw1c",
+      hwaccess: "hrw",
+      hwext: "true",
+      hwqe: "true",
+      fields: [
+        {
+            bits: "4:0",
+            name: "CAUSE",
+            resval: 1,
+            desc: '''Indicates reset cause.  The bits below are not mutually exclusive, and multiple
+            bits can be set at the same time.'''
+            enum: [
+             {value: "0x1",  name: "por",              desc: "Indicates when a device has reset due to power up"},
+             {value: "0x2",  name: "low_power_exit",   desc: "Indicates when a device has reset due low power exit"},
+             {value: "0x4",  name: "watchdog",         desc: "Indicates when a device has reset due to watchdog"},
+             {value: "0x8",  name: "escalation",       desc: "Indicates when a device has reset due to security gescalation"},
+             {value: "0x10", name: "Non-debug-module", desc: "Indicates when a device has reset due to non-debug-module request"},
+            ]
+        }
+      ]
+    },
+
+    ########################
+    # These registers below should be templatized and generated
+    ########################
+
+    { name: "SPI_DEVICE_REGEN",
+      desc: '''
+            Register write enable for spi_device reset.
+            ''',
+      swaccess: "rw1c",
+      hwaccess: "none",
+      fields: [
+        {
+            bits:   "0",
+            desc: ''' When 1, rst_spi_device_n is software programmable.
+            '''
+            resval: 1,
+        },
+      ]
+    },
+
+    { name: "USB_REGEN",
+      desc: '''
+            Register write enable for usb reset.
+            ''',
+      swaccess: "rw1c",
+      hwaccess: "none",
+      fields: [
+        {
+            bits:   "0",
+            desc: ''' When 1, rst_usb_n is software programmable.
+            '''
+            resval: 1,
+        },
+      ]
+    },
+
+    { name: "RST_SPI_DEVICE_N",
+      regwen:  "SPI_DEVICE_REGEN",
+      desc: '''
+            Software reset control for spi device
+            ''',
+      swaccess: "rw",
+      hwaccess: "hro",
+      fields: [
+        {
+            bits:   "0",
+            desc: ''' When set to 0, spi_device is held in reset.  This bit can only be
+            programmed when spi_device_regen is 1.
+            '''
+            resval: 1,
+        },
+      ]
+    },
+
+    { name: "RST_USB_N",
+      regwen:  "USB_REGEN",
+      desc: '''
+            Software reset control for usb
+            ''',
+      swaccess: "rw",
+      hwaccess: "hro",
+      fields: [
+        {
+            bits:   "0",
+            desc: ''' When set to 0, usb is held in reset. This bit can only be
+            programmed when usb_regen is 1.
+            '''
+            resval: 1,
+        },
+      ]
+    }
+  ]
+}
diff --git a/hw/ip/rstmgr/lint/rstmgr.vlt b/hw/ip/rstmgr/lint/rstmgr.vlt
new file mode 100644
index 0000000..a3aadb8
--- /dev/null
+++ b/hw/ip/rstmgr/lint/rstmgr.vlt
@@ -0,0 +1,5 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// waiver file for rstmgr
diff --git a/hw/ip/rstmgr/lint/rstmgr.waiver b/hw/ip/rstmgr/lint/rstmgr.waiver
new file mode 100644
index 0000000..1531b1b
--- /dev/null
+++ b/hw/ip/rstmgr/lint/rstmgr.waiver
@@ -0,0 +1,5 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+#
+# waiver file for rstmgr
diff --git a/hw/ip/rstmgr/rstmgr.core b/hw/ip/rstmgr/rstmgr.core
new file mode 100644
index 0000000..f597680
--- /dev/null
+++ b/hw/ip/rstmgr/rstmgr.core
@@ -0,0 +1,63 @@
+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:ip:rstmgr:0.1"
+description: "Reset manager component without the generated portions"
+
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:ip:tlul
+      - lowrisc:prim:all
+      - lowrisc:ip:rstmgr_pkg
+    files:
+      - rtl/rstmgr_reg_pkg.sv
+      - rtl/rstmgr_reg_top.sv
+      - rtl/rstmgr_ctrl.sv
+      - rtl/rstmgr_por.sv
+      - rtl/rstmgr_info.sv
+      - rtl/rstmgr.sv
+    file_type: systemVerilogSource
+
+  files_verilator_waiver:
+    depend:
+      # common waivers
+      - lowrisc:lint:common
+      - lowrisc:lint:comportable
+    files:
+    file_type: vlt
+
+  files_ascentlint_waiver:
+    depend:
+      # common waivers
+      - lowrisc:lint:common
+      - lowrisc:lint:comportable
+    files:
+      - lint/rstmgr.waiver
+    file_type: waiver
+
+parameters:
+  SYNTHESIS:
+    datatype: bool
+    paramtype: vlogdefine
+
+
+targets:
+  default: &default_target
+    filesets:
+      - tool_verilator  ? (files_verilator_waiver)
+      - tool_ascentlint ? (files_ascentlint_waiver)
+      - files_rtl
+    toplevel: rstmgr
+
+  lint:
+    <<: *default_target
+    default_tool: verilator
+    parameters:
+      - SYNTHESIS=true
+    tools:
+      verilator:
+        mode: lint-only
+        verilator_options:
+          - "-Wall"
diff --git a/hw/ip/rstmgr/rstmgr_pkg.core b/hw/ip/rstmgr/rstmgr_pkg.core
new file mode 100644
index 0000000..8ebf4d0
--- /dev/null
+++ b/hw/ip/rstmgr/rstmgr_pkg.core
@@ -0,0 +1,19 @@
+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:ip:rstmgr_pkg:0.1"
+description: "Reset Manager Package"
+
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:constants:top_pkg
+    files:
+      - rtl/rstmgr_pkg.sv
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_rtl
diff --git a/hw/ip/rstmgr/rtl/rstmgr.sv b/hw/ip/rstmgr/rtl/rstmgr.sv
new file mode 100644
index 0000000..776875a
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr.sv
@@ -0,0 +1,214 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This module is the overall reset manager wrapper
+// TODO: This module is only a draft implementation that covers most of the rstmgr
+// functoinality but is incomplete
+
+`include "prim_assert.sv"
+
+// This top level controller is fairly hardcoded right now, but will be switched to a template
+module rstmgr import rstmgr_pkg::*; (
+  // Primary module clocks
+  input clk_i,
+  input rst_ni,
+  input clk_main_i,
+  input clk_fixed_i,
+  input clk_usb_i,
+
+  // Bus Interface
+  input tlul_pkg::tl_h2d_t tl_i,
+  output tlul_pkg::tl_d2h_t tl_o,
+
+  // pwrmgr interface
+  input pwr_rst_req_t pwr_i,
+  output pwr_rst_rsp_t pwr_o,
+
+  // ast interface
+  input rstmgr_ast_t ast_i,
+
+  // cpu related inputs
+  input rstmgr_cpu_t cpu_i,
+
+  // peripheral reset requests
+  input rstmgr_peri_t peri_i,
+
+  // Interface to alert handler
+  // always on resets
+  output rstmgr_out_t rstmgr_o
+
+);
+
+  // receive POR and stretch
+  // TBD
+  // The por release may eventually need to be switched to a slower RTC type clock.
+  // Unless the power manager's default state always requests for the fast clocks to be enabled
+  // by default.
+
+  rstmgr_por i_por (
+    .clk_i,
+    .rst_ni,
+    .pok_i(ast_i.vcc_pok & ast_i.alw_pok),
+    .rst_no(rstmgr_o.rst_por_n)
+  );
+
+  ////////////////////////////////////////////////////
+  // Register Interface                             //
+  ////////////////////////////////////////////////////
+
+  rstmgr_reg_pkg::rstmgr_reg2hw_t reg2hw;
+  rstmgr_reg_pkg::rstmgr_hw2reg_t hw2reg;
+
+  rstmgr_reg_top i_reg (
+    .clk_i,
+    .rst_ni(rstmgr_o.rst_por_n),
+    .tl_i,
+    .tl_o,
+    .reg2hw,
+    .hw2reg,
+    .devmode_i(1'b1)
+  );
+
+  ////////////////////////////////////////////////////
+  // Input handling                                 //
+  ////////////////////////////////////////////////////
+
+  logic ndmreset_req_q;
+  logic ndm_req_valid;
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_sync (
+    .clk_i,
+    .rst_ni(rstmgr_o.rst_por_n),
+    .d(cpu_i.ndmreset_req),
+    .q(ndmreset_req_q)
+  );
+
+  assign ndm_req_valid = ndmreset_req_q & (pwr_i.reset_cause == None);
+
+  ////////////////////////////////////////////////////
+  // Source resets in the system                    //
+  // These are hardcoded and not directly used.     //
+  // Instead they act as async reset roots.         //
+  ////////////////////////////////////////////////////
+  logic [PowerDomains-1:0] rst_lc_src_n;
+  logic [PowerDomains-1:0] rst_sys_src_n;
+
+  // The two source reset modules are chained together.  The output of one is fed into the
+  // the second.  This ensures that if upstream resets for any reason, the associated downstream
+  // reset will also reset.
+
+  // lc reset sources
+  rstmgr_ctrl #(
+    .PowerDomains(PowerDomains)
+  ) i_lc_src (
+    .clk_i(clk_i),
+    .rst_ni(rstmgr_o.rst_por_n),
+    .rst_req_i(pwr_i.rst_lc_req),
+    .rst_parent_ni(PowerDomains'(1'b1)),
+    .rst_no(rst_lc_src_n)
+  );
+
+  // sys reset sources
+  rstmgr_ctrl #(
+    .PowerDomains(PowerDomains)
+  ) i_sys_src (
+    .clk_i(clk_i),
+    .rst_ni(rstmgr_o.rst_por_n),
+    .rst_req_i(pwr_i.rst_sys_req | {PowerDomains{ndm_req_valid}}),
+    .rst_parent_ni(rst_lc_src_n),
+    .rst_no(rst_sys_src_n)
+  );
+
+  assign pwr_o.rst_lc_src_n = rst_lc_src_n;
+  assign pwr_o.rst_sys_src_n = rst_sys_src_n;
+
+  ////////////////////////////////////////////////////
+  // leaf reset in the system                       //
+  // These should all be generated                  //
+  ////////////////////////////////////////////////////
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_lc (
+    .clk_i(clk_fixed_i),
+    .rst_ni(rst_lc_src_n[ALWAYS_ON_SEL]),
+    .d(1'b1),
+    .q(rstmgr_o.rst_lc_n)
+  );
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_sys (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_sys_src_n[ALWAYS_ON_SEL]),
+    .d(1'b1),
+    .q(rstmgr_o.rst_sys_n)
+  );
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_sys_fixed (
+    .clk_i(clk_fixed_i),
+    .rst_ni(rst_sys_src_n[ALWAYS_ON_SEL]),
+    .d(1'b1),
+    .q(rstmgr_o.rst_sys_fixed_n)
+  );
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_spi_device (
+    .clk_i(clk_fixed_i),
+    .rst_ni(rst_sys_src_n[ALWAYS_ON_SEL]),
+    .d(reg2hw.rst_spi_device_n.q),
+    .q(rstmgr_o.rst_spi_device_n)
+  );
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) i_usb (
+    .clk_i(clk_usb_i),
+    .rst_ni(rst_sys_src_n[ALWAYS_ON_SEL]),
+    .d(reg2hw.rst_usb_n.q),
+    .q(rstmgr_o.rst_usb_n)
+  );
+
+  ////////////////////////////////////////////////////
+  // Reset info construction                        //
+  ////////////////////////////////////////////////////
+
+  logic [ResetReasons-1:0] rst_reqs;
+
+  assign rst_reqs = {
+                    ndm_req_valid,
+                    (pwr_i.reset_cause == HwReq) ? peri_i.rst_reqs : ExtResetReasons'(0),
+                    (pwr_i.reset_cause == LowPwrEntry)
+                    };
+
+  rstmgr_info #(
+    .Reasons(ResetReasons)
+  ) i_info (
+    .clk_i,
+    .rst_ni(rstmgr_o.rst_por_n),
+    .rst_cpu_ni(cpu_i.rst_cpu_n),
+    .rst_req_i(rst_reqs),
+    .wr_i(reg2hw.reset_info.qe),
+    .data_i(reg2hw.reset_info.q),
+    .rst_reasons_o(hw2reg.reset_info)
+  );
+
+  ////////////////////////////////////////////////////
+  // Assertions                                     //
+  ////////////////////////////////////////////////////
+
+  // when upstream resets, downstream must also reset
+
+endmodule // rstmgr
diff --git a/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv b/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv
new file mode 100644
index 0000000..6e92769
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv
@@ -0,0 +1,71 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This module implements generic reset controls
+//
+
+`include "prim_assert.sv"
+
+module rstmgr_ctrl import rstmgr_pkg::*; #(
+  parameter int PowerDomains = 2,
+  localparam int OffDomains = PowerDomains - 1
+) (
+  input clk_i,
+  input rst_ni,
+  input [PowerDomains-1:0] rst_req_i,
+  input [PowerDomains-1:0] rst_parent_ni, // parent reset
+  output logic [PowerDomains-1:0] rst_no
+);
+
+  // The code below always assumes the always on domain is index 0
+  `ASSERT_INIT(AlwaysOnIndex_A, ALWAYS_ON_SEL == 0)
+
+  // when there are multiple on domains, the latter 1 should become another parameter
+  localparam int OffDomainSelStart = ALWAYS_ON_SEL + 1;
+
+  // the always on root reset
+  logic rst_aon_n;
+
+  // the remaining resets
+  logic [OffDomains-1:0] rst_pd_n;
+
+  // Parent resets may assert asynchronously, so we need to sync before using it as part
+  // of the control path
+  logic [PowerDomains-1:0] rst_parent_synced;
+  prim_flop_2sync #(
+    .Width(PowerDomains),
+    .ResetValue(0)
+  ) u_lc (
+    .clk_i(clk_i),
+    .rst_ni(rst_ni),
+    .d(rst_parent_ni),
+    .q(rst_parent_synced)
+  );
+
+  // always on handling
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      rst_aon_n <= '0;
+    end else begin
+      // if the parent is in reset, OR there's an always on reset request
+      // reset this node
+      rst_aon_n <= ~rst_req_i[ALWAYS_ON_SEL] & rst_parent_synced[ALWAYS_ON_SEL];
+    end
+  end
+
+  // the non-always-on domains
+  // These reset whenever the always on domain reset, to ensure power definition consistency.
+  // By extension, they also reset whenever the root (rst_ni) resets
+  always_ff @(posedge clk_i or negedge rst_aon_n) begin
+    if (!rst_aon_n) begin
+      rst_pd_n <= '0;
+    end else begin
+      rst_pd_n <= ~rst_req_i[OffDomainSelStart +: OffDomains] &
+                  rst_parent_synced[OffDomainSelStart +: OffDomains];
+    end
+  end
+
+  assign rst_no = {rst_pd_n, rst_aon_n};
+
+endmodule // rstmgr_ctrl
diff --git a/hw/ip/rstmgr/rtl/rstmgr_info.sv b/hw/ip/rstmgr/rtl/rstmgr_info.sv
new file mode 100644
index 0000000..ffe8357
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_info.sv
@@ -0,0 +1,61 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This module records the reset reason
+//
+
+`include "prim_assert.sv"
+
+module rstmgr_info #(
+  parameter int Reasons = 4
+) (
+  input clk_i,
+  input rst_ni,
+  input rst_cpu_ni,
+  input [Reasons-1:0] rst_req_i,
+  input wr_i,
+  input [Reasons:0] data_i,              // inclusive of POR
+  output logic [Reasons:0] rst_reasons_o // inclusive of POR
+);
+
+  logic [Reasons-1:0] reasons;
+  logic por;
+  logic first_reset;
+  logic rst_cpu_nq;
+
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) u_cpu_reset_synced (
+    .clk_i(clk_i),
+    .rst_ni(rst_ni),
+    .d(rst_cpu_ni),
+    .q(rst_cpu_nq)
+  );
+
+  // first reset is a flag that blocks reset recording until first de-assertion
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      first_reset <= 1'b1;
+    end else if (rst_cpu_nq) begin
+      first_reset <= 1'b0;
+    end
+  end
+
+  // if cpu has gone into reset, record reset causes
+  // the reasons is a logical OR, so that signals that were once asserted do not go away.
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      por <= 1'b1;
+      reasons <= '0;
+    end else if (!rst_cpu_nq && !first_reset) begin
+      reasons <= reasons | rst_req_i;
+    end else if (wr_i) begin
+      {reasons, por} <= {reasons, por} & ~data_i;
+    end
+  end
+
+  assign rst_reasons_o = {reasons, por};
+
+endmodule // rstmgr_info
diff --git a/hw/ip/rstmgr/rtl/rstmgr_pkg.sv b/hw/ip/rstmgr/rtl/rstmgr_pkg.sv
new file mode 100644
index 0000000..acc6b09
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_pkg.sv
@@ -0,0 +1,83 @@
+package rstmgr_pkg;
+
+  // global constants
+  parameter int ALWAYS_ON_SEL = 0;
+
+  // params that reference pwrmgr, should be replaced once pwrmgr is merged
+  //localparam int AlwaysOnSel = pwrmgr_pkg::AlwaysOnDomain;
+  //localparam int ExtResetReasons = pwrmgr_pkg::HwRstReqs;
+  localparam int PowerDomains = 2;
+  localparam int OffDomains = PowerDomains-1;
+  localparam int ExtResetReasons = 2;
+
+  // low power exit + external reasons + ndm_reset_req
+  localparam int ResetReasons = 1 + ExtResetReasons + 1;
+
+  // reasons for pwrmgr reset reset
+  typedef enum logic [1:0] {
+    None = 0,
+    LowPwrEntry = 1,
+    HwReq = 2,
+    Undefined = 3
+  } reset_cause_e;
+
+  // pwrmgr interface (these are declared in pwrmgr_pkg), should remove once present
+  typedef struct packed {
+    logic [PowerDomains-1:0] rst_lc_req;
+    logic [PowerDomains-1:0] rst_sys_req;
+    reset_cause_e reset_cause;
+  } pwr_rst_req_t;
+
+  // rstmgr to pwrmgr
+  typedef struct packed {
+    logic [PowerDomains-1:0] rst_lc_src_n;
+    logic [PowerDomains-1:0] rst_sys_src_n;
+  } pwr_rst_rsp_t;
+
+  // ast interface
+  typedef struct packed {
+    logic vcc_pok;
+    logic alw_pok;
+  } rstmgr_ast_t;
+
+  // default value for rstmgr_ast_rsp_t (for dangling ports)
+  parameter rstmgr_ast_t RSTMGR_AST_DEFAULT = '{
+    vcc_pok: 1'b1,
+    alw_pok: 1'b1
+  };
+
+  // resets generated and broadcast
+  // This should be templatized and generated
+  typedef struct packed {
+    logic rst_por_n;
+    logic rst_lc_n;
+    logic rst_sys_fixed_n;
+    logic rst_sys_n;
+    logic rst_spi_device_n;
+    logic rst_usb_n;
+  } rstmgr_out_t;
+
+  // peripherals reset requests
+  typedef struct packed {
+    logic rst_cpu_n;
+    logic ndmreset_req;
+  } rstmgr_cpu_t;
+
+  // default value for rstmgr_ast_rsp_t (for dangling ports)
+  parameter rstmgr_cpu_t RSTMGR_CPU_DEFAULT = '{
+    rst_cpu_n: 1'b1,
+    ndmreset_req: '0
+  };
+
+  // peripherals reset requests
+  typedef struct packed {
+    logic [ExtResetReasons-1:0] rst_reqs;
+  } rstmgr_peri_t;
+
+  // default value for rstmgr_ast_rsp_t (for dangling ports)
+  parameter rstmgr_peri_t RSTMGR_PERI_DEFAULT = '{
+    rst_reqs: '0
+  };
+
+
+endpackage // rstmgr_pkg
diff --git a/hw/ip/rstmgr/rtl/rstmgr_por.sv b/hw/ip/rstmgr/rtl/rstmgr_por.sv
new file mode 100644
index 0000000..cf77b9d
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_por.sv
@@ -0,0 +1,70 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This module stretches the POR
+//
+
+`include "prim_assert.sv"
+
+module rstmgr_por #(
+  parameter int FilterStages = 3,
+  parameter int StretchCount = 32
+) (
+  input clk_i,
+  input rst_ni,
+  input pok_i, // TODO: This should not be an actual separate port but the POR itself
+               // However, this cannot be done until AST integration is done.
+  output logic rst_no
+);
+  localparam int CtrWidth = $clog2(StretchCount+1);
+
+  logic rst_root_n;
+  logic [FilterStages-1:0] rst_filter_n;
+  logic rst_stable;
+  logic rst_clean_n;
+  logic [CtrWidth-1:0] cnt;
+  logic cnt_en;
+
+  // sync the POR
+  prim_flop_2sync #(
+    .Width(1),
+    .ResetValue(0)
+  ) rst_sync (
+    .clk_i(clk_i),
+    .rst_ni(rst_ni),
+    .d(1'b1),
+    .q(rst_root_n)
+  );
+
+  // filter the POR
+  always_ff @(posedge clk_i or negedge rst_root_n) begin
+    if (!rst_root_n) begin
+      rst_filter_n <= '0;
+    end else if (pok_i) begin // once AST is in, this conditional should not be here.
+      rst_filter_n <= {rst_filter_n[0 +: FilterStages-1], 1'b1};
+    end
+  end
+
+  // The stable is a vote of all filter stages.
+  // Only when all the stages agree is the reset considered stable and count allowed.
+  assign rst_clean_n = rst_filter_n[FilterStages-1];
+  assign rst_stable = &rst_filter_n;
+  assign cnt_en = rst_stable & !rst_no;
+
+  // stretch the POR
+  always_ff @(posedge clk_i or negedge rst_clean_n) begin
+    if (!rst_clean_n) begin
+      cnt <= '0;
+      rst_no <= '0;
+    end else if (!rst_stable) begin
+      cnt <= '0;
+      rst_no <= '0;
+    end else if (cnt_en && cnt == StretchCount) begin
+      rst_no <= 1'b1;
+    end else if (cnt_en) begin
+      cnt <= cnt + 1'b1;
+    end
+  end
+
+endmodule // rstmgr_por
diff --git a/hw/ip/rstmgr/rtl/rstmgr_reg_pkg.sv b/hw/ip/rstmgr/rtl/rstmgr_reg_pkg.sv
new file mode 100644
index 0000000..9471030
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_reg_pkg.sv
@@ -0,0 +1,73 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Register Package auto-generated by `reggen` containing data structure
+
+package rstmgr_reg_pkg;
+
+  ////////////////////////////
+  // Typedefs for registers //
+  ////////////////////////////
+  typedef struct packed {
+    logic [4:0]  q;
+    logic        qe;
+  } rstmgr_reg2hw_reset_info_reg_t;
+
+  typedef struct packed {
+    logic        q;
+  } rstmgr_reg2hw_rst_spi_device_n_reg_t;
+
+  typedef struct packed {
+    logic        q;
+  } rstmgr_reg2hw_rst_usb_n_reg_t;
+
+
+  typedef struct packed {
+    logic [4:0]  d;
+  } rstmgr_hw2reg_reset_info_reg_t;
+
+
+  ///////////////////////////////////////
+  // Register to internal design logic //
+  ///////////////////////////////////////
+  typedef struct packed {
+    rstmgr_reg2hw_reset_info_reg_t reset_info; // [7:2]
+    rstmgr_reg2hw_rst_spi_device_n_reg_t rst_spi_device_n; // [1:1]
+    rstmgr_reg2hw_rst_usb_n_reg_t rst_usb_n; // [0:0]
+  } rstmgr_reg2hw_t;
+
+  ///////////////////////////////////////
+  // Internal design logic to register //
+  ///////////////////////////////////////
+  typedef struct packed {
+    rstmgr_hw2reg_reset_info_reg_t reset_info; // [4:-1]
+  } rstmgr_hw2reg_t;
+
+  // Register Address
+  parameter logic [4:0] RSTMGR_RESET_INFO_OFFSET = 5'h 0;
+  parameter logic [4:0] RSTMGR_SPI_DEVICE_REGEN_OFFSET = 5'h 4;
+  parameter logic [4:0] RSTMGR_USB_REGEN_OFFSET = 5'h 8;
+  parameter logic [4:0] RSTMGR_RST_SPI_DEVICE_N_OFFSET = 5'h c;
+  parameter logic [4:0] RSTMGR_RST_USB_N_OFFSET = 5'h 10;
+
+
+  // Register Index
+  typedef enum int {
+    RSTMGR_RESET_INFO,
+    RSTMGR_SPI_DEVICE_REGEN,
+    RSTMGR_USB_REGEN,
+    RSTMGR_RST_SPI_DEVICE_N,
+    RSTMGR_RST_USB_N
+  } rstmgr_id_e;
+
+  // Register width information to check illegal writes
+  parameter logic [3:0] RSTMGR_PERMIT [5] = '{
+    4'b 0001, // index[0] RSTMGR_RESET_INFO
+    4'b 0001, // index[1] RSTMGR_SPI_DEVICE_REGEN
+    4'b 0001, // index[2] RSTMGR_USB_REGEN
+    4'b 0001, // index[3] RSTMGR_RST_SPI_DEVICE_N
+    4'b 0001  // index[4] RSTMGR_RST_USB_N
+  };
+endpackage
+
diff --git a/hw/ip/rstmgr/rtl/rstmgr_reg_top.sv b/hw/ip/rstmgr/rtl/rstmgr_reg_top.sv
new file mode 100644
index 0000000..5a2c127
--- /dev/null
+++ b/hw/ip/rstmgr/rtl/rstmgr_reg_top.sv
@@ -0,0 +1,298 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Register Top module auto-generated by `reggen`
+
+`include "prim_assert.sv"
+
+module rstmgr_reg_top (
+  input clk_i,
+  input rst_ni,
+
+  // Below Regster interface can be changed
+  input  tlul_pkg::tl_h2d_t tl_i,
+  output tlul_pkg::tl_d2h_t tl_o,
+  // To HW
+  output rstmgr_reg_pkg::rstmgr_reg2hw_t reg2hw, // Write
+  input  rstmgr_reg_pkg::rstmgr_hw2reg_t hw2reg, // Read
+
+  // Config
+  input devmode_i // If 1, explicit error return for unmapped register access
+);
+
+  import rstmgr_reg_pkg::* ;
+
+  localparam int AW = 5;
+  localparam int DW = 32;
+  localparam int DBW = DW/8;                    // Byte Width
+
+  // register signals
+  logic           reg_we;
+  logic           reg_re;
+  logic [AW-1:0]  reg_addr;
+  logic [DW-1:0]  reg_wdata;
+  logic [DBW-1:0] reg_be;
+  logic [DW-1:0]  reg_rdata;
+  logic           reg_error;
+
+  logic          addrmiss, wr_err;
+
+  logic [DW-1:0] reg_rdata_next;
+
+  tlul_pkg::tl_h2d_t tl_reg_h2d;
+  tlul_pkg::tl_d2h_t tl_reg_d2h;
+
+  assign tl_reg_h2d = tl_i;
+  assign tl_o       = tl_reg_d2h;
+
+  tlul_adapter_reg #(
+    .RegAw(AW),
+    .RegDw(DW)
+  ) u_reg_if (
+    .clk_i,
+    .rst_ni,
+
+    .tl_i (tl_reg_h2d),
+    .tl_o (tl_reg_d2h),
+
+    .we_o    (reg_we),
+    .re_o    (reg_re),
+    .addr_o  (reg_addr),
+    .wdata_o (reg_wdata),
+    .be_o    (reg_be),
+    .rdata_i (reg_rdata),
+    .error_i (reg_error)
+  );
+
+  assign reg_rdata = reg_rdata_next ;
+  assign reg_error = (devmode_i & addrmiss) | wr_err ;
+
+  // Define SW related signals
+  // Format: <reg>_<field>_{wd|we|qs}
+  //        or <reg>_{wd|we|qs} if field == 1 or 0
+  logic [4:0] reset_info_qs;
+  logic [4:0] reset_info_wd;
+  logic reset_info_we;
+  logic reset_info_re;
+  logic spi_device_regen_qs;
+  logic spi_device_regen_wd;
+  logic spi_device_regen_we;
+  logic usb_regen_qs;
+  logic usb_regen_wd;
+  logic usb_regen_we;
+  logic rst_spi_device_n_qs;
+  logic rst_spi_device_n_wd;
+  logic rst_spi_device_n_we;
+  logic rst_usb_n_qs;
+  logic rst_usb_n_wd;
+  logic rst_usb_n_we;
+
+  // Register instances
+  // R[reset_info]: V(True)
+
+  prim_subreg_ext #(
+    .DW    (5)
+  ) u_reset_info (
+    .re     (reset_info_re),
+    .we     (reset_info_we),
+    .wd     (reset_info_wd),
+    .d      (hw2reg.reset_info.d),
+    .qre    (),
+    .qe     (reg2hw.reset_info.qe),
+    .q      (reg2hw.reset_info.q ),
+    .qs     (reset_info_qs)
+  );
+
+
+  // R[spi_device_regen]: V(False)
+
+  prim_subreg #(
+    .DW      (1),
+    .SWACCESS("W1C"),
+    .RESVAL  (1'h1)
+  ) u_spi_device_regen (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    // from register interface
+    .we     (spi_device_regen_we),
+    .wd     (spi_device_regen_wd),
+
+    // from internal hardware
+    .de     (1'b0),
+    .d      ('0  ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (),
+
+    // to register interface (read)
+    .qs     (spi_device_regen_qs)
+  );
+
+
+  // R[usb_regen]: V(False)
+
+  prim_subreg #(
+    .DW      (1),
+    .SWACCESS("W1C"),
+    .RESVAL  (1'h1)
+  ) u_usb_regen (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    // from register interface
+    .we     (usb_regen_we),
+    .wd     (usb_regen_wd),
+
+    // from internal hardware
+    .de     (1'b0),
+    .d      ('0  ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (),
+
+    // to register interface (read)
+    .qs     (usb_regen_qs)
+  );
+
+
+  // R[rst_spi_device_n]: V(False)
+
+  prim_subreg #(
+    .DW      (1),
+    .SWACCESS("RW"),
+    .RESVAL  (1'h1)
+  ) u_rst_spi_device_n (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    // from register interface (qualified with register enable)
+    .we     (rst_spi_device_n_we & spi_device_regen_qs),
+    .wd     (rst_spi_device_n_wd),
+
+    // from internal hardware
+    .de     (1'b0),
+    .d      ('0  ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.rst_spi_device_n.q ),
+
+    // to register interface (read)
+    .qs     (rst_spi_device_n_qs)
+  );
+
+
+  // R[rst_usb_n]: V(False)
+
+  prim_subreg #(
+    .DW      (1),
+    .SWACCESS("RW"),
+    .RESVAL  (1'h1)
+  ) u_rst_usb_n (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+    // from register interface (qualified with register enable)
+    .we     (rst_usb_n_we & usb_regen_qs),
+    .wd     (rst_usb_n_wd),
+
+    // from internal hardware
+    .de     (1'b0),
+    .d      ('0  ),
+
+    // to internal hardware
+    .qe     (),
+    .q      (reg2hw.rst_usb_n.q ),
+
+    // to register interface (read)
+    .qs     (rst_usb_n_qs)
+  );
+
+
+
+
+  logic [4:0] addr_hit;
+  always_comb begin
+    addr_hit = '0;
+    addr_hit[0] = (reg_addr == RSTMGR_RESET_INFO_OFFSET);
+    addr_hit[1] = (reg_addr == RSTMGR_SPI_DEVICE_REGEN_OFFSET);
+    addr_hit[2] = (reg_addr == RSTMGR_USB_REGEN_OFFSET);
+    addr_hit[3] = (reg_addr == RSTMGR_RST_SPI_DEVICE_N_OFFSET);
+    addr_hit[4] = (reg_addr == RSTMGR_RST_USB_N_OFFSET);
+  end
+
+  assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
+
+  // Check sub-word write is permitted
+  always_comb begin
+    wr_err = 1'b0;
+    if (addr_hit[0] && reg_we && (RSTMGR_PERMIT[0] != (RSTMGR_PERMIT[0] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[1] && reg_we && (RSTMGR_PERMIT[1] != (RSTMGR_PERMIT[1] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[2] && reg_we && (RSTMGR_PERMIT[2] != (RSTMGR_PERMIT[2] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[3] && reg_we && (RSTMGR_PERMIT[3] != (RSTMGR_PERMIT[3] & reg_be))) wr_err = 1'b1 ;
+    if (addr_hit[4] && reg_we && (RSTMGR_PERMIT[4] != (RSTMGR_PERMIT[4] & reg_be))) wr_err = 1'b1 ;
+  end
+
+  assign reset_info_we = addr_hit[0] & reg_we & ~wr_err;
+  assign reset_info_wd = reg_wdata[4:0];
+  assign reset_info_re = addr_hit[0] && reg_re;
+
+  assign spi_device_regen_we = addr_hit[1] & reg_we & ~wr_err;
+  assign spi_device_regen_wd = reg_wdata[0];
+
+  assign usb_regen_we = addr_hit[2] & reg_we & ~wr_err;
+  assign usb_regen_wd = reg_wdata[0];
+
+  assign rst_spi_device_n_we = addr_hit[3] & reg_we & ~wr_err;
+  assign rst_spi_device_n_wd = reg_wdata[0];
+
+  assign rst_usb_n_we = addr_hit[4] & reg_we & ~wr_err;
+  assign rst_usb_n_wd = reg_wdata[0];
+
+  // Read data return
+  always_comb begin
+    reg_rdata_next = '0;
+    unique case (1'b1)
+      addr_hit[0]: begin
+        reg_rdata_next[4:0] = reset_info_qs;
+      end
+
+      addr_hit[1]: begin
+        reg_rdata_next[0] = spi_device_regen_qs;
+      end
+
+      addr_hit[2]: begin
+        reg_rdata_next[0] = usb_regen_qs;
+      end
+
+      addr_hit[3]: begin
+        reg_rdata_next[0] = rst_spi_device_n_qs;
+      end
+
+      addr_hit[4]: begin
+        reg_rdata_next[0] = rst_usb_n_qs;
+      end
+
+      default: begin
+        reg_rdata_next = '1;
+      end
+    endcase
+  end
+
+  // Assertions for Register Interface
+  `ASSERT_PULSE(wePulse, reg_we)
+  `ASSERT_PULSE(rePulse, reg_re)
+
+  `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o.d_valid)
+
+  `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit))
+
+  // this is formulated as an assumption such that the FPV testbenches do disprove this
+  // property by mistake
+  `ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.parity_en == 1'b0)
+
+endmodule
diff --git a/hw/lint/Makefile b/hw/lint/Makefile
index 215e9db..d59a036 100644
--- a/hw/lint/Makefile
+++ b/hw/lint/Makefile
@@ -34,6 +34,7 @@
         ip-usbdev              \
         ip-usb_fs_nb_pe        \
         ip-usbuart             \
+        ip-rstmgr              \
         tlul-socket_1n         \
         tlul-socket_m1         \
         tlul-adapter_reg       \
diff --git a/hw/top_earlgrey/top_earlgrey.core b/hw/top_earlgrey/top_earlgrey.core
index d34ddb3..599654b 100644
--- a/hw/top_earlgrey/top_earlgrey.core
+++ b/hw/top_earlgrey/top_earlgrey.core
@@ -30,6 +30,7 @@
       - lowrisc:ip:usbdev
       - lowrisc:ip:xbar_main
       - lowrisc:ip:xbar_peri
+      - lowrisc:ip:rstmgr
       - lowrisc:tlul:headers
       - lowrisc:prim:all
     files:
diff --git a/hw/top_earlgrey/top_earlgrey_verilator.cc b/hw/top_earlgrey/top_earlgrey_verilator.cc
index 2198243..bbbbc2e 100644
--- a/hw/top_earlgrey/top_earlgrey_verilator.cc
+++ b/hw/top_earlgrey/top_earlgrey_verilator.cc
@@ -30,6 +30,7 @@
       "generic."
       "u_impl_generic");
   simctrl.RegisterExtension(&memutil);
+  simctrl.SetInitialResetDelay(100);
 
   std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
             << "=================================" << std::endl