blob: 5de54eb78531054d76bc8357d015bea84983e4cc [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
<%!
from reggen import gen_dv
from reggen.access import HwAccess, SwRdAccess, SwWrAccess
from reggen.multi_register import MultiRegister
from reggen.register import Register
from typing import Dict
# Get a list reg and its instance name
# For single reg, return Dict[reg_inst:reg]
# For multireg, if it's dv_compact, return Dict[mr.name[idx]:mr.reg],
# if not, return all the mr.regs with their name
def get_inst_to_reg_dict(r) -> Dict:
inst_regs = {} # type: Dict[inst_name:Register]
if isinstance(r, MultiRegister):
if r.dv_compact:
inst_base = r.reg.name.lower()
for idx, reg in enumerate(r.regs):
inst_name = f'{inst_base}[{idx}]'
inst_regs[inst_name] = reg
else:
for r0 in r.regs:
inst_regs[r0.name] = r0
else:
inst_regs[r.name.lower()] = r
return inst_regs
%>\
##
##
## make_ral_pkg
## ============
##
## Generate the RAL package for a device interface.
##
## dv_base_names a DvBaseNames object that contains register base class names
##
## reg_width an integer giving the width of registers in bits
##
## reg_block_path the hierarchical path to the relevant register block in the
## design
##
## rb a RegBlock object
##
## esc_if_name a string giving the full, escaped, interface name. For
## a device interface called FOO on block BAR,
## this will be bar__foo. For an unnamed interface
## on block BAR, this will be just bar.
##
<%def name="make_ral_pkg(dv_base_names, reg_width, reg_block_path, rb, esc_if_name)">\
package ${esc_if_name}_ral_pkg;
${make_ral_pkg_hdr(dv_base_names.pkg, [])}
${make_ral_pkg_fwd_decls(esc_if_name, rb.type_regs, rb.windows)}
% for r in rb.all_regs:
<%
mr = None
if isinstance(r, MultiRegister):
mr = r
if r.dv_compact:
regs = [r.reg]
else:
regs = r.regs
else:
regs = [r]
%>\
% for idx, reg in enumerate(regs):
${make_ral_pkg_reg_class(dv_base_names.reg, dv_base_names.field, reg_width, esc_if_name,
reg_block_path, reg, mr, idx)}
% endfor
% endfor
% for window in rb.windows:
${make_ral_pkg_window_class(dv_base_names.mem, esc_if_name, window)}
% endfor
<%
reg_block_name = gen_dv.bcname(esc_if_name)
%>\
class ${reg_block_name} extends ${dv_base_names.block};
% if rb.flat_regs:
// registers
% for r in rb.all_regs:
<%
# If it's dv_compact, then create it as an array even when it only contains one item
count = 0
if isinstance(r, MultiRegister):
if r.dv_compact:
regs = [r.reg]
count = len(r.regs)
else:
regs = r.regs
else:
regs = [r]
%>\
% for r0 in regs:
<%
reg_type = gen_dv.rcname(esc_if_name, r0)
inst_name = r0.name.lower()
inst_decl = f'{inst_name}[{count}]' if count > 0 else inst_name
%>\
rand ${reg_type} ${inst_decl};
% if r0.alias_target is not None:
<%
alias_inst_name = r0.alias_target.lower()
alias_inst_decl = f'{alias_inst_name}[{count}]' if count > 0 else alias_inst_name
%>\
rand ${reg_type} ${alias_inst_decl}; // aliases to ${inst_decl}
% endif
% endfor
% endfor
% endif
% if rb.windows:
// memories
% for window in rb.windows:
rand ${gen_dv.mcname(esc_if_name, window)} ${gen_dv.miname(window)};
% endfor
% endif
`uvm_object_utils(${reg_block_name})
function new(string name = "${reg_block_name}",
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(${reg_width//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
% if rb.flat_regs:
set_hdl_path_root("tb.dut", "BkdrRegPathRtl");
set_hdl_path_root("tb.dut", "BkdrRegPathRtlShadow");
// create registers
% for r in rb.all_regs:
<%
r0 = r.reg if isinstance(r, MultiRegister) else r
reg_type = gen_dv.rcname(esc_if_name, r0)
%>\
% if isinstance(r, MultiRegister):
% for idx, reg in enumerate(r.regs):
<%
if r.dv_compact:
inst_base = r0.name.lower()
inst_name = f'{inst_base}[{idx}]'
if r0.alias_target is not None:
alias_inst_base = r0.alias_target.lower()
alias_inst_name = f'{alias_inst_base}[{idx}]'
else:
inst_name = reg.name.lower()
reg_type = gen_dv.rcname(esc_if_name, reg)
if r0.alias_target is not None:
alias_inst_name = reg.alias_target.lower()
%>\
${instantiate_register(reg_width, reg_block_path, reg, reg_type, inst_name)}\
% if reg.alias_target is not None:
// Assign alias register to generic handle.
${alias_inst_name} = ${inst_name};
% endif
% endfor
% else:
${instantiate_register(reg_width, reg_block_path, r, reg_type, r.name.lower())}\
% endif
% if r0.alias_target is not None:
<%
inst_name = r0.name.lower()
alias_inst_name = r0.alias_target.lower()
%>\
// Assign alias register to generic handle.
${alias_inst_name} = ${inst_name};
% endif
% endfor
<%
any_regwen = False
for r in rb.flat_regs:
if r.regwen:
any_regwen = True
break
%>\
% if any_regwen:
// assign locked reg to its regwen reg
% for r in rb.all_regs:
% for inst, reg in get_inst_to_reg_dict(r).items():
${apply_regwen(rb, reg, inst)}\
% endfor
% endfor
% endif
% endif
${make_ral_pkg_window_instances(reg_width, esc_if_name, rb)}
// Create functional coverage for comportable IP-specific specialized registers.
// This function can only be called if it is a root block to get the correct gating condition
// and avoid creating duplicated cov.
if (this.get_parent() == null && en_dv_reg_cov) create_cov();
endfunction : build
endclass : ${reg_block_name}
endpackage
</%def>\
##
##
## make_ral_pkg_hdr
## ================
##
## Generate the header for a RAL package
##
## dv_base_reg_pkg_name a string name for the base reg_pkg type
##
## deps a list of names for packages that should be explicitly
## imported
##
<%def name="make_ral_pkg_hdr(dv_base_reg_pkg_name, deps)">\
// dep packages
import uvm_pkg::*;
import dv_base_reg_pkg::*;
% if dv_base_reg_pkg_name != "dv_base_reg_pkg":
import ${dv_base_reg_pkg_name}::*;
% endif
% for dep in deps:
import ${dep}::*;
% endfor
// macro includes
`include "uvm_macros.svh"\
</%def>\
##
##
## make_ral_pkg_fwd_decls
## ======================
##
## Generate the forward declarations for a RAL package
##
## esc_if_name as for make_ral_pkg
##
## type_regs a list of Register objects, one for each type that
## should be defined. Each MultiRegister will contribute
## just one register to the list.
##
## windows a list of Window objects
##
<%def name="make_ral_pkg_fwd_decls(esc_if_name, type_regs, windows)">\
// Forward declare all register/memory/block classes
% for r in type_regs:
typedef class ${gen_dv.rcname(esc_if_name, r)};
% endfor
% for w in windows:
typedef class ${gen_dv.mcname(esc_if_name, w)};
% endfor
typedef class ${gen_dv.bcname(esc_if_name)};\
</%def>\
##
##
## make_ral_pkg_reg_class
## ======================
##
## Generate the classes for a register inside a RAL package
##
## dv_base_reg_name a string name for the base register type
##
## dv_base_field_name a string name for the base reg_field type
##
## reg_width as for make_ral_pkg
##
## esc_if_name as for make_ral_pkg
##
## reg_block_path as for make_ral_pkg
##
## reg a Register object
##
## mr a MultiRegister object if this reg is from a MultiRegister
##
## reg_idx the index location of this reg if this reg is from a MultiRegister,
## or zero if not
<%def name="make_ral_pkg_reg_class(dv_base_reg_name, dv_base_field_name, reg_width, esc_if_name,
reg_block_path, reg, mr, reg_idx)">\
<%
reg_name = reg.name.lower()
is_ext = reg.hwext
for field in reg.fields:
if (field.hwaccess.value[1] == HwAccess.NONE and
field.swaccess.swrd() == SwRdAccess.RD and
not field.swaccess.allows_write()):
is_ext = 1
class_name = gen_dv.rcname(esc_if_name, reg)
alias_class_name = gen_dv.alias_rcname(esc_if_name, reg)
%>\
class ${class_name} extends ${dv_base_reg_name};
// fields
<%
suffix = ""
start_idx = 0
add_style_waive = False
compact_field_inst_name = ""
compact_alias_field_inst_name = ""
if mr is None:
fields = reg.fields
else:
if not mr.compact:
fields = mr.reg.fields
else:
fields = mr.regs[reg_idx].fields
compact_field_inst_name = mr.reg.fields[0].name.lower()
compact_alias_field_inst_name = mr.reg.fields[0].alias_target
if mr.dv_compact:
# The dv_compact flag means that the fields of the multi-reg divide equally into registers.
# In this case, there's an array of registers and make_ral_pkg_reg_class() gets called once
# to define that array's type, using the fields of the first register in the replication.
assert reg_idx == 0
if len(fields) > 1:
suffix = f'[{len(fields)}]'
else:
# In this case, the multi-register is "compact", so there might be multiple copies of its
# single field in each generated register. But dv_compact is false, which probably means
# that the fields didn't divide equally into a whole number of registers. In this case, we
# are generating a different class for each output register and should spit out fields
# accordingly. Note that we generate an array, even if len(fields) = 1. If that happens, we
# know we're on the last generated register, so want to keep everything uniform.
num_fields_per_reg = 32 // fields[0].bits.width()
start_idx = num_fields_per_reg * reg_idx
end_idx = start_idx + len(fields) - 1
suffix = f'[{start_idx}:{end_idx}]'
if start_idx == 0:
add_style_waive = True
%>\
% if add_style_waive:
// verilog_lint: waive unpacked-dimensions-range-ordering
% endif
% if compact_field_inst_name:
rand ${dv_base_field_name} ${compact_field_inst_name}${suffix};
% if compact_alias_field_inst_name:
rand ${dv_base_field_name} ${compact_alias_field_inst_name.lower()}${suffix};
% endif
% else:
% for f in fields:
rand ${dv_base_field_name} ${f.name.lower()};
% if f.alias_target is not None:
rand ${dv_base_field_name} ${f.alias_target.lower()}; // aliases to ${f.name.lower()}
% endif
% endfor
% endif
`uvm_object_utils(${class_name})
function new(string name = "${class_name}",
int unsigned n_bits = ${reg_width},
int has_coverage = UVM_NO_COVERAGE);
super.new(name, n_bits, has_coverage);
endfunction : new
virtual function void build(csr_excl_item csr_excl = null);
% if alias_class_name is not None:
set_alias_name("${alias_class_name}");
% endif\
// create fields
% for idx, field in enumerate(fields):
<%
alias_reg_field_name = ""
if compact_field_inst_name:
reg_field_name = compact_field_inst_name
if compact_alias_field_inst_name:
alias_reg_field_name = compact_alias_field_inst_name
# If len(fields) > 1, we define generated reg fields as an array and we need an index to
# refer to the field object
# If start_idx > 0, the fields cross more than one register, we define an array for the
# fields even when the last register only contains one field.
if len(fields) > 1 or start_idx > 0:
reg_field_name = reg_field_name + f'[{idx + start_idx}]'
if compact_alias_field_inst_name:
alias_reg_field_name = alias_reg_field_name + f'[{idx + start_idx}]'
else:
reg_field_name = field.name.lower()
if field.alias_target is not None:
alias_reg_field_name = field.alias_target.lower()
%>\
${_create_reg_field(dv_base_field_name, reg_width, reg_block_path, reg.shadowed, reg.hwext,
reg_field_name, field)}\
% if field.alias_target is not None:
${alias_reg_field_name} = ${reg_field_name}; // assign to generic handle
% endif
% endfor
% if is_ext:
set_is_ext_reg(1);
% endif
endfunction : build
endclass : ${class_name}\
</%def>\
##
##
## _create_reg_field
## =================
##
## Generate the code that creates a uvm_reg_field object for a field
## in a register.
##
## dv_base_reg_field_name as for make_ral_pkg_reg_class
##
## reg_width as for make_ral_pkg
##
## reg_block_path as for make_ral_pkg
##
## shadowed true if the field's register is shadowed
##
## hwext true if the field's register is hwext
##
## reg_field_name a string with the name to give the field in the HDL
##
## field a Field object
<%def name="_create_reg_field(dv_base_reg_field_name, reg_width, reg_block_path, shadowed, hwext,
reg_field_name, field)">\
<%
field_size = field.bits.width()
field_access = field.swaccess.dv_rights()
if not field.hwaccess.allows_write():
field_volatile = 0
else:
field_volatile = 1
field_tags = field.tags
fname = reg_field_name
type_id_indent = ' ' * (len(fname) + 4)
%>\
${fname} = (${dv_base_reg_field_name}::
${type_id_indent}type_id::create("${field.name.lower()}"));
${fname}.configure(
.parent(this),
.size(${field_size}),
.lsb_pos(${field.bits.lsb}),
.access("${field_access}"),
.volatile(${field_volatile}),
.reset(${reg_width}'h${format(field.resval or 0, 'x')}),
.has_reset(1),
.is_rand(1),
.individually_accessible(1));
% if field.alias_target is not None:
${fname}.set_alias_name("${field.alias_target.lower()}");
% endif
${fname}.set_original_access("${field_access}");
% if field.mubi:
${fname}.set_mubi_width(${field_size});
% endif
% if field_tags:
// create field tags
% for field_tag in field_tags:
<%
tag = field_tag.split(":")
%>\
% if tag[0] == "excl":
csr_excl.add_excl(${fname}.get_full_name(), ${tag[2]}, ${tag[1]});
% endif
% endfor
% endif
</%def>\
##
##
## make_ral_pkg_window_class
## =========================
##
## Generate the classes for a window inside a RAL package
##
## dv_base_window_name a string name for the base windoe type
##
## esc_if_name as for make_ral_pkg
##
## window a Window object
<%def name="make_ral_pkg_window_class(dv_base_window_name, esc_if_name, window)">\
<%
mem_name = window.name.lower()
mem_right = window.swaccess.dv_rights()
mem_n_bits = window.validbits
mem_size = window.items
class_name = gen_dv.mcname(esc_if_name, window)
%>\
class ${class_name} extends ${dv_base_window_name};
`uvm_object_utils(${class_name})
function new(string name = "${class_name}",
longint unsigned size = ${mem_size},
int unsigned n_bits = ${mem_n_bits},
string access = "${mem_right}",
int has_coverage = UVM_NO_COVERAGE);
super.new(name, size, n_bits, access, has_coverage);
% if window.byte_write:
set_mem_partial_write_support(1);
% endif
% if window.data_intg_passthru:
set_data_intg_passthru(1);
% endif
endfunction : new
endclass : ${class_name}
</%def>\
##
##
## make_ral_pkg_window_instances
## =============================
##
## Generate the classes for a window inside a RAL package
##
## reg_width as for make_ral_pkg
##
## esc_if_name as for make_ral_pkg
##
## rb a RegBlock object
##
<%def name="make_ral_pkg_window_instances(reg_width, esc_if_name, rb)">\
% if rb.windows:
// create memories
% for w in rb.windows:
<%
mem_name = w.name.lower()
mem_right = w.swaccess.dv_rights()
mem_offset = "{}'h{:x}".format(reg_width, w.offset)
mem_n_bits = w.validbits
mem_size = w.items
%>\
${mem_name} =
${gen_dv.mcname(esc_if_name, w)}::type_id::create("${mem_name}");
${mem_name}.configure(.parent(this));
default_map.add_mem(.mem(${mem_name}),
.offset(${mem_offset}),
.rights("${mem_right}"));
% endfor
% endif
</%def>\
##
##
## instantiate_register
## ====================
##
## Actually instantiate a register in a register block
##
## reg_width an integer giving the width of registers in bits
##
## reg_block_path as for make_ral_pkg
##
## reg the Register to instantiate
##
## reg_type a string giving the type name (a subclass of
## uvm_register) to instantiate.
##
## reg_inst a string giving the field of the uvm_reg_block that
## should be set to this new register. For single
## registers, this will just be the register name. For
## elements of multi-registers, it will be the name of an
## array item.
##
<%def name="instantiate_register(reg_width, reg_block_path, reg, reg_type, reg_inst)">\
<%
reg_name = reg.name.lower()
reg_offset = "{}'h{:x}".format(reg_width, reg.offset)
inst_id_indent = ' ' * (len(reg_inst) + 4)
%>\
${reg_inst} = (${reg_type}::
${inst_id_indent}type_id::create("${reg_name}"));
${reg_inst}.configure(.blk_parent(this));
${reg_inst}.build(csr_excl);
default_map.add_reg(.rg(${reg_inst}),
.offset(${reg_offset}));
% if reg.shadowed:
% if reg.update_err_alert:
${reg_inst}.add_update_err_alert("${reg.update_err_alert}");
% endif
% if reg.storage_err_alert:
${reg_inst}.add_storage_err_alert("${reg.storage_err_alert}");
% endif
% if reg.hwext:
% for field in reg.fields:
<%
shadowed_reg_path = ''
for tag in field.tags:
parts = tag.split(':')
if parts[0] == 'shadowed_reg_path':
shadowed_reg_path = parts[1]
if not shadowed_reg_path:
print("ERROR: ext shadow_reg does not have tags for shadowed_reg_path for each field!")
assert 0
%>\
${reg_inst}.add_hdl_path_slice(
"${shadowed_reg_path}.committed_reg.q",
${field.bits.lsb}, ${field.bits.width()}, 0, "BkdrRegPathRtl");
${reg_inst}.add_hdl_path_slice(
"${shadowed_reg_path}.shadow_reg.q",
${field.bits.lsb}, ${field.bits.width()}, 0, "BkdrRegPathRtlShadow");
% endfor
% endif
% endif
% for field in reg.fields:
<%
field_size = field.bits.width()
if len(reg.fields) == 1:
reg_field_name = reg_name
else:
reg_field_name = reg_name + "_" + field.name.lower()
##if reg.async_name and not reg.hwext:
## reg_field_name += ".u_subreg"
%>\
% if ((field.hwaccess.value[1] == HwAccess.NONE and\
field.swaccess.swrd() == SwRdAccess.RD and\
not field.swaccess.allows_write())):
// constant reg
${reg_inst}.add_hdl_path_slice(
"${reg_block_path}.${reg_field_name}_qs",
${field.bits.lsb}, ${field_size}, 0, "BkdrRegPathRtl");
% elif not reg.shadowed:
${reg_inst}.add_hdl_path_slice(
"${reg_block_path}.u_${reg_field_name}.q${"s" if reg.hwext else ""}",
${field.bits.lsb}, ${field_size}, 0, "BkdrRegPathRtl");
% endif
% if reg.shadowed and not reg.hwext:
${reg_inst}.add_hdl_path_slice(
"${reg_block_path}.u_${reg_field_name}.committed_reg.q",
${field.bits.lsb}, ${field_size}, 0, "BkdrRegPathRtl");
${reg_inst}.add_hdl_path_slice(
"${reg_block_path}.u_${reg_field_name}.shadow_reg.q",
${field.bits.lsb}, ${field_size}, 0, "BkdrRegPathRtlShadow");
% endif
% endfor
% if reg.shadowed:
${reg_inst}.set_is_shadowed();
% endif
% if reg.tags:
// create register tags
% for reg_tag in reg.tags:
<%
tag = reg_tag.split(":")
%>\
% if tag[0] == "excl":
csr_excl.add_excl(${reg_inst}.get_full_name(), ${tag[2]}, ${tag[1]});
% endif
% endfor
% endif
</%def>\
##
##
## apply_regwen
## ============
##
## Apply a regwen to a register
##
## rb the register block
##
## reg the Register that needs apply regwens
##
## reg_inst a string giving the field of the uvm_reg_block that
## should be updated. For single registers, this will just
## be the register name. For elements of multi-registers,
## it will be the name of an array item.
##
<%def name="apply_regwen(rb, reg, reg_inst)">\
% if reg.regwen is None:
<% return "" %>\
% endif
% for wen in rb.all_regs:
% for wen_inst, wen_reg in get_inst_to_reg_dict(wen).items():
% if reg.regwen.lower() == wen_reg.name.lower():
${wen_inst}.add_lockable_reg_or_fld(${reg_inst});
<% return "" %>\
% elif wen_reg.name.lower() in reg.regwen.lower():
% for field in wen_reg.get_field_list():
% if reg.regwen.lower() == (wen_reg.name.lower() + "_" + field.name.lower()):
${wen_inst}.${field.name.lower()}.add_lockable_reg_or_fld(${reg_inst});
<% return "" %>\
% endif
% endfor
% endif
% endfor
% endfor
</%def>\