| # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 | """Generate SystemVerilog designs from validated register JSON tree | 
 | """ | 
 |  | 
 | import logging as log | 
 |  | 
 | from mako.template import Template | 
 | from mako import exceptions | 
 | from pkg_resources import resource_filename | 
 |  | 
 | from .data import Field, Reg, MultiReg, Window, Block | 
 | from .field_enums import HwAccess, SwRdAccess, SwWrAccess | 
 |  | 
 |  | 
 | def escape_name(name): | 
 |     return name.lower().replace(' ', '_') | 
 |  | 
 |  | 
 | def check_field_bool(obj, field, default): | 
 |     if field in obj: | 
 |         return True if obj[field] == "true" else False | 
 |     else: | 
 |         return default | 
 |  | 
 |  | 
 | def parse_field(obj, reg, nfields): | 
 |     """Convert OrderedDict field into Field class | 
 |     """ | 
 |     f = Field() | 
 |     f.name = escape_name(obj["name"]) | 
 |     # if name doesn't exist and only one field in a reg | 
 |     if f.name == "" and nfields == 1: | 
 |         f.name = reg.name | 
 |  | 
 |     # MSB, LSB | 
 |     f.lsb = obj["bitinfo"][2] | 
 |     f.msb = f.lsb + obj["bitinfo"][1] - 1 | 
 |  | 
 |     f.swaccess = obj["genswaccess"] | 
 |     f.swrdaccess = obj["genswrdaccess"] | 
 |     f.swwraccess = obj["genswwraccess"] | 
 |     f.hwaccess = obj["genhwaccess"] | 
 |     f.hwqe = obj["genhwqe"] | 
 |     f.hwre = obj["genhwre"] | 
 |     f.hwext = reg.hwext | 
 |     f.tags = obj["tags"] | 
 |     f.shadowed = reg.shadowed | 
 |  | 
 |     # resval handling. `genresval` has zero value if `resval` field is defined | 
 |     # as unknown 'x' | 
 |     f.resval = obj["genresval"] | 
 |  | 
 |     return f | 
 |  | 
 |  | 
 | def parse_reg(obj): | 
 |     """Convert OrderedDict register into Register or MultiRegister object. | 
 |     Supports nested MultiRegisters. | 
 |     """ | 
 |     if 'multireg' in obj: | 
 |         regs = [] | 
 |         for genr in obj['multireg']['genregs']: | 
 |             regs += [parse_reg(genr)] | 
 |         # get register properties of the first register in the multireg and | 
 |         # copy them to the parent | 
 |         # since all regs in a multireg have the same props | 
 |         reg = MultiReg(regs[0].get_reg_flat(0)) | 
 |         # since this is a multireg, the list of fields can | 
 |         # contain regs or multiregs | 
 |         reg.fields = regs | 
 |         # a homogenous multireg contains only one single field that is replicated | 
 |         reg.ishomog = len(obj['multireg']['fields']) == 1 | 
 |         # TODO: need to rework this once the underlying JSON has been changed | 
 |         reg.name = escape_name(obj['multireg']['name']) | 
 |         # TODO: need to reference proper param here such that it can be used | 
 |         # in the package template for the array declaration | 
 |         # reg.param = ... | 
 |     else: | 
 |         reg = Reg(escape_name(obj['name'])) | 
 |         reg.offset = obj["genoffset"] | 
 |         reg.fields = [] | 
 |  | 
 |         reg.hwext = (obj['hwext'] == "true") | 
 |         reg.hwqe = (obj["hwqe"] == "true") | 
 |         reg.hwre = (obj["hwre"] == "true") | 
 |         reg.resval = obj["genresval"] | 
 |         reg.dvrights = obj["gendvrights"] | 
 |         reg.regwen = obj["regwen"].lower() | 
 |         reg.ishomog = len(obj['fields']) == 1 | 
 |         reg.tags = (obj['tags']) | 
 |         reg.shadowed = (obj["shadowed"] == "true") | 
 |  | 
 |         # Parsing Fields | 
 |         for f in obj["fields"]: | 
 |             field = parse_field(f, reg, len(obj["fields"])) | 
 |             if field is not None: | 
 |                 reg.fields.append(field) | 
 |                 reg.width = max(reg.width, field.msb + 1) | 
 |  | 
 |         # TODO: Field bitfield overlapping check | 
 |         log.info("R[0x%04x]: %s ", reg.offset, reg.name) | 
 |         for f in reg.fields: | 
 |             log.info("  F[%2d:%2d]: %s", f.msb, f.lsb, f.name) | 
 |  | 
 |     return reg | 
 |  | 
 |  | 
 | def parse_win(obj, width): | 
 |     # Convert register window fields into Window class | 
 |     # base_addr : genoffset | 
 |     # limit_addr : genoffset + items*width | 
 |     win = Window() | 
 |     win.name = obj["name"] | 
 |     win.base_addr = obj["genoffset"] | 
 |     win.limit_addr = obj["genoffset"] + int(obj["items"]) * (width // 8) | 
 |     win.dvrights = obj["swaccess"] | 
 |     win.n_bits = obj["genvalidbits"] | 
 |  | 
 |     # TODO: Generate warnings of `noalign` or `unusual` | 
 |     return win | 
 |  | 
 |  | 
 | def json_to_reg(obj): | 
 |     """Converts JSON OrderedDict into structure having useful information for | 
 |     Template to use. | 
 |  | 
 |     Main purpose of this function is: | 
 |         - Add Offset value based on auto calculation | 
 |         - Prepare Systemverilog data structure to generate _pkg file | 
 |     """ | 
 |     block = Block() | 
 |  | 
 |     # Name | 
 |     block.name = escape_name(obj["name"]) | 
 |     log.info("Processing module: %s", block.name) | 
 |  | 
 |     block.width = int(obj["regwidth"], 0) | 
 |  | 
 |     if block.width != 32 and block.width != 64: | 
 |         log.error( | 
 |             "Current reggen tool doesn't support field width that is not 32 nor 64" | 
 |         ) | 
 |  | 
 |     log.info("Data Width is set to %d bits", block.width) | 
 |  | 
 |     block.params = obj["param_list"] if "param_list" in obj else [] | 
 |  | 
 |     block.hier_path = obj["hier_path"] if "hier_path" in obj else "" | 
 |  | 
 |     for r in obj["registers"]: | 
 |         # Check if any exception condition hit | 
 |         if 'reserved' in r: | 
 |             continue | 
 |         elif 'skipto' in r: | 
 |             continue | 
 |         elif 'sameaddr' in r: | 
 |             log.error("Current tool doesn't support 'sameaddr' type") | 
 |             continue | 
 |         elif 'window' in r: | 
 |             win = parse_win(r['window'], block.width) | 
 |             if win is not None: | 
 |                 block.wins.append(win) | 
 |             continue | 
 |  | 
 |         block.regs += [parse_reg(r)] | 
 |  | 
 |     # Last offset and calculate space | 
 |     #  Later on, it could use block.regs[-1].genoffset | 
 |     if "space" in obj: | 
 |         block.addr_width = int(obj["space"], 0).bit_length() | 
 |     else: | 
 |         block.addr_width = (obj["gensize"] - 1).bit_length() | 
 |  | 
 |     return block | 
 |  | 
 |  | 
 | def gen_rtl(obj, outdir): | 
 |     # obj: OrderedDict | 
 |  | 
 |     block = json_to_reg(obj) | 
 |  | 
 |     # Read Register templates | 
 |     reg_top_tpl = Template( | 
 |         filename=resource_filename('reggen', 'reg_top.sv.tpl')) | 
 |     reg_pkg_tpl = Template( | 
 |         filename=resource_filename('reggen', 'reg_pkg.sv.tpl')) | 
 |  | 
 |     # Generate pkg.sv with block name | 
 |     with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w', | 
 |               encoding='UTF-8') as fout: | 
 |         try: | 
 |             fout.write( | 
 |                 reg_pkg_tpl.render(block=block, | 
 |                                    HwAccess=HwAccess, | 
 |                                    SwRdAccess=SwRdAccess, | 
 |                                    SwWrAccess=SwWrAccess)) | 
 |         except:  # noqa: F722 for template Exception handling | 
 |             log.error(exceptions.text_error_template().render()) | 
 |  | 
 |     # Generate top.sv | 
 |     with open(outdir + "/" + block.name + "_reg_top.sv", 'w', | 
 |               encoding='UTF-8') as fout: | 
 |         try: | 
 |             fout.write( | 
 |                 reg_top_tpl.render(block=block, | 
 |                                    HwAccess=HwAccess, | 
 |                                    SwRdAccess=SwRdAccess, | 
 |                                    SwWrAccess=SwWrAccess)) | 
 |         except:  # noqa: F722 for template Exception handling | 
 |             log.error(exceptions.text_error_template().render()) |