[otp_ctrl] Add draft hjson for otp controller

Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/Makefile b/hw/Makefile
index 266a247..f86b0de 100644
--- a/hw/Makefile
+++ b/hw/Makefile
@@ -14,6 +14,7 @@
        hmac          \
        i2c           \
        nmi_gen       \
+       otp_ctrl      \
        padctrl       \
        pinmux        \
        pwrmgr        \
diff --git a/hw/ip/otp_ctrl/data/otp_ctrl.hjson b/hw/ip/otp_ctrl/data/otp_ctrl.hjson
new file mode 100644
index 0000000..05f1dec
--- /dev/null
+++ b/hw/ip/otp_ctrl/data/otp_ctrl.hjson
@@ -0,0 +1,475 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{ name: "otp_ctrl",
+  clock_primary: "clk_i",
+  bus_device: "tlul",
+  bus_host: "none",
+  estimated_gates: "32kGE" // Excluding OTP macro
+
+  ///////////////////////////
+  // Interrupts and Alerts //
+  ///////////////////////////
+
+  interrupt_list: [
+    { name: "otp_access_done",
+      desc: "A direct access command has completed."
+    }
+    { name: "otp_ctrl_err",
+      desc: "An error has occurred during an access. Check the ERR_CODE register to get more information."
+    }
+  ],
+
+  alert_list: [
+    // fast integrity check of buffered OTP partitions.
+    { name: "otp_reg_parity_mismatch",
+      desc: "This alert triggers if hardware detects a parity bit error in the buffered partitions.",
+    }
+    // background check digest mismatch
+    { name: "otp_reg_digest_mismatch",
+      desc: "This alert triggers if the digest over the buffered registers does not match with the digest stored in OTP.",
+    }
+  ],
+
+  ////////////////
+  // Parameters //
+  ////////////////
+  param_list: [
+    { name: "NumLcPartitionWords",
+      desc: "Number of 32bit words in the life cycle partition",
+      type: "int",
+      default: "4", // 4 x 32bit = 128bit
+      local: "true"
+    },
+    { name: "NumSecretPartitionWords",
+      desc: "Number of 32bit words in the secret partition",
+      type: "int",
+      default: "32", // 32 x 32bit = 1024bit
+      local: "true"
+    },
+    { name: "NumHwCfgWords",
+      desc: "Number of 32bit words in the hardware config partition",
+      type: "int",
+      default: "8", // 8 x 32bit = 256bit
+      local: "true"
+    },
+    // TODO: this will change once we add more switches to this partition.
+    { name: "NumHwCfgReservedRegs",
+      desc: "Number of unallocated 32bit OTP words in the hardware partition",
+      type: "int",
+      default: "6", // 6 x 32bit = 192bit
+      local: "true"
+    },
+    { name: "NumSwCfgPartitionWords",
+      desc: "Number of 32bit words in the software config partition",
+      type: "int",
+      default: "212", // 212 x 32bit = 256bit
+      local: "true"
+    },
+    { name: "NumSwCfgWindowWords",
+      desc: "Number of 32bit words in the software config partition window",
+      type: "int",
+      default: "256", // 212 x 32bit = 6784bit, but window is 256 large for alignment reasons
+      local: "true"
+    },
+    { name: "NumDebugWindowWords",
+      desc: "Number of 32bit words in the debug window.",
+      type: "int",
+      default: "500",
+      local: "true"
+    },
+  ]
+
+  /////////////////////////////
+  // Intermodule Connections //
+  /////////////////////////////
+  inter_signal_list: [
+    // Power manager init command
+    { struct: "pwr_otp_init"
+      type:   "req_rsp"
+      name:   "pwr_otp_init"
+      act:    "receiver"
+      package: "otp_ctrl_pkg"
+    }
+    // Status output to power manager
+    { struct: "otp_pwr_state"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "otp_pwr_state"
+      act:    "sender"
+      package: "otp_ctrl_pkg"
+    }
+    // LC transition command
+    { struct: "lc_otp_program"
+      type:   "req_rsp"
+      name:   "lc_otp_program"
+      act:    "receiver"
+      package: "otp_ctrl_pkg"
+    }
+    // Broadcast to LC
+    { struct: "otp_lc_data"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "otp_lc_data"
+      act:    "sender"
+      package: "otp_ctrl_pkg"
+    }
+    // Broadcast from LC
+    { struct: "lc_tx_t"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "lc_provision_en"
+      act:    "receiver"
+      package: "otp_ctrl_pkg" // TODO: move to LC package
+    }
+    { struct: "lc_tx_t"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "lc_test_en"
+      act:    "receiver"
+      package: "otp_ctrl_pkg" // TODO: move to LC package
+    }
+    //Broadcast to Key Manager
+    { struct: "keymgr_key"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "otp_keymgr_key"
+      act:    "sender"
+      package: "otp_ctrl_pkg" // TODO: move this to keymgr package
+    }
+    //Broadcast to Flash Controller
+    { struct: "flash_key"
+      type:   "broadcast"  // no `_req/rsp`
+      name:   "otp_flash_key"
+      act:    "sender"
+      package: "otp_ctrl_pkg"
+    }
+  ] // inter_signal_list
+
+  regwidth: "32",
+  registers: [
+
+    ////////////////////////
+    // Ctrl / Status CSRs //
+    ////////////////////////
+    { name: "STATUS",
+      desc: "OTP status register.",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "31:0",
+          name: "err_code",
+          desc: '''This register holds information on the OTP controller status.
+                TODO: make this an enum {INIT, READY, ERROR, LOCKED, WRITE_PENDING, READ_PENDING}
+                '''
+        }
+      ]
+    }
+    { name: "ERR_CODE",
+      desc: "OTP Ctrl Error Code",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "31:0",
+          name: "err_code",
+          desc: '''This register holds information on the error that occurred.
+                To be checked after an error interrupt occurred.
+                TODO: add reference to error type here.
+                '''
+        }
+      ]
+    }
+    { name: "DIRECT_ACCESS_CMD",
+      desc: "OTP array command for direct access.",
+      swaccess: "r0w1c",
+      hwaccess: "hro",
+      hwext:    "true",
+      hwqe:     "true",
+      fields: [
+        { bits: "0",
+          name: "read",
+          desc: "Initiates a readout sequence that reads the location specified by !!DIRECT_ACCESS_ADDRESS and !!DIRECT_ACCESS_SIZE. The command places the read data in !!DIRECT_ACCESS_RDATA."
+        }
+        { bits: "1",
+          name: "write",
+          desc: "Initiates a programming sequence that writes the !!DIRECT_ACCESS_WDATA to the location specified by !!DIRECT_ACCESS_ADDRESS and !!DIRECT_ACCESS_SIZE."
+        }
+      ]
+    }
+    { name: "DIRECT_ACCESS_ADDRESS",
+      desc: "OTP array address for direct access.",
+      swaccess: "rw",
+      hwaccess: "hro",
+      fields: [
+        { bits: "9:0",
+          desc: '''This is the address for the OTP word to be read or written through
+          the direct access interface. Note that the address is 32bit aligned internally,
+          hence bit 1:0 are ignored.'''
+        }
+      ]
+    }
+    { name: "DIRECT_ACCESS_SIZE",
+      desc: "OTP array acces size for direct access.",
+      swaccess: "rw",
+      hwaccess: "hro",
+      fields: [
+        { bits: "1:0",
+          desc: '''This is the access size for for the OTP word to be read or written through
+          the direct access interface. Note that the address is always aligned to the
+          access size internally. 0 = 1 Byte, 1 = 2 Bytes, 2 = 4 Bytes, 3 = 8 Bytes.'''
+        }
+      ]
+    }
+    { multireg: { name:     "DIRECT_ACCESS_WDATA",
+                  desc:     '''Write data for direct access. Note that only 1s are effectively programmed into the array. 0s do not have any effect.
+                  ''',
+                  count:    "2", // 2 x 32bit = 64bit
+                  swaccess: "rw",
+                  hwaccess: "hro",
+                  hwqe:     "true",
+                  cname:    "WORD",
+                  fields: [
+                    { bits: "31:0"
+                    }
+                  ]
+                }
+    },
+    { multireg: { name:     "DIRECT_ACCESS_RDATA",
+                  desc:     '''Read data for direct access.
+                  ''',
+                  count:    "2", // 2 x 32bit = 64bit
+                  swaccess: "ro",
+                  hwaccess: "hwo",
+                  cname:    "WORD",
+                  fields: [
+                    { bits: "31:0"
+                      desc: ""
+                    }
+                  ]
+                }
+    },
+
+    ///////////////////////
+    // Integrity Digests //
+    ///////////////////////
+    { skipto: "0x050" }
+
+    { name: "SECRET_INTEGRITY_DIGEST_CALC",
+      desc: "Compute and program digest for secret partition.",
+      swaccess: "r0w1c",
+      hwaccess: "hro",
+      fields: [
+        { bits: "0",
+          desc: '''Writing 1 to this register computes the partition digest and burns it into OTP.
+                Note: this effectively locks write and read access to this partition after power cycling the device.
+                '''
+        }
+      ]
+    }
+
+    { name: "HW_CFG_INTEGRITY_DIGEST_CALC",
+      desc: "Compute and program digest for the hardware config partition.",
+      swaccess: "r0w1c",
+      hwaccess: "hro",
+      fields: [
+        { bits: "0",
+          desc: '''Writing 1 to this register computes the partition digest and burns it into OTP.
+                Note that this effectively locks write access to this partition after power cycling the device.
+                '''
+        }
+      ]
+    }
+
+    { name: "SW_CFG_INTEGRITY_DIGEST_CALC",
+      desc: "Compute and program digest for the software config partition.",
+      swaccess: "r0w1c",
+      hwaccess: "hro",
+      fields: [
+        { bits: "0",
+          desc: '''Writing 1 to this register computes the partition digest and burns it into OTP.
+                Note that effectively locks write access to this partition after power cycling the device.
+                '''
+        }
+      ]
+    }
+
+    { name: "SECRET_INTEGRITY_DIGEST",
+      desc: "Integrity digest for the secret config partition",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      hwext:    "true",
+      fields: [
+        { bits: "31:0",
+          desc: '''The integrity digest is 0 by default. Once the partition has been locked,
+                this digest is set to a nonzero value.'''
+        }
+      ]
+    }
+    { name: "HW_CFG_INTEGRITY_DIGEST",
+      desc: "Hardware config partition integrity digest.",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      hwext:    "true",
+      fields: [
+        { bits: "31:0",
+          desc: '''The integrity digest is 0 by default. Once the partition has been locked,
+                this digest is set to a nonzero value.'''
+        }
+      ]
+    }
+    { name: "SW_CFG_INTEGRITY_DIGEST",
+      desc: "Integrity digest for the software config partition",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      hwext:    "true",
+      fields: [
+        { bits: "31:0",
+          desc: '''The integrity digest is 0 by default. Once the partition has been locked,
+                this digest is set to a nonzero value.'''
+        }
+      ]
+    }
+
+    //////////////////////////
+    // Life Cycle Partition //
+    //////////////////////////
+    { skipto: "0x100" }
+
+    { multireg: { name:     "LC_STATE",
+                  desc:     '''Life cycle state.
+                  ''',
+                  count:    "6", // 6 x8bit = 48bit
+                  swaccess: "ro",
+                  hwaccess: "hwo",
+                  cname:    "BYTE",
+                  fields: [
+                    { bits: "7:0"
+                      desc: "TODO: explain state encoding"
+                    }
+                  ]
+                }
+    },
+    { name: "ID_STATE",
+      desc: "ID state of the device.",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "7:0",
+          desc: '''The ID state is BLANK when set to 0x00, and CREATOR_PERSONALIZED if set to 0x6D.
+                Note that this affects certain secrets stored in FLASH and OTP. For instance, the
+                !!CREATOR_ROOT_KEY_SHARE0 stored in OTP is readable and writable by software if BLANK, and
+                all hardware outputs to the LC controller and the key manager will be tied to 0 in this state.
+                If ID state is CREATOR_PERSONALIZED the !!CREATOR_ROOT_KEY_SHARE0 is not accessible by software anymore,
+                and all hardware outputs to the LC controller and key manager will be driven.
+                '''
+        }
+      ]
+    }
+    // TODO: update XXX related bits
+    { name: "TEST_XXX_CNT",
+      desc: "Counters related to the test and XXX states."
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "7:0",
+          name: "TEST_STATE_CNT"
+          desc: "TODO: add description of this counter"
+        }
+        { bits: "15:8",
+          name: "TEST_UNLOCK_CNT"
+          desc: "Number of times test unlock has been attempted."
+        }
+        { bits: "23:16",
+          name: "TEST_EXIT_CNT"
+          desc: "Number of times test exit has been attempted."
+        }
+        { bits: "31:24",
+          name: "XXX_UNLOCK_CNT"
+          desc: "Number of times XXX unlock has been attempted."
+        }
+      ]
+    }
+    { name: "TRANSITION_CNT",
+      desc: "Counter for total amount of state transition attempts.",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "15:0",
+          desc: '''This counter will be incremented upon each state transition attempt,
+                or when the transition command coming from the life cycle controller is invalid.
+                '''
+        }
+      ]
+    }
+
+    ///////////////////////////////
+    // Hardware Config Partition //
+    ///////////////////////////////
+    { skipto: "0x200" }
+
+    // TODO: update XXX related bits
+    { name: "HW_CFG_LOCK",
+      desc: "Hardware config lock values.",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      hwext:    "true",
+      fields: [
+        { bits: "2:0",
+          name: "TEST_TOKENS_LOCK",
+          desc: '''TEST token lock value. When set to nonzero, the life cycle
+                TEST_*_TOKENS are no longer accessible to software.
+                '''
+        }
+        {
+          bits: "7:3",
+          name: "XXX_TOKEN_LOCK",
+          desc: '''XXX token lock value. When set to nonzero, the life cycle
+                XXX_*_TOKENS are no longer accessible to software.
+                '''
+        }
+      ]
+    }
+    // TODO: update this field once known
+    { multireg: { name:     "HW_CFG",
+                  desc:     '''Unallocated OTP bits.
+                  ''',
+                  count:    "NumHwCfgReservedRegs", // 7 x32bit = 192bit
+                  swaccess: "ro",
+                  hwaccess: "hwo",
+                  cname:    "WORD",
+                  fields: [
+                    { bits: "31:0"
+                    }
+                  ]
+                }
+    },
+
+    ///////////////////////////////
+    // Software Config Partition //
+    ///////////////////////////////
+
+    // TODO: split SW partition into creator and owner partitions
+    { skipto: "0x300" }
+
+    { window: {
+        name: "SW_CFG"
+        items: "NumSwCfgWindowWords" // 212 x 32bit = 6784bit, but window is 256 large for alignment reasons
+        swaccess: "ro",
+        desc: '''Any read to this window directly maps to the corresponding offset in the software
+              config partition, and triggers an OTP readout of the bytes requested. Note that the transaction
+              will block until OTP readout has completed.
+              '''
+      }
+    }
+
+    //////////////////////
+    // Test Access Port //
+    //////////////////////
+    { skipto: "0x800" }
+
+    { window: {
+        name: "TEST_ACCESS"
+        items: "NumDebugWindowWords"
+        swaccess: "rw",
+        desc: '''This directly maps to the OTP macro address map. Note that this is only
+              accessible during the test life cycle state of the OpenTitan device.
+              '''
+      }
+    }
+  ],
+}
diff --git a/hw/ip/otp_ctrl/doc/_index.md b/hw/ip/otp_ctrl/doc/_index.md
new file mode 100644
index 0000000..2ac9bee
--- /dev/null
+++ b/hw/ip/otp_ctrl/doc/_index.md
@@ -0,0 +1,54 @@
+---
+title: "OTP Controller Technical Specification"
+---
+
+
+# Overview
+
+This document specifies the functionality of the one time programmable (OTP) memory controller.
+The OTP controller is a module that is a peripheral on the chip interconnect bus, and thus follows the [Comportability Specification]({{< relref "doc/rm/comportability_specification" >}}).
+
+
+## Features
+
+## Description
+
+# Theory of Operations
+
+## Block Diagram
+
+## Hardware Interfaces
+
+### Parameters
+
+The following table lists the main parameters used throughout the OTP controller design.
+
+Localparam     | Default (Max)         | Top Earlgrey | Description
+---------------|-----------------------|--------------|---------------
+
+### Signals
+
+{{< hwcfg "hw/ip/otp_ctrl/data/otp_ctrl.hjson" >}}
+
+Signal                  | Direction        | Type                      | Description
+------------------------|------------------|---------------------------|---------------
+
+
+## Design Details
+
+# Programmers Guide
+
+
+## Power-up and Reset Considerations
+
+## Initialization
+
+## Interrupt Handling
+
+
+## Register Table
+
+{{< registers "hw/ip/otp_ctrl/data/otp_ctrl.hjson" >}}
+
+# Additional Notes
+
diff --git a/util/build_docs.py b/util/build_docs.py
index f24519e..80699d8 100755
--- a/util/build_docs.py
+++ b/util/build_docs.py
@@ -56,6 +56,7 @@
         "hw/ip/hmac/data/hmac.hjson",
         "hw/ip/i2c/data/i2c.hjson",
         "hw/ip/nmi_gen/data/nmi_gen.hjson",
+        "hw/ip/otp_ctrl/data/otp_ctrl.hjson",
         "hw/ip/padctrl/data/padctrl.hjson",
         "hw/top_earlgrey/ip/pinmux/data/autogen/pinmux.hjson",
         "hw/top_earlgrey/ip/rv_plic/data/autogen/rv_plic.hjson",