[reggen] Define a Window type to represent (memory) windows
This also removes the Window class from reggen/data.py: we'll use the
new Window class everywhere.
There's a bit of code to handle window tags at the bottom of
uvm_reg.sv.tpl that goes away completely (windows don't have tags, so
this was dead code).
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/util/shared/mem_layout.py b/hw/ip/otbn/util/shared/mem_layout.py
index e86b2ad..23d4b60 100644
--- a/hw/ip/otbn/util/shared/mem_layout.py
+++ b/hw/ip/otbn/util/shared/mem_layout.py
@@ -20,41 +20,30 @@
from typing import Dict, List, Optional, Tuple
-from .otbn_reggen import HjsonDict, load_registers
+from .otbn_reggen import load_registers, Window
# A window is represented as (offset, size)
_Window = Tuple[int, int]
def extract_windows(reg_byte_width: int,
- registers: List[HjsonDict]) -> Dict[str, _Window]:
+ registers: List[object]) -> Dict[str, _Window]:
'''Make sense of the list of register definitions and extract memories'''
# Conveniently, reggen's validate method stores 'genoffset' (the offset to
# the start) for each window, so we can just look that up.
windows = {}
- for reg in registers:
- if not isinstance(reg, dict):
+ for entry in registers:
+ if not isinstance(entry, Window):
continue
- window = reg.get('window')
- if window is None:
- continue
+ name = entry.name or 'Window at +{:#x}'.format(entry.offset)
- assert isinstance(window, dict)
+ # Should be guaranteed by RegBlock constructor
+ assert name not in windows
- offset = window['genoffset']
- assert isinstance(offset, int)
-
- items = int(window['items'])
- window_name = window.get('name', 'Window at +{:#x}'.format(offset))
- assert isinstance(window_name, str)
- if window_name in windows:
- raise ValueError('Duplicate window entry with name {!r}.'
- .format(window_name))
-
- windows[window_name] = (offset, items * reg_byte_width)
+ windows[name] = (entry.offset, entry.size_in_bytes)
return windows
diff --git a/hw/ip/otbn/util/shared/otbn_reggen.py b/hw/ip/otbn/util/shared/otbn_reggen.py
index 2c72ab0..588b1e4 100644
--- a/hw/ip/otbn/util/shared/otbn_reggen.py
+++ b/hw/ip/otbn/util/shared/otbn_reggen.py
@@ -6,13 +6,10 @@
import os
import sys
-from typing import List, Mapping, Optional, Tuple
+from typing import List, Optional, Tuple
import hjson # type: ignore
-# An hjson dict is actually an OrderedDict, but typing/mypy support for that is
-# a little spotty, so we'll use a generic Mapping type.
-HjsonDict = Mapping[str, object]
# We use reggen to read the hjson file. Since that lives somewhere completely
# different from this script (and there aren't __init__.py files scattered all
@@ -23,8 +20,9 @@
'..', '..', '..', '..', '..', 'util')
sys.path = [_UTIL_PATH] + _OLD_SYS_PATH
from reggen.validate import checking_dict, validate # type: ignore
- import reggen.register # type: ignore
import reggen.field # type: ignore
+ import reggen.register # type: ignore
+ import reggen.window # type: ignore
finally:
sys.path = _OLD_SYS_PATH
@@ -32,14 +30,16 @@
# transitively without having to mess around with sys.path.
Register = reggen.register.Register
Field = reggen.field.Field
+Window = reggen.window.Window
-_LR_RETVAL = None # type: Optional[Tuple[int, List[HjsonDict]]]
+_LR_RETVAL = None # type: Optional[Tuple[int, List[object]]]
-def load_registers() -> Tuple[int, List[HjsonDict]]:
+def load_registers() -> Tuple[int, List[object]]:
'''Load otbn.hjson with reggen
- Return its register width and list of registers. Memoized.
+ Returns (width, regs) where width is the register width and regs is a
+ list of Register, MultiRegister or Window objects. Memoized.
'''
global _LR_RETVAL
@@ -74,6 +74,5 @@
# dictionaries, so we can assert the type safely.
registers = obj['registers']
assert isinstance(registers, list)
-
_LR_RETVAL = (reg_byte_width, registers)
return _LR_RETVAL
diff --git a/util/reggen/data.py b/util/reggen/data.py
index 3c03b7c..db05f31 100644
--- a/util/reggen/data.py
+++ b/util/reggen/data.py
@@ -18,15 +18,6 @@
return name[0:match.start()]
-class Window():
- def __init__(self):
- self.base_addr = 0
- self.byte_write = 0
- self.limit_addr = 0
- self.n_bits = 0
- self.tags = []
-
-
class Block():
def __init__(self):
self.width = 32
diff --git a/util/reggen/gen_cheader.py b/util/reggen/gen_cheader.py
index 87b2d8c..29e7023 100644
--- a/util/reggen/gen_cheader.py
+++ b/util/reggen/gen_cheader.py
@@ -14,6 +14,7 @@
from .register import Register
from .multi_register import MultiRegister
+from .window import Window
def genout(outfile, msg):
@@ -143,15 +144,15 @@
def gen_cdefine_window(outstr, win, comp, regwidth, rnames, existing_defines):
- wname = win['name']
- offset = win['genoffset']
+ wname = win.name or "Window at + {:#x}".format(win.offset)
+ offset = win.offset
- genout(outstr, format_comment('Memory area: ' + first_line(win['desc'])))
+ genout(outstr, format_comment('Memory area: ' + first_line(win.desc)))
defname = as_define(comp + '_' + wname)
genout(
outstr,
gen_define(defname + '_REG_OFFSET', [], hex(offset), existing_defines))
- items = int(win['items'])
+ items = win.items
genout(
outstr,
gen_define(defname + '_SIZE_WORDS', [], str(items), existing_defines))
@@ -160,7 +161,7 @@
outstr,
gen_define(defname + '_SIZE_BYTES', [], str(items), existing_defines))
- wid = win['genvalidbits']
+ wid = win.validbits
if (wid != regwidth):
mask = (1 << wid) - 1
genout(outstr,
@@ -339,9 +340,8 @@
existing_defines)
continue
- assert isinstance(x, dict)
- if 'window' in x:
- gen_cdefine_window(outstr, x['window'], component, regwidth,
+ if isinstance(x, Window):
+ gen_cdefine_window(outstr, x, component, regwidth,
rnames, existing_defines)
continue
diff --git a/util/reggen/gen_html.py b/util/reggen/gen_html.py
index 18d289a..9e3a978 100644
--- a/util/reggen/gen_html.py
+++ b/util/reggen/gen_html.py
@@ -9,6 +9,7 @@
from .multi_register import MultiRegister
from .register import Register
+from .window import Window
def genout(outfile, msg):
@@ -217,18 +218,27 @@
def gen_html_window(outfile, win, comp, regwidth, rnames, toc, toclvl):
- wname = win['name']
- offset = win['genoffset']
- genout(
- outfile, '<table class="regdef" id="Reg_' + wname.lower() + '">\n'
- '<tr><th class="regdef"><div>' + comp + '.' + wname + ' @ + ' +
- hex(offset) + '</div><div>' + win['items'] + ' item ' +
- win['swaccess'] + ' window</div><div>Byte writes are ' +
- ('' if win['genbyte-write'] else '<i>not</i> ') +
- 'supported</div></th></tr>\n')
+ wname = win.name or '(unnamed window)'
+ offset = win.offset
+ genout(outfile,
+ '<table class="regdef" id="Reg_{lwname}">\n'
+ ' <tr>\n'
+ ' <th class="regdef">\n'
+ ' <div>{comp}.{wname} @ + {off:#x}</div>\n'
+ ' <div>{items} item {swaccess} window</div>\n'
+ ' <div>Byte writes are {byte_writes}supported</div>\n'
+ ' </th>\n'
+ ' </tr>\n'
+ .format(comp=comp,
+ wname=wname,
+ lwname=wname.lower(),
+ off=offset,
+ items=win.items,
+ swaccess=win.swaccess.key,
+ byte_writes=('' if win.byte_write else '<i>not</i> ')))
genout(outfile, '<tr><td><table class="regpic">')
genout(outfile, '<tr><td width="10%"></td>')
- wid = win['genvalidbits']
+ wid = win.validbits
for x in range(regwidth - 1, -1, -1):
if x == regwidth - 1 or x == wid - 1 or x == 0:
@@ -236,7 +246,7 @@
else:
genout(outfile, '<td class="bitnum"></td>')
genout(outfile, '</tr>')
- tblmax = int(win['items']) - 1
+ tblmax = win.items - 1
for x in [0, 1, 2, tblmax - 1, tblmax]:
if x == 2:
genout(
@@ -260,7 +270,7 @@
genout(outfile, '</tr>')
genout(outfile, '</td></tr></table>')
genout(outfile,
- '<tr>{}</tr>'.format(render_td(win['desc'], rnames, 'regde')))
+ '<tr>{}</tr>'.format(render_td(win.desc, rnames, 'regde')))
genout(outfile, "</table>\n<br>\n")
if toc is not None:
toc.append((toclvl, comp + "." + wname, "Reg_" + wname.lower()))
@@ -289,10 +299,8 @@
gen_html_register(outfile, reg, component, regwidth, rnames,
toclist, toclevel)
continue
-
- assert isinstance(x, dict)
- if 'window' in x:
- gen_html_window(outfile, x['window'], component, regwidth, rnames,
+ if isinstance(x, Window):
+ gen_html_window(outfile, x, component, regwidth, rnames,
toclist, toclevel)
continue
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index 4196529..7a35652 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -11,9 +11,10 @@
from pkg_resources import resource_filename
from .access import HwAccess, SwRdAccess, SwWrAccess
-from .data import Block, Window
+from .data import Block
from .register import Register
from .multi_register import MultiRegister
+from .window import Window
def escape_name(name):
@@ -27,22 +28,6 @@
return default
-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.byte_write = obj["genbyte-write"]
- win.limit_addr = obj["genoffset"] + int(obj["items"]) * (width // 8)
- win.dvrights = obj["swaccess"]
- win.n_bits = obj["genvalidbits"]
-
- # TODO: Generate warnings of `unusual`
- return win
-
-
def json_to_reg(obj):
"""Converts JSON OrderedDict into structure having useful information for
Template to use.
@@ -74,11 +59,9 @@
if isinstance(r, Register) or isinstance(r, MultiRegister):
block.regs.append(r)
continue
- assert isinstance(r, dict)
- if 'window' in r:
- win = parse_win(r['window'], block.width)
- if win is not None:
- block.wins.append(win)
+
+ if isinstance(r, Window):
+ block.wins.append(r)
continue
# Last offset and calculate space
diff --git a/util/reggen/gen_selfdoc.py b/util/reggen/gen_selfdoc.py
index 9df2da3..8e46ac9 100644
--- a/util/reggen/gen_selfdoc.py
+++ b/util/reggen/gen_selfdoc.py
@@ -6,7 +6,9 @@
"""
from .access import SWACCESS_PERMITTED, HWACCESS_PERMITTED
-from reggen import validate, enum_entry, field, register, multi_register
+from reggen import (validate,
+ enum_entry, field,
+ register, multi_register, window)
def genout(outfile, msg):
@@ -291,12 +293,10 @@
genout(outfile, window_intro)
doc_tbl_head(outfile, 1)
- for x in validate.window_required:
- doc_tbl_line(outfile, x, 'r', validate.window_required[x])
- for x in validate.window_optional:
- doc_tbl_line(outfile, x, 'o', validate.window_optional[x])
- for x in validate.window_added:
- doc_tbl_line(outfile, x, 'a', validate.window_added[x])
+ for k, v in window.REQUIRED_FIELDS.items():
+ doc_tbl_line(outfile, k, 'r', v)
+ for k, v in window.OPTIONAL_FIELDS.items():
+ doc_tbl_line(outfile, k, 'o', v)
genout(outfile, multi_intro)
doc_tbl_head(outfile, 1)
diff --git a/util/reggen/lib.py b/util/reggen/lib.py
index 7b27077..8253c03 100644
--- a/util/reggen/lib.py
+++ b/util/reggen/lib.py
@@ -226,6 +226,12 @@
def expand_parameter(params: List[Dict[str, object]],
value: str,
when: str) -> int:
+ # Check whether the 'parameter' is already an integer: if so, return that.
+ try:
+ return int(value, 0)
+ except ValueError:
+ pass
+
found = None
for param in params:
if param['name'] == value:
diff --git a/util/reggen/reg_pkg.sv.tpl b/util/reggen/reg_pkg.sv.tpl
index 58ef7b5..75da1a1 100644
--- a/util/reggen/reg_pkg.sv.tpl
+++ b/util/reggen/reg_pkg.sv.tpl
@@ -264,8 +264,13 @@
% if len(block.wins) > 0:
// Window parameter
% for i,w in enumerate(block.wins):
- parameter logic [BlockAw-1:0] ${ublock}_${w.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % w.base_addr};
- parameter logic [BlockAw-1:0] ${ublock}_${w.name.upper()}_SIZE = ${block.addr_width}'h ${"%x" % (w.limit_addr - w.base_addr)};
+<%
+ win_pfx = '{}_{}'.format(ublock, w.name.upper())
+ base_txt_val = "{}'h {:x}".format(block.addr_width, w.offset)
+ size_txt_val = "{}'h {:x}".format(block.addr_width, w.size_in_bytes)
+%>\
+ parameter logic [BlockAw-1:0] ${win_pfx}_OFFSET = ${base_txt_val};
+ parameter logic [BlockAw-1:0] ${win_pfx}_SIZE = ${size_txt_val};
% endfor
% endif
diff --git a/util/reggen/reg_top.sv.tpl b/util/reggen/reg_top.sv.tpl
index 2fbdf9b..4eb75c4 100644
--- a/util/reggen/reg_top.sv.tpl
+++ b/util/reggen/reg_top.sv.tpl
@@ -109,11 +109,15 @@
// TODO: Can below codes be unique case () inside ?
% for i,w in enumerate(block.wins):
- % if w.limit_addr == 2**block.addr_width:
- if (tl_i.a_address[AW-1:0] >= ${w.base_addr}) begin
- // Exceed or meet the address range. Removed the comparison of limit addr ${"'h %x" % w.limit_addr}
+<%
+ base_addr = w.offset
+ limit_addr = w.offset + w.size_in_bytes
+%>\
+ % if limit_addr == 2**block.addr_width:
+ if (tl_i.a_address[AW-1:0] >= ${base_addr}) begin
+ // Exceed or meet the address range. Removed the comparison of limit addr 'h ${'{:x}'.format(limit_addr)}
% else:
- if (tl_i.a_address[AW-1:0] >= ${w.base_addr} && tl_i.a_address[AW-1:0] < ${w.limit_addr}) begin
+ if (tl_i.a_address[AW-1:0] >= ${base_addr} && tl_i.a_address[AW-1:0] < ${limit_addr}) begin
% endif
reg_steer = ${i};
end
diff --git a/util/reggen/uvm_reg.sv.tpl b/util/reggen/uvm_reg.sv.tpl
index d26746d..28174f9 100644
--- a/util/reggen/uvm_reg.sv.tpl
+++ b/util/reggen/uvm_reg.sv.tpl
@@ -155,9 +155,9 @@
% for w in block.wins:
<%
mem_name = w.name.lower()
- mem_right = w.dvrights.upper()
- mem_n_bits = w.n_bits
- mem_size = int((w.limit_addr - w.base_addr) / (mem_n_bits / 8))
+ mem_right = w.swaccess.dv_rights()
+ mem_n_bits = w.validbits
+ mem_size = w.items
%>\
// Class: ${gen_dv.mcname(block, w)}
class ${gen_dv.mcname(block, w)} extends ${dv_base_prefix}_mem;
@@ -286,28 +286,16 @@
% for w in block.wins:
<%
mem_name = w.name.lower()
- mem_right = w.dvrights.upper()
- mem_offset = str(block.width) + "'h" + "%x" % w.base_addr
- mem_n_bits = w.n_bits
- mem_size = int((w.limit_addr - w.base_addr) / (mem_n_bits / 8))
- mem_tags = w.tags
+ mem_right = w.swaccess.dv_rights()
+ mem_offset = str(block.width) + "'h" + "%x" % w.offset
+ mem_n_bits = w.validbits
+ mem_size = w.items
%>\
${mem_name} = ${gen_dv.mcname(block, w)}::type_id::create("${mem_name}");
${mem_name}.configure(.parent(this));
default_map.add_mem(.mem(${mem_name}),
.offset(${mem_offset}),
.rights("${mem_right}"));
- % if mem_tags:
- // create memory tags
- % for mem_tag in mem_tags:
-<%
- tag = mem_tag.split(":")
-%>\
- % if tag[0] == "excl":
- csr_excl.add_excl(${mem_name}.get_full_name(), ${tag[2]}, ${tag[1]});
- % endif
- % endfor
- % endif
% endfor
endfunction : build
diff --git a/util/reggen/validate.py b/util/reggen/validate.py
index 7314cc7..210f05c 100644
--- a/util/reggen/validate.py
+++ b/util/reggen/validate.py
@@ -9,11 +9,12 @@
import re
from collections import OrderedDict
-from .access import SWAccess, HWAccess, SWACCESS_PERMITTED
+from .access import SWAccess, HWAccess
from .bits import Bits
from .field import Field
from .multi_register import MultiRegister
from .register import Register
+from .window import Window
# Routine that can be used for Hjson object_pairs_hook
@@ -377,94 +378,10 @@
"from a base instance."]
}
-# Register keys
-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"],
- '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."
- ],
- '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', "valid 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)"]
-}
-
-# Field keys
-# special case in the code, no name and no desc if only field
-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)"]
-}
-
key_use = {'r': "required", 'o': "optional", 'a': "added by tool"}
-# if not int, check in param_list
-def resolve_value(entry, param_list):
- val, not_int = check_int(entry, "", True)
- err = 0
-
- if not_int:
- param, err = search_param(param_list, entry)
- val = param['default']
- if param['local'] != "true":
- log.warning(
- "It is recommended to define {} as localparam,"
- " since it should not be changed in the design".format(entry))
-
- return int(val), err
-
-
-def _upd_gennames(regs, offset, register):
+def _upd_regnames(regs, offset, register):
genrnames = regs['genrnames']
rname = register.name.lower()
err = 0
@@ -562,7 +479,7 @@
fields=fields,
update_err_alert=None,
storage_err_alert=None)
- _upd_gennames(regs, offset, reg)
+ _upd_regnames(regs, offset, reg)
return reg
@@ -600,92 +517,6 @@
return alert_regs, 0
-def validate_window(win, offset, regwidth, top):
- error = 0
-
- if 'name' not 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.debug(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"
- 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
-
- param, err = resolve_value(win['items'], top['param_list'])
- error += err
-
- winitems, err = check_int(param, name + " items")
-
- if err:
- error += err
- winitems = 4
-
- win['items'] = str(winitems)
-
- # 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.")
-
- # 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 swaccess not 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
-
-
""" Check that terms specified for regwen exist
Regwen are all assumed to be individual registers.
@@ -953,9 +784,24 @@
continue
if 'window' in x:
- err, offset = validate_window(x['window'], offset, fullwidth, regs)
- error += err
- vld_regs.append(x)
+ try:
+ window = Window.from_raw(offset, fullwidth,
+ regs.get('param_list', []),
+ x['window'])
+ vld_regs.append(window)
+ offset = window.offset + window.size_in_bytes
+
+ if window.name is not None:
+ if window.name in regs['genrnames']:
+ log.error('Duplicate window name {!r} at offset {:#x}.'
+ .format(offset, window.name))
+ error += 1
+ regs['genrnames'].append(window.name.lower())
+ except ValueError as err:
+ log.error('Error in window at offset {:#x}: {}'
+ .format(offset, err))
+ error += 1
+
continue
if 'multireg' in x:
@@ -965,7 +811,7 @@
x['multireg'])
vld_regs.append(multi_reg)
for reg in multi_reg.regs:
- error += _upd_gennames(regs, offset, reg)
+ error += _upd_regnames(regs, offset, reg)
offset += addrsep * len(multi_reg.regs)
except ValueError as err:
log.error('Error in multireg at offset {:#x}: {}'
@@ -978,7 +824,7 @@
regs.get('param_list', []),
x)
vld_regs.append(reg)
- error += _upd_gennames(regs, offset, reg)
+ error += _upd_regnames(regs, offset, reg)
except ValueError as err:
log.error('Error in register at offset {:#x}: {}'
.format(offset, err))
diff --git a/util/reggen/window.py b/util/reggen/window.py
new file mode 100644
index 0000000..b151d85
--- /dev/null
+++ b/util/reggen/window.py
@@ -0,0 +1,162 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+from typing import Dict, List, Optional
+
+from .access import SWAccess
+from .lib import (check_keys, check_str, check_bool, check_int,
+ expand_parameter)
+
+REQUIRED_FIELDS = {
+ '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"
+OPTIONAL_FIELDS = {
+ 'name': ['s', "Name of the window"],
+ '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."
+ ],
+ 'unusual': [
+ 's', "True if window has unusual parameters "
+ "(set to prevent Unusual: errors)."
+ "Defaults to false if not present."
+ ]
+}
+
+
+class Window:
+ '''A class representing a memory window'''
+ def __init__(self,
+ name: Optional[str],
+ desc: str,
+ unusual: bool,
+ byte_write: bool,
+ validbits: int,
+ items: int,
+ size_in_bytes: int,
+ offset: int,
+ swaccess: SWAccess):
+ assert 0 < validbits
+ assert 0 < items <= size_in_bytes
+
+ self.name = name
+ self.desc = desc
+ self.unusual = unusual
+ self.byte_write = byte_write
+ self.validbits = validbits
+ self.items = items
+ self.size_in_bytes = size_in_bytes
+ self.offset = offset
+ self.swaccess = swaccess
+
+ # Check that offset has been adjusted so that the first item in the
+ # window has all zeros in the low bits.
+ po2_size = 1 << (self.size_in_bytes - 1).bit_length()
+ assert not (offset & (po2_size - 1))
+
+ @staticmethod
+ def from_raw(offset: int,
+ reg_width: int,
+ params: List[Dict[str, object]],
+ raw: object) -> 'Window':
+ rd = check_keys(raw, 'window',
+ list(REQUIRED_FIELDS.keys()),
+ list(OPTIONAL_FIELDS.keys()))
+
+ r_name = rd.get('name')
+ wind_desc = 'window at offset {:#x}'.format(offset)
+ if r_name is None:
+ name = None
+ else:
+ name = check_str(r_name, wind_desc)
+ wind_desc = '{!r} {}'.format(name, wind_desc)
+
+ desc = check_str(rd['desc'], 'desc field for ' + wind_desc)
+
+ unusual = check_bool(rd.get('unusual', False),
+ 'unusual field for ' + wind_desc)
+ byte_write = check_bool(rd.get('byte-write', False),
+ 'byte-write field for ' + wind_desc)
+
+ validbits = check_int(rd.get('validbits', reg_width),
+ 'validbits field for ' + wind_desc)
+ if validbits <= 0:
+ raise ValueError('validbits field for {} is not positive.'
+ .format(wind_desc))
+ if validbits > reg_width:
+ raise ValueError('validbits field for {} is {}, '
+ 'which is greater than {}, the register width.'
+ .format(wind_desc, validbits, reg_width))
+
+ r_items = check_str(rd['items'], 'items field for ' + wind_desc)
+ items = expand_parameter(params, r_items,
+ 'expanding items field for ' + wind_desc)
+ if items <= 0:
+ raise ValueError("Items field for {} is {}, "
+ "which isn't positive."
+ .format(wind_desc, items))
+
+ assert reg_width % 8 == 0
+ size_in_bytes = items * (reg_width // 8)
+
+ # Round size_in_bytes up to the next power of 2. The calculation is
+ # like clog2 calculations in SystemVerilog, where we start with the
+ # last index, rather than the number of elements.
+ assert size_in_bytes > 0
+ po2_size = 1 << (size_in_bytes - 1).bit_length()
+
+ # A size that isn't a power of 2 is not allowed unless the unusual flag
+ # is set.
+ if po2_size != size_in_bytes and not unusual:
+ raise ValueError('Items field for {} is {}, which gives a size of '
+ '{} bytes. This is not a power of 2 (next power '
+ 'of 2 is {}). If you want to do this even so, '
+ 'set the "unusual" flag.'
+ .format(wind_desc, items,
+ size_in_bytes, po2_size))
+
+ # Adjust offset if necessary to make sure the base address of the first
+ # item in the window has all zeros in the low bits.
+ addr_mask = po2_size - 1
+ if offset & addr_mask:
+ offset = (offset | addr_mask) + 1
+ offset = offset
+
+ swaccess = SWAccess(wind_desc, rd['swaccess'])
+ if not (swaccess.value[4] or unusual):
+ raise ValueError('swaccess field for {} is {}, which is an '
+ 'unusual access type for a window. If you want '
+ 'to do this, set the "unusual" flag.'
+ .format(wind_desc, swaccess.key))
+
+ return Window(name, desc, unusual, byte_write,
+ validbits, items, size_in_bytes, offset, swaccess)
+
+ def _asdict(self) -> Dict[str, object]:
+ rd = {
+ 'desc': self.desc,
+ 'items': self.items,
+ 'swaccess': self.swaccess.key,
+ 'byte-write': self.byte_write,
+ 'validbits': self.validbits,
+ 'unusual': self.unusual
+ }
+ if self.name is not None:
+ rd['name'] = self.name
+
+ return {'window': rd}
diff --git a/util/topgen.py b/util/topgen.py
index ed48deb..52cb48e 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -19,10 +19,10 @@
from mako.template import Template
import tlgen
-from reggen import gen_dv, gen_rtl, validate
+from reggen import access, gen_dv, gen_rtl, validate, window
from topgen import amend_clocks, get_hjsonobj_xbars
from topgen import intermodule as im
-from topgen import merge_top, search_ips, check_flash, validate_top
+from topgen import merge_top, search_ips, validate_top
from topgen.c import TopGenC
# Common header for generated files
@@ -830,21 +830,28 @@
for ip_obj in ip_objs:
top_block.blocks.append(gen_rtl.json_to_reg(ip_obj))
+ assert top_block.width % 8 == 0
+ reg_width_in_bytes = top_block.width // 8
+
# add memories
- if "memory" in top.keys():
- for item in list(top["memory"]):
- mem = gen_rtl.Window()
- mem.name = item["name"]
- mem.base_addr = int(item["base_addr"], 0)
- mem.limit_addr = int(item["base_addr"], 0) + int(item["size"], 0)
- mem.byte_write = ('byte_write' in item and
- item["byte_write"].lower() == "true")
- if "swaccess" in item.keys():
- mem.dvrights = item["swaccess"]
- else:
- mem.dvrights = "RW"
- mem.n_bits = top_block.width
- top_block.wins.append(mem)
+ for item in list(top.get("memory", [])):
+ byte_write = ('byte_write' in item and
+ item["byte_write"].lower() == "true")
+ size_in_bytes = int(item['size'], 0)
+ num_regs = size_in_bytes // reg_width_in_bytes
+ swaccess = access.SWAccess('top-level memory',
+ item.get('swaccess', 'rw'))
+
+ mem = window.Window(name=item['name'],
+ desc='(generated from top-level)',
+ unusual=False,
+ byte_write=byte_write,
+ validbits=top_block.width,
+ items=num_regs,
+ size_in_bytes=size_in_bytes,
+ offset=int(item["base_addr"], 0),
+ swaccess=swaccess)
+ top_block.wins.append(mem)
# get sub-block base addresses, instance names from top cfg
for block in top_block.blocks:
@@ -854,7 +861,7 @@
# sort by the base_addr of 1st instance of the block
top_block.blocks.sort(key=lambda block: next(iter(block.base_addr))[1])
- top_block.wins.sort(key=lambda win: win.base_addr)
+ top_block.wins.sort(key=lambda win: win.offset)
# generate the top ral model with template
gen_dv.gen_ral(top_block, dv_base_prefix, str(out_path))