// 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
<%
  from topgen import lib # TODO: Split lib to common lib module

  from reggen.access import HwAccess, SwRdAccess, SwWrAccess
  from reggen.register import Register
  from reggen.multi_register import MultiRegister

  from reggen import gen_rtl

  localparams = block.params.get_localparams()

  addr_widths = gen_rtl.get_addr_widths(block)

  lblock = block.name.lower()
  ublock = lblock.upper()

  def reg_pfx(reg):
    return '{}_{}'.format(ublock, reg.name.upper())

  def reg_resname(reg):
    return '{}_RESVAL'.format(reg_pfx(reg))

  def field_resname(reg, field):
    return '{}_{}_RESVAL'.format(reg_pfx(reg), field.name.upper())

%>\
<%def name="typedefs_for_iface(iface_name, iface_desc, for_iface, rb)">\
<%
   hdr = gen_rtl.make_box_quote('Typedefs for registers' + for_iface)
%>\
% for r in rb.all_regs:
  % if r.get_n_bits(["q"]):
    % if hdr:

${hdr}
    % endif
<%
    r0 = gen_rtl.get_r0(r)
    hdr = None
%>\

  typedef struct packed {
    % if r.is_homogeneous():
      ## If we have a homogeneous register or multireg, there is just one field
      ## (possibly replicated many times). The typedef is for one copy of that
      ## field.
<%
      field = r.get_field_list()[0]
      field_q_width = field.get_n_bits(r0.hwext, ['q'])
      field_q_bits = lib.bitarray(field_q_width, 2)
%>\
    logic ${field_q_bits} q;
      % if field.hwqe:
    logic        qe;
      % endif
      % if field.hwre or (r0.shadowed and r0.hwext):
    logic        re;
      % endif
      % if r0.shadowed and not r0.hwext:
    logic        err_update;
    logic        err_storage;
      % endif
    % else:
      ## We are inhomogeneous, which means there is more than one different
      ## field. Generate a reg2hw typedef that packs together all the fields of
      ## the register.
      % for f in r0.fields:
        % if f.get_n_bits(r0.hwext, ["q"]) >= 1:
<%
          field_q_width = f.get_n_bits(r0.hwext, ['q'])
          field_q_bits = lib.bitarray(field_q_width, 2)

          struct_name = f.name.lower()
%>\
    struct packed {
      logic ${field_q_bits} q;
          % if f.hwqe:
      logic        qe;
          % endif
          % if f.hwre or (r0.shadowed and r0.hwext):
      logic        re;
          % endif
          % if r0.shadowed and not r0.hwext:
      logic        err_update;
      logic        err_storage;
          % endif
    } ${struct_name};
        %endif
      %endfor
    %endif
  } ${gen_rtl.get_reg_tx_type(block, r, False)};
  %endif
% endfor
% for r in rb.all_regs:
  % if r.get_n_bits(["d"]):
    % if hdr:

${hdr}
    % endif
<%
    r0 = gen_rtl.get_r0(r)
    hdr = None
%>\

  typedef struct packed {
    % if r.is_homogeneous():
      ## If we have a homogeneous register or multireg, there is just one field
      ## (possibly replicated many times). The typedef is for one copy of that
      ## field.
<%
      field = r.get_field_list()[0]
      field_d_width = field.get_n_bits(r0.hwext, ['d'])
      field_d_bits = lib.bitarray(field_d_width, 2)
%>\
    logic ${field_d_bits} d;
      % if not r0.hwext:
    logic        de;
      % endif
    % else:
      ## We are inhomogeneous, which means there is more than one different
      ## field. Generate a hw2reg typedef that packs together all the fields of
      ## the register.
      % for f in r0.fields:
        % if f.get_n_bits(r0.hwext, ["d"]) >= 1:
<%
          field_d_width = f.get_n_bits(r0.hwext, ['d'])
          field_d_bits = lib.bitarray(field_d_width, 2)

          struct_name = f.name.lower()
%>\
    struct packed {
      logic ${field_d_bits} d;
          % if not r0.hwext:
      logic        de;
          % endif
    } ${struct_name};
        %endif
      %endfor
    %endif
  } ${gen_rtl.get_reg_tx_type(block, r, True)};
  % endif
% endfor
</%def>\
<%def name="reg2hw_for_iface(iface_name, iface_desc, for_iface, rb)">\
<%
nbits = rb.get_n_bits(["q", "qe", "re"])
packbit = 0
%>\
% if nbits > 0:

  // Register -> HW type${for_iface}
  typedef struct packed {
% for r in rb.all_regs:
  % if r.get_n_bits(["q"]):
<%
    r0 = gen_rtl.get_r0(r)
    struct_type = gen_rtl.get_reg_tx_type(block, r, False)
    struct_width = r0.get_n_bits(['q', 'qe', 're'])

    if isinstance(r, MultiRegister):
      struct_type += " [{}:0]".format(r.count - 1)
      struct_width *= r.count

    msb = nbits - packbit - 1
    lsb = msb - struct_width + 1
    packbit += struct_width
%>\
    ${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}]
  % endif
% endfor
  } ${gen_rtl.get_iface_tx_type(block, iface_name, False)};
% endif
</%def>\
<%def name="hw2reg_for_iface(iface_name, iface_desc, for_iface, rb)">\
<%
nbits = rb.get_n_bits(["d", "de"])
packbit = 0
%>\
% if nbits > 0:

  // HW -> register type${for_iface}
  typedef struct packed {
% for r in rb.all_regs:
  % if r.get_n_bits(["d"]):
<%
    r0 = gen_rtl.get_r0(r)
    struct_type = gen_rtl.get_reg_tx_type(block, r, True)
    struct_width = r0.get_n_bits(['d', 'de'])

    if isinstance(r, MultiRegister):
      struct_type += " [{}:0]".format(r.count - 1)
      struct_width *= r.count

    msb = nbits - packbit - 1
    lsb = msb - struct_width + 1
    packbit += struct_width
%>\
    ${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}]
  % endif
% endfor
  } ${gen_rtl.get_iface_tx_type(block, iface_name, True)};
% endif
</%def>\
<%def name="offsets_for_iface(iface_name, iface_desc, for_iface, rb)">\
% if not rb.flat_regs:
<% return STOP_RENDERING %>
% endif

  // Register offsets${for_iface}
<%
aw_name, aw = addr_widths[iface_name]
%>\
% for r in rb.flat_regs:
<%
value = "{}'h {:x}".format(aw, r.offset)
%>\
  parameter logic [${aw_name}-1:0] ${reg_pfx(r)}_OFFSET = ${value};
% endfor
</%def>\
<%def name="hwext_resvals_for_iface(iface_name, iface_desc, for_iface, rb)">\
<%
  hwext_regs = [r for r in rb.flat_regs if r.hwext]
%>\
% if hwext_regs:

  // Reset values for hwext registers and their fields${for_iface}
  % for reg in hwext_regs:
<%
    reg_width = reg.get_width()
    reg_msb = reg_width - 1
    reg_resval = "{}'h {:x}".format(reg_width, reg.resval)
%>\
  parameter logic [${reg_msb}:0] ${reg_resname(reg)} = ${reg_resval};
    % for field in reg.fields:
      % if field.resval is not None:
<%
    field_width = field.bits.width()
    field_msb = field_width - 1
    field_resval = "{}'h {:x}".format(field_width, field.resval)
%>\
  parameter logic [${field_msb}:0] ${field_resname(reg, field)} = ${field_resval};
      % endif
    % endfor
  % endfor
% endif
</%def>\
<%def name="windows_for_iface(iface_name, iface_desc, for_iface, rb)">\
% if rb.windows:
<%
  aw_name, aw = addr_widths[iface_name]
%>\

  // Window parameters${for_iface}
% for i,w in enumerate(rb.windows):
<%
    win_pfx = '{}_{}'.format(ublock, w.name.upper())
    base_txt_val = "{}'h {:x}".format(aw, w.offset)
    size_txt_val = "'h {:x}".format(w.size_in_bytes)

    offset_type = 'logic [{}-1:0]'.format(aw_name)
    size_type = 'int unsigned'
    max_type_len = max(len(offset_type), len(size_type))

    offset_type += ' ' * (max_type_len - len(offset_type))
    size_type += ' ' * (max_type_len - len(size_type))

%>\
  parameter ${offset_type} ${win_pfx}_OFFSET = ${base_txt_val};
  parameter ${size_type} ${win_pfx}_SIZE   = ${size_txt_val};
% endfor
% endif
</%def>\
<%def name="reg_data_for_iface(iface_name, iface_desc, for_iface, rb)">\
% if rb.flat_regs:
<%
  lpfx = gen_rtl.get_type_name_pfx(block, iface_name)
  upfx = lpfx.upper()
  idx_len = len("{}".format(len(rb.flat_regs) - 1))
%>\

  // Register index${for_iface}
  typedef enum int {
% for r in rb.flat_regs:
    ${ublock}_${r.name.upper()}${"" if loop.last else ","}
% endfor
  } ${lpfx}_id_e;

  // Register width information to check illegal writes${for_iface}
  parameter logic [3:0] ${upfx}_PERMIT [${len(rb.flat_regs)}] = '{
  % for i, r in enumerate(rb.flat_regs):
<%
  index_str = "{}".format(i).rjust(idx_len)
  width = r.get_width()
  if width > 24:
    mask = '1111'
  elif width > 16:
    mask = '0111'
  elif width > 8:
    mask = '0011'
  else:
    mask = '0001'

  comma = ',' if i < len(rb.flat_regs) - 1 else ' '
%>\
    4'b ${mask}${comma} // index[${index_str}] ${ublock}_${r.name.upper()}
  % endfor
  };
% endif
</%def>\

package ${lblock}_reg_pkg;
% if localparams:

  // Param list
% for param in localparams:
  parameter ${param.param_type} ${param.name} = ${param.value};
% endfor
% endif

  // Address widths within the block
% for param_name, width in addr_widths.values():
  parameter int ${param_name} = ${width};
% endfor
<%
  just_default = len(block.reg_blocks) == 1 and None in block.reg_blocks
%>\
% for iface_name, rb in block.reg_blocks.items():
<%
  iface_desc = iface_name or 'default'
  for_iface = '' if just_default else ' for {} interface'.format(iface_desc)
%>\
${typedefs_for_iface(iface_name, iface_desc, for_iface, rb)}\
${reg2hw_for_iface(iface_name, iface_desc, for_iface, rb)}\
${hw2reg_for_iface(iface_name, iface_desc, for_iface, rb)}\
${offsets_for_iface(iface_name, iface_desc, for_iface, rb)}\
${hwext_resvals_for_iface(iface_name, iface_desc, for_iface, rb)}\
${windows_for_iface(iface_name, iface_desc, for_iface, rb)}\
${reg_data_for_iface(iface_name, iface_desc, for_iface, rb)}\
% endfor

endpackage

