tinyriscv/tools/regtool/reggen/uvm_reg_base.sv.tpl

565 lines
16 KiB
Smarty

// 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}]' if len(r.regs) > 1 else inst_base
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_prefix a string naming the base register type. If it is FOO,
## then we will inherit from FOO_reg (assumed to
## be a subclass of uvm_reg).
##
## 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_prefix, reg_width, reg_block_path, rb, esc_if_name)">\
package ${esc_if_name}_ral_pkg;
${make_ral_pkg_hdr(dv_base_prefix, [])}
${make_ral_pkg_fwd_decls(esc_if_name, rb.type_regs, rb.windows)}
% for r in rb.all_regs:
<%
if isinstance(r, MultiRegister):
reg = r.reg
if r.dv_compact:
reg.fields = r.regs[0].fields
regs = [reg]
else:
regs = r.regs
else:
regs = [r]
%>\
% for reg in regs:
${make_ral_pkg_reg_class(dv_base_prefix, reg_width, esc_if_name, reg_block_path, reg)}
% endfor
% endfor
% for window in rb.windows:
${make_ral_pkg_window_class(dv_base_prefix, esc_if_name, window)}
% endfor
<%
reg_block_name = gen_dv.bcname(esc_if_name)
%>\
class ${reg_block_name} extends ${dv_base_prefix}_reg_block;
% if rb.flat_regs:
// registers
% for r in rb.all_regs:
<%
if isinstance(r, MultiRegister):
if r.dv_compact:
regs = [r.reg]
count = len(r.regs)
else:
regs = r.regs
count = 1
else:
regs = [r]
count = 1
%>\
% 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 > 1 else inst_name
%>\
rand ${reg_type} ${inst_decl};
% 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", "BkdrRegPathRtlCommitted");
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 len(r.regs) > 1 else inst_base
else:
inst_name = reg.name.lower()
reg_type = gen_dv.rcname(esc_if_name, reg)
%>\
${instantiate_register(reg_width, reg_block_path, reg, reg_type, inst_name)}\
% endfor
% else:
${instantiate_register(reg_width, reg_block_path, r, reg_type, r.name.lower())}\
% 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)}
endfunction : build
endclass : ${reg_block_name}
endpackage
</%def>\
##
##
## make_ral_pkg_hdr
## ================
##
## Generate the header for a RAL package
##
## dv_base_prefix as for make_ral_pkg
##
## deps a list of names for packages that should be explicitly
## imported
##
<%def name="make_ral_pkg_hdr(dv_base_prefix, deps)">\
// dep packages
import uvm_pkg::*;
import dv_base_reg_pkg::*;
% if dv_base_prefix != "dv_base":
import ${dv_base_prefix}_reg_pkg::*;
% 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_prefix as for make_ral_pkg
##
## 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 or MultiRegister object
<%def name="make_ral_pkg_reg_class(dv_base_prefix, reg_width, esc_if_name, reg_block_path, reg)">\
<%
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)
%>\
class ${class_name} extends ${dv_base_prefix}_reg;
// fields
% for f in reg.fields:
rand ${dv_base_prefix}_reg_field ${f.name.lower()};
% endfor
`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);
// create fields
% for field in reg.fields:
<%
if len(reg.fields) == 1:
reg_field_name = reg_name
else:
reg_field_name = reg_name + "_" + field.name.lower()
%>\
${_create_reg_field(dv_base_prefix, reg_width, reg_block_path, reg.shadowed, reg.hwext, reg_field_name, field)}
% 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_prefix as for make_ral_pkg
##
## 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_prefix, 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 = field.name.lower()
type_id_indent = ' ' * (len(fname) + 4)
%>\
${fname} = (${dv_base_prefix}_reg_field::
${type_id_indent}type_id::create("${fname}"));
${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));
${fname}.set_original_access("${field_access}");
% if field_tags:
// create field tags
% for field_tag in field_tags:
<%
tag = field_tag.split(":")
%>\
% if tag[0] == "excl":
csr_excl.add_excl(${field.name.lower()}.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_prefix as for make_ral_pkg
##
## esc_if_name as for make_ral_pkg
##
## window a Window object
<%def name="make_ral_pkg_window_class(dv_base_prefix, 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_prefix}_mem;
`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
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 and reg.hwext:
<%
shadowed_reg_path = ''
for tag in reg.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!")
assert 0
bit_idx = reg.fields[-1].bits.msb + 1
%>\
${reg_inst}.add_update_err_alert("${reg.update_err_alert}");
${reg_inst}.add_storage_err_alert("${reg.storage_err_alert}");
${reg_inst}.add_hdl_path_slice(
"${shadowed_reg_path}.committed_reg.q",
0, ${bit_idx}, 0, "BkdrRegPathRtlCommitted");
${reg_inst}.add_hdl_path_slice(
"${shadowed_reg_path}.shadow_reg.q",
0, ${bit_idx}, 0, "BkdrRegPathRtlShadow");
% 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 ((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");
% else:
${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 shadowed and not hwext:
${reg_inst}.add_hdl_path_slice(
"${reg_block_path}.u_${reg_field_name}.committed_reg.q",
${field.bits.lsb}, ${field_size}, 0, "BkdrRegPathRtlCommitted");
${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>\