lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | """Generate SystemVerilog designs from validated register json tree |
| 5 | """ |
| 6 | |
| 7 | import logging as log |
| 8 | import operator |
| 9 | import sys |
| 10 | |
| 11 | from mako.template import Template |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 12 | from mako import exceptions |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 13 | from pkg_resources import resource_filename |
| 14 | |
Eunchan Kim | b993190 | 2019-09-26 14:16:40 -0700 | [diff] [blame] | 15 | from .data import * |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 16 | from .field_enums import HwAccess, SwAccess, SwRdAccess, SwWrAccess |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 17 | |
Eunchan Kim | 51461cd | 2019-09-18 14:00:49 -0700 | [diff] [blame] | 18 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 19 | def escape_name(name): |
| 20 | return name.lower().replace(' ', '_') |
| 21 | |
| 22 | |
| 23 | def check_field_bool(obj, field, default): |
| 24 | if field in obj: |
| 25 | return True if obj[field] == "true" else False |
| 26 | else: |
| 27 | return default |
| 28 | |
| 29 | |
| 30 | def parse_field(obj, reg, nfields): |
| 31 | """Convert OrderedDict field into Field class |
| 32 | """ |
| 33 | f = Field() |
| 34 | f.name = escape_name(obj["name"]) |
| 35 | # if name doesn't exist and only one field in a reg |
| 36 | if f.name == "" and nfields == 1: |
| 37 | f.name = reg.name |
| 38 | |
| 39 | # MSB, LSB |
| 40 | f.lsb = obj["bitinfo"][2] |
| 41 | f.msb = f.lsb + obj["bitinfo"][1] - 1 |
| 42 | |
| 43 | #assert not 'swaccess' in obj, "R[%s] F[%s]: SwAccess in Field not supported" % (reg.name, f.name) |
| 44 | f.swaccess = obj["genswaccess"] |
| 45 | f.swrdaccess = obj["genswrdaccess"] |
| 46 | f.swwraccess = obj["genswwraccess"] |
| 47 | f.hwaccess = obj["genhwaccess"] |
| 48 | f.hwqe = obj["genhwqe"] |
| 49 | f.hwre = obj["genhwre"] |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 50 | f.hwext = reg.hwext |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 51 | |
| 52 | # resval handling. `genresval` has zero value if `resval` field is defined |
| 53 | # as unknown 'x' |
| 54 | f.resval = obj["genresval"] |
| 55 | |
| 56 | return f |
| 57 | |
| 58 | |
| 59 | def parse_reg(obj): |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 60 | """Convert OrderedDict register into Register or MultiRegister object. |
| 61 | Supports nested MultiRegisters. |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 62 | """ |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 63 | if 'multireg' in obj: |
| 64 | regs = [] |
| 65 | for genr in obj['multireg']['genregs']: |
| 66 | regs += [parse_reg(genr)] |
| 67 | # get register properties of the first register in the multireg and |
| 68 | # copy them to the parent |
| 69 | # since all regs in a multireg have the same props |
| 70 | reg = MultiReg(regs[0].get_reg_flat(0)) |
| 71 | # since this is a multireg, the list of fields can |
| 72 | # contain regs or multiregs |
| 73 | reg.fields = regs |
Michael Schaffner | a2c51d9 | 2019-09-27 16:38:24 -0700 | [diff] [blame] | 74 | # a homogenous multireg contains only one single field that is replicated |
| 75 | reg.ishomog = len(obj['multireg']['fields']) == 1 |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 76 | # TODO: need to rework this once the underlying JSON has been changed |
| 77 | reg.name = escape_name(obj['multireg']['name']) |
| 78 | # TODO: need to reference proper param here such that it can be used |
| 79 | # in the package template for the array declaration |
| 80 | # reg.param = ... |
| 81 | else: |
| 82 | reg = Reg(escape_name(obj['name'])) |
| 83 | reg.offset = obj["genoffset"] |
| 84 | reg.fields = [] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 85 | |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 86 | reg.hwext = (obj['hwext'] == "true") |
| 87 | reg.hwqe = (obj["hwqe"] == "true") |
| 88 | reg.hwre = (obj["hwre"] == "true") |
| 89 | reg.resval = obj["genresval"] |
| 90 | reg.dvrights = obj["gendvrights"] |
| 91 | reg.regwen = obj["regwen"].lower() |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 92 | |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 93 | # Parsing Fields |
| 94 | for f in obj["fields"]: |
| 95 | field = parse_field(f, reg, len(obj["fields"])) |
| 96 | if field != None: |
| 97 | reg.fields.append(field) |
| 98 | reg.width = max(reg.width, field.msb + 1) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 99 | |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 100 | # TODO(eunchan): Field bitfield overlapping check |
| 101 | log.info("R[0x%04x]: %s ", reg.offset, reg.name) |
| 102 | for f in reg.fields: |
| 103 | log.info(" F[%2d:%2d]: %s", f.msb, f.lsb, f.name) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 104 | |
| 105 | return reg |
| 106 | |
| 107 | |
| 108 | def parse_win(obj, width): |
| 109 | # Convert register window fields into Window class |
| 110 | # base_addr : genoffset |
| 111 | # limit_addr : genoffset + items*width |
| 112 | win = Window() |
| 113 | win.name = obj["name"] |
| 114 | win.base_addr = obj["genoffset"] |
| 115 | win.limit_addr = obj["genoffset"] + int(obj["items"]) * (width // 8) |
| 116 | win.dvrights = obj["swaccess"] |
| 117 | win.n_bits = obj["genvalidbits"] |
| 118 | |
| 119 | # TODO: Generate warnings of `noalign` or `unusual` |
| 120 | return win |
| 121 | |
| 122 | |
| 123 | def json_to_reg(obj): |
| 124 | """Converts json OrderedDict into structure having useful information for |
| 125 | Template to use. |
| 126 | |
| 127 | Main purpose of this function is: |
| 128 | - Add Offset value based on auto calculation |
| 129 | - Prepare Systemverilog data structure to generate _pkg file |
| 130 | """ |
| 131 | block = Block() |
| 132 | |
| 133 | # Name |
| 134 | block.name = escape_name(obj["name"]) |
| 135 | log.info("Processing module: %s", block.name) |
| 136 | |
| 137 | block.width = int(obj["regwidth"], 0) |
| 138 | |
| 139 | if block.width != 32 and block.width != 64: |
| 140 | log.error( |
| 141 | "Current reggen tool doesn't support field width that is not 32 nor 64" |
| 142 | ) |
| 143 | |
| 144 | log.info("Data Width is set to %d bits", block.width) |
| 145 | |
Eunchan Kim | c4873f3 | 2019-09-25 12:46:12 -0700 | [diff] [blame] | 146 | block.params = obj["param_list"] if "param_list" in obj else [] |
| 147 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 148 | for r in obj["registers"]: |
| 149 | # Check if any exception condition hit |
| 150 | if 'reserved' in r: |
| 151 | continue |
| 152 | elif 'skipto' in r: |
| 153 | continue |
| 154 | elif 'sameaddr' in r: |
| 155 | log.error("Current tool doesn't support 'sameaddr' type") |
| 156 | continue |
| 157 | elif 'window' in r: |
| 158 | win = parse_win(r['window'], block.width) |
| 159 | if win != None: |
| 160 | block.wins.append(win) |
| 161 | continue |
Michael Schaffner | 9a94b6c | 2019-09-25 16:17:35 -0700 | [diff] [blame] | 162 | |
| 163 | block.regs += [parse_reg(r)] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 164 | |
| 165 | # Last offset and calculate space |
| 166 | # Later on, it could use block.regs[-1].genoffset |
| 167 | if "space" in obj: |
| 168 | block.addr_width = int(obj["space"], 0).bit_length() |
| 169 | else: |
| 170 | block.addr_width = (obj["gensize"] - 1).bit_length() |
| 171 | |
| 172 | return block |
| 173 | |
| 174 | |
| 175 | def gen_rtl(obj, outdir): |
| 176 | # obj: OrderedDict |
| 177 | |
| 178 | block = json_to_reg(obj) |
| 179 | |
| 180 | # Read Register templates |
| 181 | reg_top_tpl = Template( |
Michael Schaffner | c703936 | 2019-10-22 16:16:06 -0700 | [diff] [blame^] | 182 | filename=resource_filename('reggen', 'reg_top.sv.tpl')) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 183 | reg_pkg_tpl = Template( |
Michael Schaffner | c703936 | 2019-10-22 16:16:06 -0700 | [diff] [blame^] | 184 | filename=resource_filename('reggen', 'reg_pkg.sv.tpl')) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 185 | |
| 186 | # Generate pkg.sv with block name |
| 187 | with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w', |
| 188 | encoding='UTF-8') as fout: |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 189 | try: |
| 190 | fout.write( |
| 191 | reg_pkg_tpl.render(block=block, |
| 192 | HwAccess=HwAccess, |
| 193 | SwRdAccess=SwRdAccess, |
| 194 | SwWrAccess=SwWrAccess)) |
| 195 | except: |
| 196 | log.error(exceptions.text_error_template().render()) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 197 | |
| 198 | # Generate top.sv |
| 199 | with open(outdir + "/" + block.name + "_reg_top.sv", 'w', |
| 200 | encoding='UTF-8') as fout: |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 201 | try: |
| 202 | fout.write( |
| 203 | reg_top_tpl.render(block=block, |
| 204 | HwAccess=HwAccess, |
| 205 | SwRdAccess=SwRdAccess, |
| 206 | SwWrAccess=SwWrAccess)) |
| 207 | except: |
| 208 | log.error(exceptions.text_error_template().render()) |