Start of public OpenTitan development history
Code contributors:
Alex Bradbury <asb@lowrisc.org>
Cindy Chen <chencindy@google.com>
Eunchan Kim <eunchan@google.com>
Gaurang Chitroda <gaurangg@google.com>
Mark Hayter <mark.hayter@gmail.com>
Michael Schaffner <msf@google.com>
Miguel Osorio <miguelosorio@google.com>
Nils Graf <nilsg@google.com>
Philipp Wagner <phw@lowrisc.org>
Pirmin Vogel <vogelpi@lowrisc.org>
Ram Babu Penugonda <rampenugonda@google.com>
Scott Johnson <scottdj@google.com>
Shail Kushwah <kushwahs@google.com>
Srikrishna Iyer <sriyer@google.com>
Steve Nelson <Steve.Nelson@wdc.com>
Tao Liu <taliu@google.com>
Timothy Chen <timothytim@google.com>
Tobias Wölfel <tobias.woelfel@mailbox.org>
Weicai Yang <weicai@google.com>
diff --git a/util/reggen/validate.py b/util/reggen/validate.py
new file mode 100644
index 0000000..da45c21
--- /dev/null
+++ b/util/reggen/validate.py
@@ -0,0 +1,1206 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""
+Register json validation
+"""
+
+import logging as log
+import sys
+
+from reggen.field_enums import *
+
+
+# Routine that can be used for hjson object_pairs_hook
+# The baseline is dict(pairs) i.e. construct a dictonary from pairs
+# The usual is OrderedDict(pairs) which is redundant in latest python
+# Both of these silently allow repeated keys, which this version detects
+def checking_dict(pairs):
+ d = {}
+ for x in pairs:
+ if x[0] in d:
+ repkey = 'Repeated' + x[0]
+ log.warn("Repeated key " + x[0] + " added as " + repkey)
+ d[repkey] = x[1]
+ else:
+ d[x[0]] = x[1]
+ return d
+
+
+# validating version of int(x, 0)
+# returns int value, error flag
+# if error flag is True value will be zero
+def check_int(x, err_prefix):
+ if isinstance(x, int):
+ return x, False
+ if x[0] == '0' and len(x) > 2:
+ if x[1] in 'bB':
+ validch = '01'
+ elif x[1] in 'oO':
+ validch = '01234567'
+ elif x[1] in 'xX':
+ validch = '0123456789abcdefABCDEF'
+ else:
+ log.error(err_prefix +
+ ": int must start digit, 0b, 0B, 0o, 0O, 0x or 0X")
+ return 0, True
+ for c in x[2:]:
+ if not c in validch:
+ log.error(err_prefix + ": Bad character " + c + " in " + x)
+ return 0, True
+ else:
+ if not x.isdecimal():
+ log.error(err_prefix + ": Number not valid int " + x)
+ return 0, 1
+ return int(x, 0), False
+
+
+def check_bool(x, err_prefix):
+ """check_bool checks if input 'x' is one of the list:
+ "true", "false"
+
+ It returns value as Bool type and Error condition.
+ """
+ if isinstance(x, bool):
+ # if Bool returns as it is
+ return x, False
+ if not x.lower() in ["true", "false"]:
+ log.error(err_prefix + ": Bad field value " + x)
+ return False, True
+ else:
+ return (x.lower() == "true"), False
+
+
+def check_ln(obj, x, withwidth, err_prefix):
+ error = 0
+ if not isinstance(obj[x], list):
+ log.error(err_prefix + ' element ' + x + ' not a list')
+ return 1
+ for y in obj[x]:
+ error += check_keys(y, ln_required, ln_optional if withwidth else {},
+ {}, err_prefix + ' element ' + x)
+ if withwidth:
+ if 'width' in y:
+ w, err = check_int(y['width'], err_prefix + ' width in ' + x)
+ if err:
+ error += 1
+ w = 1
+ else:
+ w = 1
+ y['width'] = str(w)
+
+ return error
+
+
+def check_keys(obj, required_keys, optional_keys, added_keys, err_prefix):
+ error = 0
+ for x in required_keys:
+ if not x in obj:
+ error += 1
+ log.error(err_prefix + " missing required key " + x)
+ for x in obj:
+ type = ''
+ if x in required_keys:
+ type = required_keys[x][0]
+ elif x in optional_keys:
+ type = optional_keys[x][0]
+ elif not x in added_keys:
+ log.warning(err_prefix + " contains extra key " + x)
+ if type[:2] == 'ln':
+ error += check_ln(obj, x, type == 'lnw', err_prefix)
+ return error
+
+
+# Only allow zero or one of the list of keys
+def check_zero_one_key(obj, optone, err_prefix):
+ error = 0
+ seenopt = 0
+ for x in obj:
+ if (x in optone):
+ seenopt += 1
+ if (seenopt > 1) or ((seenopt == 1) and len(obj) > 1):
+ log.error(err_prefix + " only allowed one option key: ")
+ for x in obj:
+ log.error(err_prefix + " found: " + x)
+ error += 1
+ return error
+
+
+def bitfield_add(bfield, num):
+ if ':' in bfield:
+ brange = bfield.partition(':')
+ msb = brange[0]
+ lsb = brange[2]
+ return str(int(msb) + num) + ':' + str(int(lsb) + num)
+ else:
+ return str(int(bfield) + num)
+
+
+# get_bits to give a sort key
+def get_bits(x):
+ pos = x['bits'].find(':')
+ if pos < 0:
+ return int(x['bits'])
+ else:
+ return int(x['bits'][:pos])
+
+
+# returns tuple (bitfield_mask, field width, lsb)
+def bitmask(bfield):
+ if ':' in bfield:
+ brange = bfield.partition(':')
+ msb = brange[0]
+ lsb = brange[2]
+ res = 0
+ if ((not msb.isdecimal()) or (not lsb.isdecimal()) or
+ (int(lsb) > int(msb))):
+ log.error("Bad bit range " + bfield + str(brange))
+ return (0, 0, 0)
+ else:
+ for i in range(int(lsb), int(msb) + 1):
+ res |= (1 << i)
+ return (res, int(msb) - int(lsb) + 1, int(lsb))
+ if (not bfield.isdecimal()):
+ log.error("Bad bit number " + bfield)
+ return (0, 0, 0)
+ else:
+ return (1 << int(bfield), 1, int(bfield))
+
+
+val_types = {
+ 'd': ["int", "integer (binary 0b, octal 0o, decimal, hex 0x)"],
+ 'x': ["xint", "x for undefined otherwise int"],
+ 'b': [
+ "bitrange", "bit number as decimal integer, \
+ or bit-range as decimal integers msb:lsb"
+ ],
+ 'l': ["list", "comma separated list enclosed in `[]`"],
+ 'ln': ["name list", 'comma separated list enclosed in `[]` of '\
+ 'one or more groups that have just name and dscr keys.'\
+ ' e.g. `{ name: "name", desc: "description"}`'],
+ 'lnw': ["name list+", 'name list that optionally contains a width'],
+ 'g': ["group", "comma separated group of key:value enclosed in `{}`"],
+ 's': ["string", "string, typically short"],
+ 't': ["text", "string, may be multi-line enclosed in `'''` "\
+ "may use `**bold**`, `*italic*` or `!!Reg` markup"],
+ 'T': ["tuple", "tuple enclosed in ()"],
+ 'pi': ["python int", "Native python type int (generated)"],
+ 'pb': ["python Bool", "Native python type Bool (generated)"],
+ 'pl': ["python list", "Native python type list (generated)"],
+ 'pe': ["python enum", "Native python type enum (generated)"]
+}
+
+# Toplevel keys
+top_required = {
+ 'name': ['s', "name of the component"],
+ 'clock_primary': ['s', "name of the primary clock"],
+ 'bus_device': ['s', "name of the bus interface for the device"],
+ 'registers': [
+ 'l', "list of register definition groups and \
+ offset control groups"
+ ]
+}
+top_optional = {
+ 'bus_host': ['s', "name of the bus interface as host"],
+ 'other_clock_list': ['l', "list of other chip clocks needed"],
+ 'available_input_list': ['lnw', "list of available peripheral inputs"],
+ 'available_output_list': ['lnw', "list of available peripheral outputs"],
+ 'available_inout_list': ['lnw', "list of available peripheral inouts"],
+ 'interrupt_list': ['lnw', "list of peripheral interrupts"],
+ 'no_auto_intr_regs': ['s', "Set to true to suppress automatic "\
+ "generation of interrupt registers. " \
+ "Defaults to false if not present."],
+ 'alert_list': ['ln', "list of peripheral alerts"],
+ 'regwidth': ['d', "width of registers in bits (default 32)"],
+ 'SPDX-License-Identifier': ['s', "License ientifier (if using pure json) "\
+ "Only use this if unable to put this "\
+ "information in a comment at the top of the "\
+ "file."]
+}
+top_added = {'genrnames': ['pl', "list of register names"],
+ 'genautoregs': ['pb', "Registers were generated from config info"],
+ 'genwennames': ['pl', "list of registers used as write enables"],
+ 'gennextoffset': ['pi', "offset next register would use"],
+ 'gensize': ['pi', "address space size needed for registers. "\
+ "Generated by tool as next power of 2."]
+ }
+
+# ln type has list of groups with only name and description
+# (was called "subunit" in cfg_validate)
+ln_required = {
+ 'name': ['s', "name of the item"],
+ 'desc': ['s', "description of the item"],
+}
+ln_optional = {
+ 'width': ['d', "bit width of the item (if not 1)"],
+}
+
+# Registers list may have embedded keys
+list_optone = {'reserved': ['d', "number of registers to reserve space for"],
+ 'skipto': ['d', "set next register offset to value"],
+ 'sameaddr': ['l', "list of register definition groups "\
+ "that share the same offset"],
+ 'window': ['g', "group defining an address range "\
+ "for something other than standard registers"],
+ 'multireg': ['g', "group defining registers generated "\
+ "from a base instance."]
+ }
+
+# Register keys
+reg_required = {
+ 'name': ['s', "name of the register"],
+ 'desc': ['t', "description of the register"],
+ 'fields': ['l', "list of register field description groups"]
+}
+
+reg_optional = {
+ 'swaccess': [
+ 's', "software access permission to use for " +
+ "fields that don't specify swaccess"
+ ],
+ 'hwaccess': [
+ 's', "hardware access permission to use for " +
+ "fields that don't specify hwaccess"
+ ],
+ 'hwext': [
+ 's',
+ "'true' if the register is stored out side " + "of the register module"
+ ],
+ 'hwqe': [
+ 's', "'true' if hardware uses 'q' enable signal, " +
+ "which is latched signal of software write pulse."
+ ],
+ 'hwre': [
+ 's', "'true' if hardware uses 're' signal, " +
+ "which is latched signal of software read pulse."
+ ],
+ 'regwen': [
+ 's', "if register is write-protected by another register, that " +
+ "register name should be given here. empty-string for no register " +
+ "write protection"
+ ],
+ 'resval': ['d', "reset value of full register (default 0)"]
+}
+reg_added = {
+ 'genresval': ['pi', "reset value generated from resval and fields"],
+ 'genresmask': ['pi', "mask of bits with valid reset value (not x)"],
+ 'genbitsused': ['pi', "mask of bits defined in the register"],
+ 'genoffset': ['pi', "offset address of the register"],
+ 'genbasebits': ['pi', "multireg only: mask of base bits defined"],
+ 'gendvrights': ['s', "SW Rights used in UVM reg class"]
+}
+
+# Window keys
+window_required = {
+ 'name': ['s', "Name of the window"],
+ 'desc': ['t', "description of the window"],
+ 'items': ['d', "size in fieldaccess width words of the window"],
+ 'swaccess': ['s', "software access permitted"],
+}
+
+# TODO potential for additional optional to give more type info?
+# eg sram-hw-port: "none", "sync", "async"
+window_optional = {'byte-write': ['s', "True if byte writes are supported. "\
+ "Defaults to false if not present."],
+ 'validbits': ['d', "Number of valid data bits within "\
+ "regwidth sized word. "\
+ "Defaults to regwidth. If "\
+ "smaller than the regwidth then in each "\
+ "word of the window bits "\
+ "[regwidth-1:validbits] are unused and "\
+ "bits [validbits-1:0] are valid."],
+ 'noalign': ['s', "Set to True to prevent tool aligning "\
+ "the base address of the window. "\
+ "Defaults to false if not present."],
+ 'unusual': ['s', "True if window has unusual parameters "\
+ "(set to prevent Unusual: errors)."\
+ "Defaults to false if not present."]
+ }
+
+window_added = {'genbyte-write': ['pb', "generated boolean for byte-write "],
+ 'genvalidbits': ['pi', "vailid data width"],
+ 'genoffset': ['pi', "base offset address of the window "\
+ "(aligned for size)"],
+ 'genswaccess': ['pe', "Software access (gen enum)"],
+ 'genswwraccess': ['pe', "Software write access (gen enum)"],
+ 'genswrdaccess': ['pe', "Software read access (gen enum)"]
+ }
+
+# Multireg keys
+multireg_required = {'name': ['s', "base name of the registers"],
+ 'desc': ['t', "description of the registers"],
+ 'count': ['d', "number of instances to generate"],
+ 'cname': ['s', "base name for each instance, mostly "\
+ "useful for refering to instance in messages"],
+ 'fields': ['l', "list of register field description"\
+ "groups. Describes bit positions used for"\
+ " base instance."]
+ }
+multireg_optional = reg_optional
+multireg_added = {'genregs': ['l',
+ "generated list of registers with required "\
+ "and added keys"]
+ }
+
+# Field keys
+# special case in the code, no name and no desc if only field
+field_required = {
+ 'name': ['s', "name of the field (optional if only field)"],
+ 'desc': ['t', "description of field (optional if no name)"],
+ 'bits': ['b', "bit or bit range (msb:lsb)"]
+}
+field_optional = {
+ 'swaccess': [
+ 's', "software access permission, copied from "
+ "register if not provided in field. "
+ "(Tool adds if not provided.)"
+ ],
+ 'hwaccess': [
+ 's', "hardware access permission, copied from "
+ "register if not prvided in field. "
+ "(Tool adds if not provided.)"
+ ],
+ 'resval': [
+ 'x', "reset value, comes from register resval "
+ "if not provided in field. Zero if neither "
+ "are provided and the field is readable, "
+ "x if neither are provided and the field "
+ "is wo. Must match if both are provided."
+ ],
+ 'enum': ['l', "list of permitted enumeration groups"]
+}
+field_added = {
+ 'genrsvdenum': ['pb', "enum did not cover every possible value"],
+ 'genresval': [
+ 'pi', "resval for field constructed by the tool. "
+ "Will be set to 0 for x."
+ ],
+ 'genresvalx': ['pb', "Indicates if resval is x"],
+ 'genswaccess': ['pe', "Software access (generated enum)"],
+ 'genswwraccess': ['pe', "Software write access (generated enum)"],
+ 'genswrdaccess': ['pe', "Software read access (generated enum)"],
+ 'genhwaccess': ['pe', "Hardware access (generated Enum)"],
+ 'genhwqe': ['pb', "Hardware qualifier enable signal needed"],
+ 'genhwre': ['pb', "Hardware read enable signal needed"],
+ 'bitinfo': ['T', "tuple (bitfield_mask, field width, lsb)"]
+}
+
+# Enum keys
+enum_required = {
+ 'name': ['s', "name of the member of the enum"],
+ 'desc': ['t', "description when field has this value"],
+ 'value': ['d', "value of this member of the enum"]
+}
+enum_optional = {}
+enum_added = {}
+
+# swaccess permitted values
+# text description, access enum, wr access enum, rd access enum, ok in window
+swaccess_permitted = {
+ 'ro': ("Read Only",
+ SwAccess.RO, SwWrAccess.NONE, SwRdAccess.RD, True),
+ 'rc': ("Read Only, reading clears",
+ SwAccess.RC, SwWrAccess.WR, SwRdAccess.RC, False),
+ 'rw': ("Read/Write",
+ SwAccess.RW, SwWrAccess.WR, SwRdAccess.RD, True),
+ 'r0w1c': ("Read zero, Write with 1 clears",
+ SwAccess.W1C, SwWrAccess.WR, SwRdAccess.NONE, False),
+ 'rw1s': ("Read, Write with 1 sets",
+ SwAccess.W1S, SwWrAccess.WR, SwRdAccess.RD, False),
+ 'rw1c': ("Read, Write with 1 clears",
+ SwAccess.W1C, SwWrAccess.WR, SwRdAccess.RD, False),
+ 'rw0c': ("Read, Write with 0 clears",
+ SwAccess.W0C, SwWrAccess.WR, SwRdAccess.RD, False),
+ 'wo': ("Write Only",
+ SwAccess.WO, SwWrAccess.WR, SwRdAccess.NONE, True)
+} # yapf: disable
+
+# hwaccess permitted values
+hwaccess_permitted = {
+ 'hro': ("Read Only", HwAccess.HRO),
+ 'hrw': ("Read/Write", HwAccess.HRW),
+ 'hwo': ("Write Only", HwAccess.HWO),
+ 'none': ("No Access Needed", HwAccess.NONE)
+}
+
+key_use = {'r': "required", 'o': "optional", 'a': "added by tool"}
+
+# Register name prohibited (used as reserved keywords in systemverilog)
+keywords_verilog = [
+ 'alias', 'always', 'always_comb', 'always_ff', 'always_latch', 'and',
+ 'assert', 'assign', 'assume', 'automatic', 'before', 'begin', 'bind',
+ 'bins', 'binsof', 'bit', 'break', 'buf', 'bufif0', 'bufif1', 'byte',
+ 'case', 'casex', 'casez', 'cell', 'chandle', 'class', 'clocking', 'cmos',
+ 'config', 'const', 'constraint', 'context', 'continue', 'cover',
+ 'covergroup', 'coverpoint', 'cross', 'deassign', 'default', 'defparam',
+ 'design', 'disable', 'dist', 'do', 'edge', 'else', 'end', 'endcase',
+ 'endclass', 'endclocking', 'endconfig', 'endfunction', 'endgenerate',
+ 'endgroup', 'endinterface', 'endmodule', 'endpackage', 'endprimitive',
+ 'endprogram', 'endproperty', 'endspecify', 'endsequence', 'endtable',
+ 'endtask', 'enum', 'event', 'expect', 'export', 'extends', 'extern',
+ 'final', 'first_match', 'for', 'force', 'foreach', 'forever', 'fork',
+ 'forkjoin', 'function', 'generate', 'genvar', 'highz0', 'highz1', 'if',
+ 'iff', 'ifnone', 'ignore_bins', 'illegal_bins', 'import', 'incdir',
+ 'include', 'initial', 'inout', 'input', 'inside', 'instance', 'int',
+ 'integer', 'interface', 'intersect', 'join', 'join_any', 'join_none',
+ 'large', 'liblist', 'library', 'local', 'localparam', 'logic', 'longint',
+ 'macromodule', 'matches', 'medium', 'modport', 'module', 'nand', 'negedge',
+ 'new', 'nmos', 'nor', 'noshowcancelled', 'not', 'notif0', 'notif1', 'null',
+ 'or', 'output', 'package', 'packed', 'parameter', 'pmos', 'posedge',
+ 'primitive', 'priority', 'program', 'property', 'protected', 'pull0',
+ 'pull1', 'pulldown', 'pullup', 'pulsestyle_onevent', 'pulsestyle_ondetect',
+ 'pure', 'rand', 'randc', 'randcase', 'randsequence', 'rcmos', 'real',
+ 'realtime', 'ref', 'reg', 'release', 'repeat', 'return', 'rnmos', 'rpmos',
+ 'rtran', 'rtranif0', 'rtranif1', 'scalared', 'sequence', 'shortint',
+ 'shortreal', 'showcancelled', 'signed', 'small', 'solve', 'specify',
+ 'specparam', 'static', 'string', 'strong0', 'strong1', 'struct', 'super',
+ 'supply0', 'supply1', 'table', 'tagged', 'task', 'this', 'throughout',
+ 'time', 'timeprecision', 'timeunit', 'tran', 'tranif0', 'tranif1', 'tri',
+ 'tri0', 'tri1', 'triand', 'trior', 'trireg', 'type', 'typedef', 'union',
+ 'unique', 'unsigned', 'use', 'uwire', 'var', 'vectored', 'virtual', 'void',
+ 'wait', 'wait_order', 'wand', 'weak0', 'weak1', 'while', 'wildcard',
+ 'wire', 'with', 'within', 'wor', 'xnor', 'xor'
+]
+
+
+def validate_fields(fields, rname, default_sw, default_hw, full_resval,
+ reg_hwqe, reg_hwre, width):
+ error = 0
+ bits_used = 0
+ gen_resval = 0
+ gen_resmask = 0
+ fcount = 0
+
+ fieldnames = []
+ if len(fields) == 0:
+ log.warn(rname + " fields is empty")
+
+ for field in fields:
+ fcount += 1
+ if not 'name' in field:
+ fname = rname + ".field" + str(fcount)
+ if (len(fields) == 1):
+ field['name'] = rname
+ # only allow no desc if no name
+ if not 'desc' in field:
+ field['desc'] = ""
+ else:
+ fname = field['name']
+ if fname in keywords_verilog:
+ error += 1
+ log.error(rname + " field " + fname + " uses verilog keywords")
+ if (fname == ""):
+ fname = rname + ".field" + str(fcount)
+ else:
+ if fname in fieldnames:
+ error += 1
+ log.error(rname + " field " + str(fcount) +
+ ": duplicate use of field name " + fname)
+ else:
+ fieldnames.append(fname)
+ fname = rname + "." + fname
+ ck_err = check_keys(field, field_required, field_optional, field_added,
+ fname)
+ if (ck_err != 0):
+ error += ck_err
+ continue
+
+ if not 'swaccess' in field:
+ if (default_sw == None):
+ error += 1
+ log.error(fname + ":no swaccess or register default swaccess")
+ swaccess = "wo"
+ else:
+ log.info(fname + ": use register default swaccess")
+ field['swaccess'] = default_sw
+ swaccess = default_sw
+ else:
+ swaccess = field['swaccess']
+ if (not swaccess in swaccess_permitted):
+ error += 1
+ log.error(fname + ": Bad field swaccess value " + swaccess)
+ swaccess = "wo"
+ swacc_info = swaccess_permitted[swaccess]
+ field['genswaccess'] = swacc_info[1]
+ field['genswwraccess'] = swacc_info[2]
+ field['genswrdaccess'] = swacc_info[3]
+
+ if not 'hwaccess' in field:
+ if (default_hw == None):
+ error += 1
+ log.error(fname + ": no hwaccess or register default hwaccess")
+ hwaccess = "hro"
+ else:
+ log.info(fname + ": use register default hwaccess")
+ field['hwaccess'] = default_hw
+ hwaccess = default_hw
+ else:
+ hwaccess = field['hwaccess']
+ if (not hwaccess in hwaccess_permitted):
+ error += 1
+ log.error(fname + ": Bad field hwaccess value " + hwaccess)
+ hwaccess = "hro"
+ hwacc_info = hwaccess_permitted[hwaccess]
+ field['genhwaccess'] = hwacc_info[1]
+ field['genhwqe'] = reg_hwqe
+ field['genhwre'] = reg_hwre
+
+ # allow an int but make a string for all downstream users
+ if isinstance(field['bits'], int):
+ field['bits'] = str(field['bits'])
+ field_bits = bitmask(field['bits'])
+ if (field_bits[0] == 0):
+ error += 1
+ else:
+ reuse_check = bits_used & field_bits[0]
+ # > is correct here because the check is of the bit
+ # above the msb. The equal case is thus valid
+ if ((field_bits[1] + field_bits[2]) > width):
+ error += 1
+ log.error(fname + ": Register not wide enough for bits: " +
+ field['bits'])
+ elif reuse_check != 0:
+ error += 1
+ log.error(fname + ": Defines already defined bits " +
+ hex(reuse_check))
+ bits_used |= field_bits[0]
+ field['bitinfo'] = field_bits
+ max_in_field = (1 << field_bits[1]) - 1
+
+ if 'resval' in field:
+ if field['resval'] != "x":
+ resval, ierr = check_int(field['resval'], fname + " resval")
+ if ierr:
+ error += 1
+ if (resval > max_in_field):
+ error += 1
+ log.error(fname + ": Reset value " + field['resval'] +
+ " greater than max field can hold (" +
+ hex(max_in_field) + ")")
+ resval &= max_in_field
+
+ if ((full_resval != None) and
+ (resval !=
+ ((full_resval >> field_bits[2]) & max_in_field))):
+ error += 1
+ log.error(fname + ": Field resval " + field['resval'] +
+ " differs from value in main register resval " +
+ hex(full_resval))
+ gen_resval |= resval << field_bits[2]
+ gen_resmask |= field_bits[0]
+ field['genresval'] = resval
+ field['genresvalx'] = False
+ else:
+ field['genresval'] = 0
+ field['genresvalx'] = True
+ else:
+ if (full_resval != None):
+ resval = (full_resval >> field_bits[2]) & max_in_field
+ gen_resval |= resval << field_bits[2]
+ gen_resmask |= field_bits[0]
+ field['genresval'] = resval
+ field['genresvalx'] = False
+ log.info(fname + ": use register default genresval")
+ else:
+ if swaccess[0] != 'w':
+ field['genresval'] = 0
+ field['genresvalx'] = False
+ log.info(fname + ": use zero genresval")
+ gen_resmask |= field_bits[0]
+ else:
+ field['genresval'] = 0
+ field['genresvalx'] = True
+ log.info(fname + ": use x genresval")
+
+ if 'enum' in field:
+ if max_in_field > 127:
+ log.warning(fname + "enum too big for checking.")
+ enum_mask = 0
+ else:
+ enum_mask = (1 << (max_in_field + 1)) - 1
+ for enum in field['enum']:
+ eck_err = check_keys(enum, enum_required, [], [],
+ fname + " enum")
+ if (eck_err != 0):
+ error += eck_err
+ continue
+ ename = enum['name']
+ val, ierr = check_int(enum['value'], fname + "." + ename)
+ if ierr:
+ error += 1
+ if (val > max_in_field):
+ error += 1
+ log.error(fname + ": enum value " + str(val) + "too big")
+ elif max_in_field <= 127:
+ valbit = 1 << val
+ if ((enum_mask & valbit) == 0):
+ log.warning(fname + "enum has multiple " + str(val))
+ else:
+ enum_mask ^= valbit
+
+ if (enum_mask != 0):
+ field['genrsvdenum'] = True
+ log.info(fname + ": Enum values not complete. Mask " +
+ hex(enum_mask))
+
+ return error, gen_resval, gen_resmask, bits_used
+
+
+def parse_dvrights(field=None):
+ if field == None:
+ return "RO"
+ elif field in ['ro', 'rc']:
+ return "RO"
+ elif field in ['rw', 'r0w1c', 'rw1s', 'rw1c', 'rw0c']:
+ return "RW"
+ else:
+ return "WO"
+
+
+def validate_reg_defaults(reg, rname):
+ error = 0
+ if 'swaccess' in reg:
+ default_sw = reg['swaccess']
+ if (not default_sw in swaccess_permitted):
+ error += 1
+ log.error(rname + ": Bad register swaccess value " + default_sw)
+ default_sw = None
+ else:
+ default_sw = None
+
+ if 'hwaccess' in reg:
+ default_hw = reg['hwaccess']
+ if (not default_hw in hwaccess_permitted):
+ error += 1
+ log.error(rname + ": Bad register hwaccess value " + default_hw)
+ default_hw = None
+ else:
+ default_hw = "hro" # Read-Only
+
+ if 'hwext' in reg:
+ hwext, ierr = check_bool(reg['hwext'], rname + " hwext")
+ if ierr:
+ error += 1
+ reg['hwext'] = "false"
+ else:
+ reg['hwext'] = "false"
+
+ if 'hwqe' in reg:
+ hwqe, ierr = check_bool(reg['hwqe'], rname + " hwqe")
+
+ if ierr:
+ error += 1
+ reg['hwqe'] = "false"
+ elif hwqe == False and reg[
+ 'hwext'] == "true" and reg['swaccess'] != "ro":
+ log.warning(rname + ": hwqe must be true for hwext register. " +
+ "Changing it to true.")
+ reg['hwqe'] = "true"
+ elif reg['hwext'] == "true" and reg['swaccess'] != "ro":
+ log.warning(rname + ": hwqe not provided but must be true for "\
+ "hwext not read-only register. Setting it to true.")
+ reg['hwqe'] = "true"
+ else:
+ reg['hwqe'] = "false"
+
+ if 'hwre' in reg:
+ hwre, ierr = check_bool(reg['hwre'], rname + " hwre")
+
+ if ierr:
+ error += 1
+ reg['hwre'] = "false"
+ elif hwre == True and reg['hwext'] == "false":
+ log.warning(rname + ": hwre cannot be used with hwext. " +
+ "Changing it to false.")
+ reg['hwre'] = "false"
+ else:
+ reg['hwre'] = "false"
+
+ if 'regwen' not in reg:
+ reg['regwen'] = ''
+
+ if 'resval' in reg:
+ full_resval, ierr = check_int(reg['resval'], rname + " resval")
+ if ierr:
+ error += 1
+ full_resval = None
+ else:
+ full_resval = None
+
+ return error, default_sw, default_hw, full_resval
+
+
+def validate_register(reg, offset, width, top):
+ error = 0
+
+ if not 'name' in reg:
+ rname = "Register at +" + hex(offset)
+ else:
+ rname = reg['name']
+ if rname in keywords_verilog:
+ error += 1
+ log.error("Register at +" + hex(offset) + rname +
+ " uses verilog keywords")
+ if rname.lower() in top['genrnames']:
+ error += 1
+ log.error("Register at +" + hex(offset) + " duplicate name " +
+ rname)
+ else:
+ top['genrnames'].append(rname.lower())
+
+ error += check_keys(reg, reg_required, reg_optional, reg_added, rname)
+
+ derr, default_sw, default_hw, full_resval = validate_reg_defaults(
+ reg, rname)
+ error += derr
+
+ # if there was an error before this then can't trust anything!
+ if error > 0:
+ log.info(rname + "@" + hex(offset) + " " + str(error) +
+ " top level errors. Not processing fields")
+ return error
+
+ gen = validate_fields(reg['fields'], rname, default_sw, default_hw,
+ full_resval, reg['hwqe'] == "true",
+ reg['hwre'] == "true", width)
+ error = error + gen[0]
+ # ensure the fields are in order (except if error which could be bad bits)
+ if error == 0:
+ reg['fields'].sort(key=get_bits)
+ reg['genresval'] = gen[1]
+ reg['genresmask'] = gen[2]
+ reg['genbitsused'] = gen[3]
+ reg['genoffset'] = offset
+ reg['gendvrights'] = parse_dvrights(default_sw)
+
+ if ((reg['regwen'] != '') and
+ (not reg['regwen'] in top['genwennames'])):
+ top['genwennames'].append(reg['regwen'])
+
+ log.info(rname + "@" + hex(offset) + " " + str(error) + " errors. Mask " +
+ hex(gen[3]))
+
+ return error
+
+
+def validate_multi(mreg, offset, addrsep, width, top):
+ error = 0
+ bits_used = 0
+
+ if not 'name' in mreg:
+ mrname = "MultiRegister at +" + hex(offset)
+ else:
+ mrname = mreg['name']
+ error = check_keys(mreg, multireg_required, multireg_optional,
+ multireg_added, mrname)
+ derr, default_sw, default_hw, full_resval = validate_reg_defaults(
+ mreg, mrname)
+ error += derr
+
+ # if there was an error before this then can't trust anything!
+ if error > 0:
+ log.info(mrname + "@" + hex(offset) + " " + str(error) +
+ " top level errors. Not processing fields")
+ return error
+
+ gen = validate_fields(mreg['fields'], mrname, default_sw, default_hw,
+ full_resval, mreg['hwqe'] == "true",
+ mreg['hwre'] == "true", width)
+
+ error += gen[0]
+
+ mcount, ierr = check_int(mreg['count'], mrname + " multireg count")
+ if ierr:
+ error += 1
+
+ if error > 0:
+ return (error, 0)
+ bused = gen[3]
+ max_rval = (1 << width) - 1
+ cname = mreg['cname']
+ bpos = 0
+ inum = 0
+ rlist = []
+ rnum = 0
+ while inum < mcount:
+ closereg = False
+ if bpos == 0:
+ genreg = {}
+ genreg['name'] = mrname + str(rnum)
+ genreg['desc'] = mreg['desc']
+ genreg['hwext'] = mreg['hwext']
+ genreg['hwqe'] = mreg['hwqe']
+ genreg['hwre'] = mreg['hwre']
+ genreg['regwen'] = mreg['regwen']
+ resval = 0
+ resmask = 0
+ bits_used = 0
+ genfields = []
+
+ while bpos < width:
+ trypos = bused << bpos
+ if trypos > max_rval:
+ bpos = width
+ break
+ if (trypos & bits_used) == 0:
+ break
+ bpos += 1
+ if bpos < width:
+ # found a spot
+ for fn in mreg['fields']:
+ newf = fn.copy()
+ newf['name'] += str(inum)
+ if bpos != 0:
+ newf['bits'] = bitfield_add(newf['bits'], bpos)
+ newf['desc'] = 'for ' + cname + str(inum)
+ newf['bitinfo'] = (newf['bitinfo'][0] << bpos,
+ newf['bitinfo'][1],
+ newf['bitinfo'][2] + bpos)
+ if 'enum' in newf:
+ del newf['enum']
+ else:
+ newf['desc'] += ' for ' + cname + str(inum)
+ genfields.append(newf)
+ bits_used = bits_used | bused << bpos
+ resval = resval | gen[1] << bpos
+ resmask = resmask | gen[2] << bpos
+ bpos += 1
+ inum += 1
+ if inum == mcount:
+ closereg = True
+ else:
+ # need new register
+ closereg = True
+
+ if closereg:
+ genreg['genresval'] = resval
+ genreg['genresmask'] = resmask
+ genreg['genbitsused'] = bits_used
+ genreg['genbasebits'] = bused
+ genreg['genoffset'] = offset + (rnum * addrsep)
+ genreg['gendvrights'] = parse_dvrights(default_sw)
+ genfields.sort(key=get_bits)
+ genreg['fields'] = genfields
+ rnum += 1
+ bpos = 0
+ rlist.append(genreg)
+ top['genrnames'].append(genreg['name'].lower())
+ if len(rlist) == 1:
+ rlist[0]['name'] = mrname
+ top['genrnames'].pop()
+ mreg['genregs'] = rlist
+ top['genrnames'].append(mrname.lower())
+ return error, rnum
+
+
+def make_intr_reg(regs, name, offset, swaccess, hwaccess, desc):
+ intrs = regs['interrupt_list']
+ genreg = {}
+ genreg['name'] = name
+ genreg['desc'] = desc
+ genreg['hwext'] = 'true' if name == 'INTR_TEST' else 'false'
+ genreg['hwqe'] = 'true' if name == 'INTR_TEST' else 'false'
+ genreg['hwre'] = 'false'
+ bits_used = 0
+ genfields = []
+ cur_bit = 0
+ for bit in intrs:
+ newf = {}
+ newf['name'] = bit['name']
+ w = 1
+ if 'width' in bit and bit['width'] != '1':
+ w = int(bit['width'], 0)
+ newf['bits'] = str(cur_bit + w - 1) + ':' + str(cur_bit)
+ newf['bitinfo'] = (((1 << w) - 1) << cur_bit, w, cur_bit)
+ else:
+ newf['bits'] = str(cur_bit)
+ newf['bitinfo'] = (1 << cur_bit, 1, cur_bit)
+ if name == 'INTR_ENABLE':
+ newf['desc'] = 'Enable interrupt when ' + \
+ ('corresponding bit in ' if w > 1 else '') + \
+ '!!INTR_STATE.' + newf['name'] + ' is set'
+ elif name == 'INTR_TEST':
+ newf['desc'] = 'Write 1 to force ' + \
+ ('corresponding bit in ' if w > 1 else '') + \
+ '!!INTR_STATE.' + newf['name'] + ' to 1'
+ else:
+ newf['desc'] = bit['desc']
+ newf['swaccess'] = swaccess
+ swacc_info = swaccess_permitted[swaccess]
+ newf['genswaccess'] = swacc_info[1]
+ newf['genswwraccess'] = swacc_info[2]
+ newf['genswrdaccess'] = swacc_info[3]
+ newf['hwaccess'] = hwaccess
+ hwacc_info = hwaccess_permitted[hwaccess]
+ newf['genhwaccess'] = hwacc_info[1]
+ newf['genhwqe'] = True if name == 'INTR_TEST' else False
+ newf['genhwre'] = False
+ newf['genresval'] = 0
+ newf['genresvalx'] = False
+
+ bits_used = bits_used | (1 << cur_bit)
+ cur_bit += 1
+ genfields.append(newf)
+
+ genreg['genresval'] = 0
+ genreg['genresmask'] = bits_used
+ genreg['genbitsused'] = bits_used
+ genreg['genoffset'] = offset
+ genreg['gendvrights'] = parse_dvrights(swaccess)
+ genreg['fields'] = genfields
+ genreg['regwen'] = ''
+ regs['genrnames'].append(name.lower())
+ return genreg
+
+
+def make_intr_regs(regs, offset, addrsep, fullwidth):
+ iregs = []
+ intrs = regs['interrupt_list']
+ if len(intrs) > fullwidth:
+ log.error('More than ' + str(fullwidth) + ' interrupts in list')
+ return iregs, 1
+
+ iregs.append(
+ make_intr_reg(regs, 'INTR_STATE', offset, 'rw1c', 'hrw',
+ 'Interrupt State Register'))
+ iregs.append(
+ make_intr_reg(regs, 'INTR_ENABLE', offset + addrsep, 'rw', 'hro',
+ 'Interrupt Enable Register'))
+ iregs.append(
+ make_intr_reg(regs, 'INTR_TEST', offset + 2 * addrsep, 'wo', 'hro',
+ 'Interrupt Test Register'))
+ return iregs, 0
+
+
+def validate_window(win, offset, regwidth, top):
+ error = 0
+
+ if not 'name' in win:
+ name = "Window at +" + hex(offset)
+ else:
+ name = win['name']
+ if name.lower() in top['genrnames']:
+ error += 1
+ log.error("Window at +" + hex(offset) + " duplicate name " + name)
+ else:
+ top['genrnames'].append(name.lower())
+
+ error += check_keys(win, window_required, window_optional, window_added,
+ name)
+
+ # if there was an error before this then can't trust anything!
+ if error > 0:
+ log.info(name + "@" + hex(offset) + " " + str(error) +
+ " top level errors. Window will be ignored.")
+ return error, offset
+
+ # optional flags
+ unusual = 'unusual' in win and win['unusual'].lower() == "true"
+ noalign = 'noalign' in win and win['noalign'].lower() == "true"
+ win['genbyte-write'] = ('byte-write' in win and
+ win['byte-write'].lower() == "true")
+
+ if 'validbits' in win:
+ wid, err = check_int(win['validbits'], name + " validbits")
+ if err:
+ error += err
+ wid = regwidth
+ if wid > regwidth:
+ error += 1
+ log.error(name + ": validbits " + str(wid) +
+ " is greater than regwidth (" + str(regwidth) + ").")
+ wid = regwidth
+ win['genvalidbits'] = wid
+ else:
+ win['genvalidbits'] = regwidth
+
+ winitems, err = check_int(win['items'], name + " items")
+ if err:
+ error += err
+ winitems = 4
+ # convert items to bytes
+ winsize = winitems * (regwidth // 8)
+ # if size is not a power of two, po2_size is next po2 larger
+ po2_size = 1 << (winsize.bit_length() - 1)
+ if winsize != po2_size:
+ # the -1 above was wrong if not a power of two
+ po2_size = po2_size << 1
+ if not unusual:
+ log.warn(name + ": Unusual: Size " + str(winitems) +
+ " is not a power of 2.")
+
+ if noalign:
+ genoff = offset
+ nextoff = offset + winsize
+ else:
+ # Align to ensure base address of first item in window has
+ # all zeros in the low bits
+ if (offset & (po2_size - 1)) != 0:
+ genoff = (offset | (po2_size - 1)) + 1
+ else:
+ genoff = offset
+ nextoff = genoff + winsize
+ win['genoffset'] = genoff
+
+ swaccess = win['swaccess']
+ if (not swaccess in swaccess_permitted):
+ log.warn(name + ": Bad window swaccess value " + swaccess)
+ swaccess = "wo"
+ swacc_info = swaccess_permitted[swaccess]
+ win['genswaccess'] = swacc_info[1]
+ win['genswwraccess'] = swacc_info[2]
+ win['genswrdaccess'] = swacc_info[3]
+ if not swacc_info[4] and not unusual:
+ log.warn(name + ": Unusual: access type for a window " + swaccess)
+
+ return error, nextoff
+
+
+def check_wen_regs(regs):
+ error = 0
+ for x in regs['genwennames']:
+ if not x.lower() in regs['genrnames']:
+ error += 1
+ log.error(x + " used as regwen but is not defined")
+ else:
+ for reg in regs['registers']:
+ if ('name' in reg):
+ if (reg['name'] == x):
+ break
+
+ if reg['genbitsused'] != 1:
+ error += 1
+ log.error(x + " used as regwen fails requirement to only " +
+ "define bit 0")
+ elif reg['genresval'] != 1:
+ error += 1
+ log.error(x + " used as regwen fails requirement to default " +
+ "to 1")
+ elif reg['fields'][0]['genswaccess'] != SwAccess.W1C:
+ error += 1
+ log.error(x + " used as regwen fails requirement to be " +
+ "rw1c")
+ return error
+
+
+def validate(regs):
+ if not 'name' in regs:
+ log.error("Component has no name. Aborting.")
+ return 1
+ component = regs['name']
+
+ error = check_keys(regs, top_required, top_optional, top_added, component)
+ if (error > 0):
+ log.error("Component has top level errors. Aborting.")
+ return error
+ regs['genrnames'] = []
+ regs['genwennames'] = []
+ error = 0
+
+ if 'regwidth' in regs:
+ fullwidth, ierr = check_int(regs['regwidth'], "regwidth")
+ if ierr:
+ fullwidth = 32
+ error += 1
+ else:
+ fullwidth = 32
+ log.warning('regwidth not specified, assuming 32.')
+ regs['regwidth'] = str(fullwidth)
+
+ if ((fullwidth % 8) != 0):
+ addrsep = (fullwidth // 8) + 1
+ log.warning("regwidth is not a multiple of 8 bits!")
+ else:
+ addrsep = fullwidth // 8
+
+ offset = 0
+ autoregs = []
+
+ # auto header generation would go here and update autoregs
+
+ if 'no_auto_intr_regs' in regs:
+ no_autoi, err = check_bool(
+ regs['no_auto_intr_regs'], 'no_auto_intr_regs')
+ if err:
+ error += 1
+ else:
+ no_autoi = False
+ if 'interrupt_list' in regs and not 'genautoregs' in regs and not no_autoi:
+ iregs, err = make_intr_regs(regs, offset, addrsep, fullwidth)
+ error += err
+ autoregs.extend(iregs)
+ offset += addrsep * len(iregs)
+
+ for x in regs['registers']:
+ ck_err = check_zero_one_key(x, list_optone, "At " + hex(offset))
+ if ck_err != 0:
+ error += ck_err
+ continue
+
+ if 'reserved' in x:
+ nreserved, ierr = check_int(x['reserved'],
+ "Reserved at " + hex(offset))
+ if ierr:
+ error += 1
+ else:
+ offset = offset + (addrsep * nreserved)
+ continue
+
+ if 'skipto' in x:
+ skipto, ierr = check_int(x['skipto'], "skipto at " + hex(offset))
+ if ierr:
+ error += 1
+ elif (skipto <= offset):
+ log.error("{skipto " + x['skipto'] + "} at " + hex(offset) +
+ " evaluates as " + hex(skipto) +
+ " which would move backwards")
+ error += 1
+ elif (skipto % addrsep) != 0:
+ log.error("{skipto " + x['skipto'] + "} at " + hex(offset) +
+ " evaluates as " + hex(skipto) +
+ " which is not a multiple of the register size " +
+ str(addrsep))
+ error += 1
+ else:
+ offset = skipto
+ continue
+
+ if 'sameaddr' in x:
+ for sareg in x['sameaddr']:
+ error += validate_register(sareg, offset, fullwidth, regs)
+ offset += addrsep
+ continue
+
+ if 'window' in x:
+ err, offset = validate_window(x['window'], offset, fullwidth, regs)
+ error += err
+ continue
+
+ if 'multireg' in x:
+ err, n = validate_multi(x['multireg'], offset, addrsep, fullwidth,
+ regs)
+ error += err
+ offset += addrsep * n
+ continue
+
+ error += validate_register(x, offset, fullwidth, regs)
+ offset += addrsep
+ regs['gennextoffset'] = offset
+ # make the right thing happen if now exactly on power of 2
+ if (offset > 0): offset -= 1
+ regs['gensize'] = 1 << offset.bit_length()
+
+ error += check_wen_regs(regs)
+
+ if autoregs:
+ # auto generated registers go at the front
+ autoregs.extend(regs['registers'])
+ regs['registers'] = autoregs
+ regs['genautoregs'] = True
+
+ log.info("Validated, size = " + hex(regs['gensize']) + " errors=" +
+ str(error) + " names are " + str(regs['genrnames']))
+ if (error > 0):
+ log.error("Register description had " + str(error) + " error" +
+ "s" if error > 1 else "")
+ return error