// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// UVM registers auto-generated by `reggen` containing UVM definitions for the entire top-level
<%!
  from topgen.gen_dv import sv_base_addr
  from reggen.gen_dv import bcname, mcname, miname
%>
##
## This template is used for chip-wide tests. It expects to be run with the
## following arguments
##
##    top              a Top object
##
##    dv_base_prefix   a string for the base register type. If it is FOO, then
##                     we will inherit from FOO_reg (assumed to be a subclass
##                     of uvm_reg).
##
## Like uvm_reg.sv.tpl, we use functions from uvm_reg_base.sv.tpl to define
## per-device-interface code.
##
<%namespace file="uvm_reg_base.sv.tpl" import="*"/>\
##
##
## Waive the package-filename check: we're going to be defining all sorts of
## packages in a single file.

// verilog_lint: waive-start package-filename
##
## Iterate over the device interfaces of blocks in Top, constructing a package
## for each. Sorting items like this guarantees we'll work alphabetically in
## block name.
% for block_name, block in sorted(top.blocks.items()):
%   for if_name, rb in block.reg_blocks.items():
<%
      if_suffix = '' if if_name is None else '_' + if_name
      esc_if_name = block_name.lower() + if_suffix
      if_desc = '' if if_name is None else '; interface {}'.format(if_name)
      reg_block_path = 'u_reg' + if_suffix
      reg_block_path = reg_block_path if block.hier_path is None else block.hier_path + "." + reg_block_path
%>\
// Block: ${block_name.lower()}${if_desc}
${make_ral_pkg(dv_base_prefix, top.regwidth, reg_block_path, rb, esc_if_name)}
%   endfor
% endfor
##
##
## Now that we've made the block-level packages, re-instate the
## package-filename check. The only package left is chip_ral_pkg, which should
## match the generated filename.

// verilog_lint: waive-start package-filename

// Block: chip
package chip_ral_pkg;
<%
  if_packages = []
  for block_name, block in sorted(top.blocks.items()):
    for if_name in block.reg_blocks:
      if_suffix = '' if if_name is None else '_' + if_name
      if_packages.append('{}{}_ral_pkg'.format(block_name.lower(), if_suffix))

  windows = top.window_block.windows
%>\
${make_ral_pkg_hdr(dv_base_prefix, if_packages)}
${make_ral_pkg_fwd_decls('chip', [], windows)}
% for window in windows:

${make_ral_pkg_window_class(dv_base_prefix, 'chip', window)}
% endfor

  class chip_reg_block extends ${dv_base_prefix}_reg_block;
    // sub blocks
% for block_name, block in sorted(top.blocks.items()):
%   for inst_name in top.block_instances[block_name.lower()]:
%     for if_name, rb in block.reg_blocks.items():
<%
        if_suffix = '' if if_name is None else '_' + if_name
        esc_if_name = block_name.lower() + if_suffix
        if_inst = inst_name + if_suffix
%>\
    rand ${bcname(esc_if_name)} ${if_inst};
%     endfor
%   endfor
% endfor
% if windows:
    // memories
%   for window in windows:
    rand ${mcname('chip', window)} ${miname(window)};
%   endfor
% endif

    `uvm_object_utils(chip_reg_block)

    function new(string name = "chip_reg_block",
                 int    has_coverage = UVM_NO_COVERAGE);
      super.new(name, has_coverage);
    endfunction : new

    virtual function void build(uvm_reg_addr_t base_addr,
                                csr_excl_item csr_excl = null);
      // create default map
      this.default_map = create_map(.name("default_map"),
                                    .base_addr(base_addr),
                                    .n_bytes(${top.regwidth//8}),
                                    .endian(UVM_LITTLE_ENDIAN));
      if (csr_excl == null) begin
        csr_excl = csr_excl_item::type_id::create("csr_excl");
        this.csr_excl = csr_excl;
      end

      // create sub blocks and add their maps
% for block_name, block in sorted(top.blocks.items()):
%   for inst_name in top.block_instances[block_name.lower()]:
%     for if_name, rb in block.reg_blocks.items():
<%
        if_suffix = '' if if_name is None else '_' + if_name
        esc_if_name = block_name.lower() + if_suffix
        if_inst = inst_name + if_suffix

        if top.attrs.get(inst_name) == 'reggen_only':
          hdl_path = 'tb.dut.u_' + inst_name
        else:
          hdl_path = 'tb.dut.top_earlgrey.u_' + inst_name
        qual_if_name = (inst_name, if_name)
        base_addr = top.if_addrs[qual_if_name]
        base_addr_txt = sv_base_addr(top, qual_if_name)

        hpr_indent = (len(if_inst) + len('.set_hdl_path_root(')) * ' '
%>\
      ${if_inst} = ${bcname(esc_if_name)}::type_id::create("${if_inst}");
      ${if_inst}.configure(.parent(this));
      ${if_inst}.build(.base_addr(base_addr + ${base_addr_txt}), .csr_excl(csr_excl));
      ${if_inst}.set_hdl_path_root("${hdl_path}",
      ${hpr_indent}"BkdrRegPathRtl");
      ${if_inst}.set_hdl_path_root("${hdl_path}",
      ${hpr_indent}"BkdrRegPathRtlCommitted");
      ${if_inst}.set_hdl_path_root("${hdl_path}",
      ${hpr_indent}"BkdrRegPathRtlShadow");
      default_map.add_submap(.child_map(${if_inst}.default_map),
                             .offset(base_addr + ${base_addr_txt}));
%     endfor
%   endfor
% endfor
${make_ral_pkg_window_instances(top.regwidth, 'chip', top.window_block)}

    endfunction : build
  endclass : chip_reg_block

endpackage
