Start of public OpenTitan development history

Code contributors:
Alex Bradbury <asb@lowrisc.org>
Cindy Chen <chencindy@google.com>
Eunchan Kim <eunchan@google.com>
Gaurang Chitroda <gaurangg@google.com>
Mark Hayter <mark.hayter@gmail.com>
Michael Schaffner <msf@google.com>
Miguel Osorio <miguelosorio@google.com>
Nils Graf <nilsg@google.com>
Philipp Wagner <phw@lowrisc.org>
Pirmin Vogel <vogelpi@lowrisc.org>
Ram Babu Penugonda <rampenugonda@google.com>
Scott Johnson <scottdj@google.com>
Shail Kushwah <kushwahs@google.com>
Srikrishna Iyer <sriyer@google.com>
Steve Nelson <Steve.Nelson@wdc.com>
Tao Liu <taliu@google.com>
Timothy Chen <timothytim@google.com>
Tobias Wölfel <tobias.woelfel@mailbox.org>
Weicai Yang <weicai@google.com>
diff --git a/util/reggen/reg_top.tpl.sv b/util/reggen/reg_top.tpl.sv
new file mode 100644
index 0000000..d47c927
--- /dev/null
+++ b/util/reggen/reg_top.tpl.sv
@@ -0,0 +1,552 @@
+// 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`
+<%
+  num_wins = len(block.wins)
+  num_wins_width = ((num_wins+1).bit_length()) - 1
+  num_dsp  = num_wins + 1
+%>
+
+module ${block.name}_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,
+% if num_wins != 0:
+
+  // Output port for window
+  output tlul_pkg::tl_h2d_t tl_win_o  [${num_wins}],
+  input  tlul_pkg::tl_d2h_t tl_win_i  [${num_wins}],
+
+% endif
+  // To HW
+  output ${block.name}_reg_pkg::${block.name}_reg2hw_t reg2hw, // Write
+  input  ${block.name}_reg_pkg::${block.name}_hw2reg_t hw2reg  // Read
+);
+
+  import ${block.name}_reg_pkg::* ;
+
+  localparam AW = ${block.addr_width};
+  localparam IW = $bits(tl_i.a_source);
+  localparam DW = ${block.width};
+  localparam DBW = DW/8;                    // Byte Width
+  localparam logic [$clog2($clog2(DBW)+1)-1:0] FSZ = $clog2(DBW); // Full Size 2^(FSZ) = DBW;
+
+  // register signals
+  logic          reg_we;
+  logic          reg_re;
+  logic [AW-1:0] reg_addr;
+  logic [DW-1:0] reg_wdata;
+  logic          reg_valid;
+  logic [DW-1:0] reg_rdata;
+  logic          tl_malformed, tl_addrmiss;
+
+  // Bus signals
+  tlul_pkg::tl_d_op_e rsp_opcode; // AccessAck or AccessAckData
+  logic          reqready;
+  logic [IW-1:0] reqid;
+  logic [IW-1:0] rspid;
+
+  logic          outstanding;
+
+  tlul_pkg::tl_h2d_t tl_reg_h2d;
+  tlul_pkg::tl_d2h_t tl_reg_d2h;
+
+% if num_wins == 0:
+  assign tl_reg_h2d = tl_i;
+  assign tl_o       = tl_reg_d2h;
+% else:
+  tlul_pkg::tl_h2d_t tl_socket_h2d [${num_dsp}];
+  tlul_pkg::tl_d2h_t tl_socket_d2h [${num_dsp}];
+
+  logic [${num_wins_width}:0] reg_steer;
+
+  // socket_1n connection
+  assign tl_reg_h2d = tl_socket_h2d[${num_wins}];
+  assign tl_socket_d2h[${num_wins}] = tl_reg_d2h;
+
+  % for i,t in enumerate(block.wins):
+  assign tl_win_o[${i}] = tl_socket_h2d[${i}];
+  assign tl_socket_d2h[${i}] = tl_win_i[${i}];
+  % endfor
+
+  // Create Socket_1n
+  tlul_socket_1n #(
+    .N          (${num_dsp}),
+    .HReqPass   (1'b1),
+    .HRspPass   (1'b1),
+    .DReqPass   ({${num_dsp}{1'b1}}),
+    .DRspPass   ({${num_dsp}{1'b1}}),
+    .HReqDepth  (4'h1),
+    .HRspDepth  (4'h1),
+    .DReqDepth  ({${num_dsp}{4'h1}}),
+    .DRspDepth  ({${num_dsp}{4'h1}})
+  ) u_socket (
+    .clk_i,
+    .rst_ni,
+    .tl_h_i (tl_i),
+    .tl_h_o (tl_o),
+    .tl_d_o (tl_socket_h2d),
+    .tl_d_i (tl_socket_d2h),
+    .dev_select (reg_steer)
+  );
+
+  // Create steering logic
+  always_comb begin
+    reg_steer = ${num_dsp-1};       // Default set to register
+
+    // TODO: Can below codes be unique case () inside ?
+  % for i,w in enumerate(block.wins):
+      % if w.limit_addr == 2**block.addr_width:
+    if (tl_i.a_address[AW-1:0] >= ${w.base_addr}) begin
+      // Exceed or meet the address range. Removed the comparison of limit addr ${"'h %x" % w.limit_addr}
+      % else:
+    if (tl_i.a_address[AW-1:0] >= ${w.base_addr} && tl_i.a_address[AW-1:0] < ${w.limit_addr}) begin
+      % endif
+      reg_steer = ${i};
+    end
+  % endfor
+  end
+% endif
+
+  // TODO(eunchan): Fix it after bus interface is finalized
+  assign reg_we = tl_reg_h2d.a_valid && tl_reg_d2h.a_ready &&
+                  ((tl_reg_h2d.a_opcode == tlul_pkg::PutFullData) ||
+                   (tl_reg_h2d.a_opcode == tlul_pkg::PutPartialData));
+  assign reg_re = tl_reg_h2d.a_valid && tl_reg_d2h.a_ready &&
+                  (tl_reg_h2d.a_opcode == tlul_pkg::Get);
+  assign reg_addr = tl_reg_h2d.a_address[AW-1:0];
+  assign reg_wdata = tl_reg_h2d.a_data;
+
+  assign tl_reg_d2h.d_valid  = reg_valid;
+  assign tl_reg_d2h.d_opcode = rsp_opcode;
+  assign tl_reg_d2h.d_param  = '0;
+  assign tl_reg_d2h.d_size   = FSZ;         // always Full Size
+  assign tl_reg_d2h.d_source = rspid;
+  assign tl_reg_d2h.d_sink   = '0;          // Used in TL-C
+  assign tl_reg_d2h.d_data   = reg_rdata;
+  assign tl_reg_d2h.d_user   = '0;          // Doesn't allow additional features yet
+  assign tl_reg_d2h.d_error  = tl_malformed | tl_addrmiss;
+
+  assign tl_reg_d2h.a_ready  = reqready;
+
+  assign reqid     = tl_reg_h2d.a_source;
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      tl_malformed <= 1'b1;
+    end else if (tl_reg_h2d.a_valid && tl_reg_d2h.a_ready) begin
+      if ((tl_reg_h2d.a_opcode != tlul_pkg::Get) &&
+          (tl_reg_h2d.a_opcode != tlul_pkg::PutFullData) &&
+          (tl_reg_h2d.a_opcode != tlul_pkg::PutPartialData)) begin
+        tl_malformed <= 1'b1;
+      // Only allow Full Write with full mask
+      end else if (tl_reg_h2d.a_size != FSZ || tl_reg_h2d.a_mask != {DBW{1'b1}}) begin
+        tl_malformed <= 1'b1;
+      end else if (tl_reg_h2d.a_user.parity_en == 1'b1) begin
+        tl_malformed <= 1'b1;
+      end else begin
+        tl_malformed <= 1'b0;
+      end
+    end
+  end
+  // TODO(eunchan): Revise Register Interface logic after REG INTF finalized
+  // TODO(eunchan): Make concrete scenario
+  //    1. Write: No response, so that it can guarantee a request completes a clock after we
+  //              It means, bus_reg_ready doesn't have to be lowered.
+  //    2. Read: response. So bus_reg_ready should assert after reg_bus_valid & reg_bus_ready
+  //               _____         _____
+  // a_valid _____/     \_______/     \______
+  //         ___________         _____
+  // a_ready            \_______/     \______ <- ERR though no logic malfunction
+  //                     _____________
+  // d_valid ___________/             \______
+  //                             _____
+  // d_ready ___________________/     \______
+  //
+  // Above example is fine but if r.b.r doesn't assert within two cycle, then it can be wrong.
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    // Not to accept new request when a request is handling
+    //   #Outstanding := 1
+    if (!rst_ni) begin
+      reqready <= 1'b0;
+    end else if (reg_we || reg_re) begin
+      reqready <= 1'b0;
+    end else if (outstanding == 1'b0) begin
+      reqready <= 1'b1;
+    end
+  end
+
+  // Request/ Response ID
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      rspid <= '0;
+    end else if (reg_we || reg_re) begin
+      rspid <= reqid;
+    end
+  end
+
+  // Define SW related signals
+  // Format: <reg>_<field>_{wd|we|qs}
+  //        or <reg>_{wd|we|qs} if field == 1 or 0
+  % for r in block.regs:
+    % if len(r.fields) == 1:
+<%
+      msb = r.fields[0].msb
+      lsb = r.fields[0].lsb
+      sig_name = r.name
+      f = r.fields[0]
+      swwraccess = f.swwraccess
+      swrdaccess = f.swrdaccess
+      hwext = r.hwext
+      regwen = r.regwen
+%>\
+${sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen)}\
+    % else:
+      % for f in r.fields:
+<%
+      msb = f.msb
+      lsb = f.lsb
+      sig_name = r.name + "_" + f.name
+      swwraccess = f.swwraccess
+      swrdaccess = f.swrdaccess
+      hwext = r.hwext
+      regwen = r.regwen
+%>\
+${sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen)}\
+      % endfor
+    % endif
+  % endfor
+
+  // Register instances
+  % for r in block.regs:
+  // R[${r.name}]: V(${str(r.hwext)})
+    % if len(r.fields) == 1:
+<%
+      f = r.fields[0]
+      finst_name = r.name
+      fsig_name = r.name
+      msb = f.msb
+      lsb = f.lsb
+      swaccess = f.swaccess
+      swrdaccess = f.swrdaccess
+      swwraccess = f.swwraccess
+      hwaccess = f.hwaccess
+      hwqe = f.hwqe
+      hwre = f.hwre
+      hwext = r.hwext
+      resval = f.resval
+      regwen = r.regwen
+%>
+${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
+    % else:
+      % for f in r.fields:
+<%
+      finst_name = r.name + "_" + f.name
+      fsig_name = r.name + "." + f.name
+      msb = f.msb
+      lsb = f.lsb
+      swaccess = f.swaccess
+      swrdaccess = f.swrdaccess
+      swwraccess = f.swwraccess
+      hwaccess = f.hwaccess
+      hwqe = f.hwqe
+      hwre = f.hwre
+      hwext = r.hwext
+      resval = f.resval
+      regwen = r.regwen
+%>
+  //   F[${f.name}]: ${f.msb}:${f.lsb}
+${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
+      % endfor
+    % endif
+
+  ## for: block.regs
+  % endfor
+
+  logic [${len(block.regs)-1}:0] addr_hit;
+  always_comb begin
+    addr_hit = '0;
+    % for i,r in enumerate(block.regs):
+    addr_hit[${i}] = (reg_addr == ${block.name.upper()}_${r.name.upper()}_OFFSET);
+    % endfor
+  end
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      tl_addrmiss <= 1'b0;
+    end else if (reg_re || reg_we) begin
+      tl_addrmiss <= ~|addr_hit;
+    end
+  end
+
+  // Write Enable signal
+  % for i, r in enumerate(block.regs):
+    % if len(r.fields) == 1:
+<%
+      f = r.fields[0]
+      sig_name = r.name
+      inst_name = r.name
+      msb = f.msb
+      lsb = f.lsb
+      swrdaccess = f.swrdaccess
+      swwraccess = f.swwraccess
+      hwext = r.hwext
+%>
+${we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, i)}\
+    % else:
+      % for f in r.fields:
+<%
+      sig_name = r.name + "_" + f.name
+      inst_name = r.name + "." + f.name
+      msb = f.msb
+      lsb = f.lsb
+      swrdaccess = f.swrdaccess
+      swwraccess = f.swwraccess
+      hwext = r.hwext
+%>
+${we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, i)}\
+      % endfor
+    % endif
+  % endfor
+
+  // Read data return
+  logic [DW-1:0] reg_rdata_next;
+  always_comb begin
+    reg_rdata_next = '0;
+    unique case (1'b1)
+      % for i, r in enumerate(block.regs):
+        % if len(r.fields) == 1:
+<%
+          f = r.fields[0]
+          sig_name = r.name
+          inst_name = r.name
+          msb = f.msb
+          lsb = f.lsb
+          swrdaccess = f.swrdaccess
+%>\
+      addr_hit[${i}]: begin
+${rdata_gen(sig_name, msb, lsb, swrdaccess)}\
+      end
+
+        % else:
+      addr_hit[${i}]: begin
+          % for f in r.fields:
+<%
+          sig_name = r.name + "_" + f.name
+          inst_name = r.name + "." + f.name
+          msb = f.msb
+          lsb = f.lsb
+          swrdaccess = f.swrdaccess
+%>\
+${rdata_gen(sig_name, msb, lsb, swrdaccess)}\
+          % endfor
+      end
+
+        % endif
+      % endfor
+      default: begin
+        reg_rdata_next = '1;
+      end
+    endcase
+  end
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      reg_valid <= 1'b0;
+      reg_rdata <= '0;
+      rsp_opcode <= tlul_pkg::AccessAck;
+    end else if (reg_re || reg_we) begin
+      // Guarantee to return data in a cycle
+      reg_valid <= 1'b1;
+      if (reg_re) begin
+        reg_rdata <= reg_rdata_next;
+        rsp_opcode <= tlul_pkg::AccessAckData;
+      end else begin
+        rsp_opcode <= tlul_pkg::AccessAck;
+      end
+    end else if (tl_reg_h2d.d_ready) begin
+      reg_valid <= 1'b0;
+    end
+  end
+
+  // Outstanding: 1 outstanding at a time. Identical to `reg_valid`
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      outstanding <= 1'b0;
+    end else if (tl_reg_h2d.a_valid && tl_reg_d2h.a_ready) begin
+      outstanding <= 1'b1;
+    end else if (tl_reg_d2h.d_valid && tl_reg_h2d.d_ready) begin
+      outstanding <= 1'b0;
+    end
+  end
+
+  // Assertions for Register Interface
+  `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni)
+  `ASSERT_PULSE(rePulse, reg_re, clk_i, !rst_ni)
+
+  `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> reg_valid, clk_i, !rst_ni)
+
+  `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit), clk_i, !rst_ni)
+
+  `ASSERT(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.parity_en == 1'b0, clk_i, !rst_ni)
+
+endmodule
+<%def name="str_bits_sv(msb, lsb)">\
+% if msb != lsb:
+${msb}:${lsb}\
+% else:
+${msb}\
+% endif
+</%def>\
+<%def name="str_arr_sv(msb, lsb)">\
+% if msb != lsb:
+[${msb-lsb}:0] \
+% endif
+</%def>\
+<%def name="sig_gen(msb, lsb, sig_name, swwraccess, swrdaccess, hwext, regwen)">\
+  % if swrdaccess != SwRdAccess.NONE:
+  logic ${str_arr_sv(msb, lsb)}${sig_name}_qs;
+  % endif
+  % if swwraccess != SwWrAccess.NONE:
+  logic ${str_arr_sv(msb, lsb)}${sig_name}_wd;
+  logic ${sig_name}_we;
+  % endif
+  % if swrdaccess != SwRdAccess.NONE and hwext:
+  logic ${sig_name}_re;
+  % endif
+</%def>\
+<%def name="finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)">\
+  % if hwext:       ## if hwext, instantiate prim_subreg_ext
+  prim_subreg_ext #(
+    .DW    (${msb - lsb + 1})
+  ) u_${finst_name} (
+    % if swrdaccess != SwRdAccess.NONE:
+    .re     (${finst_name}_re),
+    % else:
+    .re     (1'b0),
+    % endif
+    % if swwraccess != SwWrAccess.NONE:
+      % if regwen:
+    // qualified with register enable
+    .we     (${finst_name}_we & ${regwen}_qs),
+      % else:
+    .we     (${finst_name}_we),
+      % endif
+    .wd     (${finst_name}_wd),
+    % else:
+    .we     (1'b0),
+    .wd     ('0),
+    % endif
+    % if hwaccess == HwAccess.HRO:
+    .d      ('0),
+    % else:
+    .d      (hw2reg.${fsig_name}.d),
+    % endif
+    % if hwre:
+    .qre    (reg2hw.${fsig_name}.re),
+    % else:
+    .qre    (),
+    % endif
+    % if hwaccess == HwAccess.HWO:
+    .qe     (),
+    .q      (),
+    % else:
+      % if hwqe:
+    .qe     (reg2hw.${fsig_name}.qe),
+      % else:
+    .qe     (),
+      % endif
+    .q      (reg2hw.${fsig_name}.q ),
+    % endif
+    % if swrdaccess != SwRdAccess.NONE:
+    .qs     (${finst_name}_qs)
+    % else:
+    .qs     ()
+    % endif
+  );
+  % else:       ## if not hwext, instantiate prim_subreg or constant assign
+    % if hwaccess == HwAccess.NONE and swrdaccess == SwRdAccess.RD and swwraccess == SwWrAccess.NONE:
+  // constant-only read
+  assign ${finst_name}_qs = ${msb-lsb+1}'h${"%x" % resval};
+    % else:     ## not hwext not constant
+  prim_subreg #(
+    .DW      (${msb - lsb + 1}),
+    .SWACCESS("${swaccess.name}"),
+    .RESVAL  (${msb-lsb+1}'h${"%x" % resval})
+  ) u_${finst_name} (
+    .clk_i   (clk_i    ),
+    .rst_ni  (rst_ni  ),
+
+      % if swwraccess != SwWrAccess.NONE: ## non-RO types
+        % if regwen:
+    // from register interface (qualified with register enable)
+    .we     (${finst_name}_we & ${regwen}_qs),
+        % else:
+    // from register interface
+    .we     (${finst_name}_we),
+        % endif
+    .wd     (${finst_name}_wd),
+      % else:                             ## RO types
+    .we     (1'b0),
+    .wd     ('0  ),
+      % endif
+
+    // from internal hardware
+      % if hwaccess == HwAccess.HRO or hwaccess == HwAccess.NONE:
+    .de     (1'b0),
+    .d      ('0  ),
+      % else:
+    .de     (hw2reg.${fsig_name}.de),
+    .d      (hw2reg.${fsig_name}.d ),
+      % endif
+
+    // to internal hardware
+      % if hwaccess == HwAccess.HWO or hwaccess == HwAccess.NONE:
+    .qe     (),
+    .q      (),
+      % else:
+        % if hwqe:
+    .qe     (reg2hw.${fsig_name}.qe),
+        % else:
+    .qe     (),
+        % endif
+    .q      (reg2hw.${fsig_name}.q ),
+      % endif
+
+      % if swrdaccess != SwRdAccess.NONE:
+    // to register interface (read)
+    .qs     (${finst_name}_qs)
+      % else:
+    .qs     ()
+      % endif
+  );
+    % endif  ## end non-constant prim_subreg
+  % endif
+</%def>\
+<%def name="we_gen(sig_name, msb, lsb, swrdaccess, swwraccess, hwext, idx)">\
+% if swwraccess != SwWrAccess.NONE:
+  % if swrdaccess != SwRdAccess.RC:
+  assign ${sig_name}_we = addr_hit[${idx}] && reg_we;
+  assign ${sig_name}_wd = reg_wdata[${str_bits_sv(msb,lsb)}];
+  % else:
+  ## Generate WE based on read request, read should clear
+  assign ${sig_name}_we = addr_hit[${idx}] && reg_re;
+  assign ${sig_name}_wd = '1;
+  % endif
+% endif
+% if swrdaccess != SwRdAccess.NONE and hwext:
+  assign ${sig_name}_re = addr_hit[${idx}] && reg_re;
+% endif
+</%def>\
+<%def name="rdata_gen(sig_name, msb, lsb, swrdaccess)">\
+% if swrdaccess != SwRdAccess.NONE:
+        reg_rdata_next[${str_bits_sv(msb,lsb)}] = ${sig_name}_qs;
+% else:
+        reg_rdata_next[${str_bits_sv(msb,lsb)}] = '0;
+% endif
+</%def>\