| # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 | '''Generate DV code for an IP block''' | 
 |  | 
 | import logging as log | 
 | import os | 
 | import sys | 
 | from collections import defaultdict | 
 | from typing import Dict, List, Union, Optional | 
 |  | 
 | import yaml | 
 |  | 
 | from mako import exceptions  # type: ignore | 
 | from mako.lookup import TemplateLookup  # type: ignore | 
 | from pkg_resources import resource_filename | 
 |  | 
 | from reggen.ip_block import IpBlock | 
 | from reggen.multi_register import MultiRegister | 
 | from reggen.register import Register | 
 | from reggen.window import Window | 
 |  | 
 |  | 
 | class DvBaseNames: | 
 |     # Class global attributes | 
 |     valid_types = ["pkg", "block", "reg", "field", "mem", "all"] | 
 |  | 
 |     def __init__(self) -> None: | 
 |         self.with_prefix("dv_base") | 
 |  | 
 |     def with_prefix(self, prefix: str) -> None: | 
 |         self.pkg = prefix + "_reg_pkg" | 
 |         self.block = prefix + "_reg_block" | 
 |         self.reg = prefix + "_reg" | 
 |         self.field = prefix + "_reg_field" | 
 |         self.mem = prefix + "_mem" | 
 |  | 
 |     def set_entity(self, base_type: str, entity: str) -> None: | 
 |         assert base_type in self.valid_types, f"Invalid argument type: {base_type}" | 
 |         if base_type == "all": | 
 |             self.with_prefix(entity) | 
 |         else: | 
 |             setattr(self, base_type, entity) | 
 |  | 
 |  | 
 | def bcname(esc_if_name: str) -> str: | 
 |     '''Get the name of the dv_base_reg_block subclass for this device interface''' | 
 |     return esc_if_name + "_reg_block" | 
 |  | 
 |  | 
 | def rcname(esc_if_name: str, r: Union[Register, MultiRegister]) -> str: | 
 |     '''Get the name of the dv_base_reg subclass for this register''' | 
 |     return '{}_reg_{}'.format(esc_if_name, r.name.lower()) | 
 |  | 
 |  | 
 | def alias_rcname(esc_if_name: str, | 
 |                  r: Union[Register, MultiRegister]) -> Optional[str]: | 
 |     '''Get the name of the dv_base_reg subclass for this alias register''' | 
 |     if r.alias_target is not None: | 
 |         return '{}_reg_{}'.format(esc_if_name, r.alias_target.lower()) | 
 |     else: | 
 |         return None | 
 |  | 
 |  | 
 | def mcname(esc_if_name: str, m: Window) -> str: | 
 |     '''Get the name of the dv_base_mem subclass for this memory''' | 
 |     return '{}_mem_{}'.format(esc_if_name, m.name.lower()) | 
 |  | 
 |  | 
 | def miname(m: Window) -> str: | 
 |     '''Get the lower-case name of a memory block''' | 
 |     return m.name.lower() | 
 |  | 
 |  | 
 | def gen_core_file(outdir: str, | 
 |                   lblock: str, | 
 |                   dv_base_names: List[str], | 
 |                   paths: List[str]) -> None: | 
 |     depends = ["lowrisc:dv:dv_base_reg"] | 
 |     blocks_base_names = get_dv_base_names_objects(dv_base_names) | 
 |  | 
 |     if blocks_base_names is not None: | 
 |         # Assume the core file naming convetion is the package name without `_pkg` | 
 |         # suffix. | 
 |         for block in blocks_base_names: | 
 |             pkg_name = blocks_base_names[block].pkg | 
 |             depends.append("lowrisc:dv:{}".format(pkg_name[:-4])) | 
 |  | 
 |     # Generate a fusesoc core file that points at the files we've just | 
 |     # generated. | 
 |     core_data = { | 
 |         'name': "lowrisc:dv:{}_ral_pkg".format(lblock), | 
 |         'filesets': { | 
 |             'files_dv': { | 
 |                 'depend': depends, | 
 |                 'files': paths, | 
 |                 'file_type': 'systemVerilogSource' | 
 |             }, | 
 |         }, | 
 |         'targets': { | 
 |             'default': { | 
 |                 'filesets': [ | 
 |                     'files_dv', | 
 |                 ], | 
 |             }, | 
 |         }, | 
 |     } | 
 |     core_file_path = os.path.join(outdir, lblock + '_ral_pkg.core') | 
 |     with open(core_file_path, 'w') as core_file: | 
 |         core_file.write('CAPI=2:\n') | 
 |         yaml.dump(core_data, core_file, encoding='utf-8') | 
 |  | 
 |  | 
 | def get_dv_base_names_objects(dv_base_names: List[str]) -> Dict[str, DvBaseNames]: | 
 |     '''Returns a dictionary mapping a `DvBaseNames` object to a block. | 
 |  | 
 |     `dv_bave_names` is a list of base class entity names provided on the command-line, in the | 
 |     following format: | 
 |     ast:block:ast_base_reg_block ast:pkg:ast_base_reg_pkg otp_ctrl:all:otp_ctrl_base | 
 |  | 
 |     This function creates a dictionary that wraps the provided base class overrides for each block | 
 |     within a `DvBaseNames` object and returns a dictionary mapping the object to the block. | 
 |     ''' | 
 |     if dv_base_names is None: | 
 |         return None | 
 |  | 
 |     dv_base_names_dict = defaultdict(DvBaseNames)  # type: Dict[str, DvBaseNames] | 
 |     for item in dv_base_names: | 
 |         try: | 
 |             block, base_type, entity = item.split(":") | 
 |         except ValueError: | 
 |             log.error(f"Bad input arg: {item}") | 
 |             sys.exit(1) | 
 |         dv_base_names_dict[block].set_entity(base_type, entity) | 
 |     return dv_base_names_dict | 
 |  | 
 |  | 
 | def get_block_base_name(dv_base_names_map: Dict[str, DvBaseNames], block: str) -> DvBaseNames: | 
 |     '''Given a dictionary of `DvBaseNames` and return a `DvBaseNames` object for a specific block. | 
 |  | 
 |     If the given dictionary is empty, or cannot find the block name in the list of dictionary keys, | 
 |     this function will return the default `DvBaseNames` object. | 
 |     ''' | 
 |     if dv_base_names_map is None: | 
 |         return DvBaseNames() | 
 |     try: | 
 |         return dv_base_names_map[block] | 
 |     except KeyError: | 
 |         return DvBaseNames() | 
 |  | 
 |  | 
 | def gen_dv(block: IpBlock, dv_base_names: List[str], outdir: str) -> int: | 
 |     '''Generate DV files for an IpBlock''' | 
 |  | 
 |     lookup = TemplateLookup(directories=[resource_filename('reggen', '.')]) | 
 |     uvm_reg_tpl = lookup.get_template('uvm_reg.sv.tpl') | 
 |  | 
 |     # Generate the RAL package(s). For a device interface with no name we | 
 |     # generate the package "<block>_ral_pkg" (writing to <block>_ral_pkg.sv). | 
 |     # In any other case, we also need the interface name, giving | 
 |     # <block>_<ifname>_ral_pkg. | 
 |     generated = [] | 
 |  | 
 |     lblock = block.name.lower() | 
 |     dv_base_names_map = get_dv_base_names_objects(dv_base_names) | 
 |     block_dv_base_names = get_block_base_name(dv_base_names_map, lblock) | 
 |     device_hier_paths = block.bus_interfaces.device_hier_paths | 
 |  | 
 |     for if_name, rb in block.reg_blocks.items(): | 
 |  | 
 |         hier_path = device_hier_paths[if_name] | 
 |         if_suffix = '' if if_name is None else '_' + if_name.lower() | 
 |         mod_base = lblock + if_suffix | 
 |         reg_block_path = hier_path + if_suffix | 
 |  | 
 |         file_name = mod_base + '_ral_pkg.sv' | 
 |         generated.append(file_name) | 
 |         reg_top_path = os.path.join(outdir, file_name) | 
 |         with open(reg_top_path, 'w', encoding='UTF-8') as fout: | 
 |             try: | 
 |                 fout.write(uvm_reg_tpl.render(rb=rb, | 
 |                                               block=block, | 
 |                                               esc_if_name=mod_base, | 
 |                                               reg_block_path=reg_block_path, | 
 |                                               dv_base_names=block_dv_base_names)) | 
 |             except:  # noqa F722 for template Exception handling | 
 |                 log.error(exceptions.text_error_template().render()) | 
 |                 return 1 | 
 |  | 
 |     gen_core_file(outdir, lblock, dv_base_names, generated) | 
 |     return 0 |