[topgen] Generate Software Definitions for Alert Handler

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/hw/top_earlgrey/data/top_earlgrey.c.tpl b/hw/top_earlgrey/data/top_earlgrey.c.tpl
index a20f4e4..4cfbabe 100644
--- a/hw/top_earlgrey/data/top_earlgrey.c.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.c.tpl
@@ -11,3 +11,11 @@
  * `${helper.plic_sources.name.as_c_type()}`.
  */
 ${helper.plic_mapping.render_definition()}
+
+/**
+ * Alert Handler Alert Source to Peripheral Map
+ *
+ * This array is a mapping from `${helper.alert_alerts.name.as_c_type()}` to
+ * `${helper.alert_sources.name.as_c_type()}`.
+ */
+${helper.alert_mapping.render_definition()}
diff --git a/hw/top_earlgrey/data/top_earlgrey.h.tpl b/hw/top_earlgrey/data/top_earlgrey.h.tpl
index 8985179..47e7a5d 100644
--- a/hw/top_earlgrey/data/top_earlgrey.h.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.h.tpl
@@ -116,6 +116,30 @@
  */
 ${helper.plic_targets.render()}
 
+/**
+ * Alert Handler Source Peripheral.
+ *
+ * Enumeration used to determine which peripheral asserted the corresponding
+ * alert.
+ */
+${helper.alert_sources.render()}
+
+/**
+ * Alert Handler Alert Source.
+ *
+ * Enumeration of all Alert Handler Alert Sources. The alert sources belonging to
+ * the same peripheral are guaranteed to be consecutive.
+ */
+${helper.alert_alerts.render()}
+
+/**
+ * Alert Handler Alert Source to Peripheral Map
+ *
+ * This array is a mapping from `${helper.alert_alerts.name.as_c_type()}` to
+ * `${helper.alert_sources.name.as_c_type()}`.
+ */
+${helper.alert_mapping.render_declaration()}
+
 // Header Extern Guard
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.c b/hw/top_earlgrey/sw/autogen/top_earlgrey.c
index ebaa89a..9b2ad78 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.c
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.c
@@ -96,3 +96,26 @@
   [kTopEarlgreyPlicIrqIdOtbnErr] = kTopEarlgreyPlicPeripheralOtbn,
 };
 
+
+/**
+ * Alert Handler Alert Source to Peripheral Map
+ *
+ * This array is a mapping from `top_earlgrey_alert_id_t` to
+ * `top_earlgrey_alert_peripheral_t`.
+ */
+const top_earlgrey_alert_peripheral_t
+    top_earlgrey_alert_for_peripheral[12] = {
+  [kTopEarlgreyAlertIdAesCtrlErr] = kTopEarlgreyAlertPeripheralAes,
+  [kTopEarlgreyAlertIdHmacMsgPushShaDisabled] = kTopEarlgreyAlertPeripheralHmac,
+  [kTopEarlgreyAlertIdOtbnImemUncorrectable] = kTopEarlgreyAlertPeripheralOtbn,
+  [kTopEarlgreyAlertIdOtbnDmemUncorrectable] = kTopEarlgreyAlertPeripheralOtbn,
+  [kTopEarlgreyAlertIdOtbnRegUncorrectable] = kTopEarlgreyAlertPeripheralOtbn,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts0] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts1] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts2] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts3] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts4] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts5] = kTopEarlgreyAlertPeripheralSensorCtrl,
+  [kTopEarlgreyAlertIdSensorCtrlAstAlerts6] = kTopEarlgreyAlertPeripheralSensorCtrl,
+};
+
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.h b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
index 143fbd2..db39d92 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.h
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
@@ -590,6 +590,51 @@
   kTopEarlgreyPlicTargetLast = 0, /**< \internal Final PLIC target */
 } top_earlgrey_plic_target_t;
 
+/**
+ * Alert Handler Source Peripheral.
+ *
+ * Enumeration used to determine which peripheral asserted the corresponding
+ * alert.
+ */
+typedef enum top_earlgrey_alert_peripheral {
+  kTopEarlgreyAlertPeripheralAes = 0, /**< aes */
+  kTopEarlgreyAlertPeripheralHmac = 1, /**< hmac */
+  kTopEarlgreyAlertPeripheralOtbn = 2, /**< otbn */
+  kTopEarlgreyAlertPeripheralSensorCtrl = 3, /**< sensor_ctrl */
+  kTopEarlgreyAlertPeripheralLast = 3, /**< \internal Final Alert peripheral */
+} top_earlgrey_alert_peripheral_t;
+
+/**
+ * Alert Handler Alert Source.
+ *
+ * Enumeration of all Alert Handler Alert Sources. The alert sources belonging to
+ * the same peripheral are guaranteed to be consecutive.
+ */
+typedef enum top_earlgrey_alert_id {
+  kTopEarlgreyAlertIdAesCtrlErr = 0, /**< aes_ctrl_err */
+  kTopEarlgreyAlertIdHmacMsgPushShaDisabled = 1, /**< hmac_msg_push_sha_disabled */
+  kTopEarlgreyAlertIdOtbnImemUncorrectable = 2, /**< otbn_imem_uncorrectable */
+  kTopEarlgreyAlertIdOtbnDmemUncorrectable = 3, /**< otbn_dmem_uncorrectable */
+  kTopEarlgreyAlertIdOtbnRegUncorrectable = 4, /**< otbn_reg_uncorrectable */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts0 = 5, /**< sensor_ctrl_ast_alerts 0 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts1 = 6, /**< sensor_ctrl_ast_alerts 1 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts2 = 7, /**< sensor_ctrl_ast_alerts 2 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts3 = 8, /**< sensor_ctrl_ast_alerts 3 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts4 = 9, /**< sensor_ctrl_ast_alerts 4 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts5 = 10, /**< sensor_ctrl_ast_alerts 5 */
+  kTopEarlgreyAlertIdSensorCtrlAstAlerts6 = 11, /**< sensor_ctrl_ast_alerts 6 */
+  kTopEarlgreyAlertIdLast = 11, /**< \internal The Last Valid Alert ID. */
+} top_earlgrey_alert_id_t;
+
+/**
+ * Alert Handler Alert Source to Peripheral Map
+ *
+ * This array is a mapping from `top_earlgrey_alert_id_t` to
+ * `top_earlgrey_alert_peripheral_t`.
+ */
+extern const top_earlgrey_alert_peripheral_t
+    top_earlgrey_alert_for_peripheral[12];
+
 // Header Extern Guard
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/util/topgen/c.py b/util/topgen/c.py
index f62eb9f..17ce546 100644
--- a/util/topgen/c.py
+++ b/util/topgen/c.py
@@ -4,7 +4,6 @@
 """This contains a class which is used to help generate `top_{name}.h` and
 `top_{name}.h`.
 """
-
 from collections import OrderedDict
 
 from mako.template import Template
@@ -137,6 +136,19 @@
 
         self._init_plic_targets()
         self._init_plic_mapping()
+        self._init_alert_mapping()
+
+    def modules(self):
+        return [(m["name"],
+                 MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
+                              m["base_addr"], m["size"]))
+                for m in self.top["module"]]
+
+    def memories(self):
+        return [(m["name"],
+                 MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
+                              m["base_addr"], m["size"]))
+                for m in self.top["memory"]]
 
     def _init_plic_targets(self):
         enum = CEnum(self._top_name + Name(["plic", "target"]))
@@ -153,10 +165,11 @@
         """We eventually want to generate a mapping from interrupt id to the
         source peripheral.
 
-        In order to do so, we generate two enums, and store the generated names
-        in a dictionary that represents the mapping.
+        In order to do so, we generate two enums (one for interrupts, one for
+        sources), and store the generated names in a dictionary that represents
+        the mapping.
 
-        Plic Interrupt ID 0 corresponds to no interrupt, and so no peripheral,
+        PLIC Interrupt ID 0 corresponds to no interrupt, and so no peripheral,
         so we encode that in the enum as "unknown".
 
         The interrupts have to be added in order, with "none" first, to ensure
@@ -211,14 +224,55 @@
         self.plic_interrupts = interrupts
         self.plic_mapping = plic_mapping
 
-    def modules(self):
-        return [(m["name"],
-                 MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
-                              m["base_addr"], m["size"]))
-                for m in self.top["module"]]
+    def _init_alert_mapping(self):
+        """We eventually want to generate a mapping from alert id to the source
+        peripheral.
 
-    def memories(self):
-        return [(m["name"],
-                 MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
-                              m["base_addr"], m["size"]))
-                for m in self.top["memory"]]
+        In order to do so, we generate two enums (one for alerts, one for
+        sources), and store the generated names in a dictionary that represents
+        the mapping.
+
+        Alert Handler has no concept of "no alert", unlike the PLIC.
+
+        The alerts have to be added in order, to ensure that they get the
+        correct mapping to their alert id, which is used for addressing the
+        right registers and bits.
+        """
+        sources = CEnum(self._top_name + Name(["alert", "peripheral"]))
+        alerts = CEnum(self._top_name + Name(["alert", "id"]))
+        alert_mapping = CArrayMapping(
+            self._top_name + Name(["alert", "for", "peripheral"]),
+            sources.name)
+
+        # When we generate the `alerts` enum, the only info we have about the
+        # source is the module name. We'll use `source_name_map` to map a short
+        # module name to the full name object used for the enum constant.
+        source_name_map = {}
+
+        for name in self.top["alert_module"]:
+            source_name = sources.add_constant(Name.from_snake_case(name),
+                                               docstring=name)
+            source_name_map[name] = source_name
+
+        sources.add_last_constant("Final Alert peripheral")
+
+        for alert in self.top["alert"]:
+            if "width" in alert and int(alert["width"]) != 1:
+                for i in range(int(alert["width"])):
+                    name = Name.from_snake_case(
+                        alert["name"]) + Name([str(i)])
+                    irq_id = alerts.add_constant(name, docstring="{} {}".format(
+                                                         alert["name"], i))
+                    source_name = source_name_map[alert["module_name"]]
+                    alert_mapping.add_entry(irq_id, source_name)
+            else:
+                name = Name.from_snake_case(alert["name"])
+                alert_id = alerts.add_constant(name, docstring=alert["name"])
+                source_name = source_name_map[alert["module_name"]]
+                alert_mapping.add_entry(alert_id, source_name)
+
+        alerts.add_last_constant("The Last Valid Alert ID.")
+
+        self.alert_sources = sources
+        self.alert_alerts = alerts
+        self.alert_mapping = alert_mapping