Skip to content

Commit

Permalink
Implemented the majority of the HTML generator
Browse files Browse the repository at this point in the history
  • Loading branch information
jvanstraten committed Aug 19, 2019
1 parent cfe8763 commit 6db4558
Show file tree
Hide file tree
Showing 12 changed files with 685 additions and 279 deletions.
1 change: 1 addition & 0 deletions vhdmmio/config/behavior/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def behaviors():
def behavior(name, brief, level=0):
"""Decorator generator which registers a behavior configurable."""
def decorator(cls):
cls.__str__ = lambda _: name
_BEHAVIOR_LIST.append((name, cls, brief, level))
return cls
return decorator
Expand Down
2 changes: 1 addition & 1 deletion vhdmmio/config/interrupt.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ def active():
yield 'low', 'the interrupt is level/strobe-sensitive, active-low.'
yield 'rising', 'the interrupt is rising-edge sensitive.'
yield 'falling', 'the interrupt is falling-edge sensitive.'
yield 'any-edge', 'the interrupt is sensitive to any edge.'
yield 'edge', 'the interrupt is sensitive to any edge.'
14 changes: 14 additions & 0 deletions vhdmmio/core/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,17 @@ def doc_iter(self):
read_ob = self.read.get(address, None)
write_ob = self.write.get(address, None)
yield subaddresses, address_repr, read_ob, write_ob

def doc_represent_address(self, address):
"""Formats documentation for the given internal address. Returns a
tuple of the formatted address and a list of string representations of
any additional match conditions."""
bus_address = None
conditions = []
for signal, subaddress in self.signals.split_address(address).items():
subaddress = subaddress.doc_represent(signal.width)
if signal is AddressSignalMap.BUS:
bus_address = subaddress
elif subaddress != '-':
conditions.append('%s=%s' % (signal.name, subaddress))
return bus_address, conditions
7 changes: 7 additions & 0 deletions vhdmmio/core/behavior/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,10 @@ def field(self):
def bus(self):
"""The behavior of the bus for this field."""
return self._bus

@property
def doc_reset(self):
"""The reset value as printed in the documentation as an integer, or
`None` if the field is driven by a signal and thus does not have a
register to reset."""
return None
21 changes: 21 additions & 0 deletions vhdmmio/core/behavior/interrupt.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,24 @@ def interrupt(self):
if self._interrupt is None:
raise ValueError('interrupt not yet attached')
return self._interrupt

@property
def doc_reset(self):
"""The reset value as printed in the documentation as an integer, or
`None` if the field is driven by a signal and thus does not have a
register to reset."""
flag = 0
if self.interrupt.level_sensitive and self.interrupt.enabled_after_reset:
flag = None
if self.cfg.mode == 'raw':
return None
if self.cfg.mode == 'enable':
return int(self.interrupt.enabled_after_reset)
if self.cfg.mode == 'flag':
return flag
if self.cfg.mode == 'unmask':
return int(self.interrupt.unmasked_after_reset)
assert self.cfg.mode == 'masked'
if self.interrupt.unmasked_after_reset:
return flag
return 0
15 changes: 15 additions & 0 deletions vhdmmio/core/behavior/primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ def __init__(self, resources, field, behavior_cfg, read_allow_cfg, write_allow_c
raise ValueError('external status fields cannot be combined with an '
'internal monitor signal')

# Figure out the reset value for the documentation.
if is_int_stat or is_ext_stat:
self._doc_reset = None
elif behavior_cfg.reset is None:
self._doc_reset = 0
else:
self._doc_reset = int(behavior_cfg.reset)

# The `stream` write mode (stream to MMIO) cannot be combined with
# hardware read, because both produce a `data` port (in opposite
# direction). The `valid` signal of the full hardware read interface
Expand Down Expand Up @@ -256,3 +264,10 @@ def monitor_internal(self):
their field index. Otherwise, the signal takes the shape of the field
itself."""
return self._monitor_internal

@property
def doc_reset(self):
"""The reset value as printed in the documentation as an integer, or
`None` if the field is driven by a signal and thus does not have a
register to reset."""
return self._doc_reset
24 changes: 21 additions & 3 deletions vhdmmio/core/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ..config import MetadataConfig
from .mixins import Named, Unique, Accessed
from .address import AddressSignalMap

class FieldMapping(Unique):
"""Represents a field mapping within a `Block`."""
Expand Down Expand Up @@ -32,14 +33,13 @@ def high(self):

@property
def low(self):
"""The low bit in the field mapped by this mapping, or `None` if the
field is a single bit."""
"""The low bit in the field mapped by this mapping."""
return self._low

@property
def offset(self):
"""The offset of the mapping's low bit in the bus word."""
return self._low
return self._offset

@property
def read(self):
Expand Down Expand Up @@ -131,6 +131,10 @@ def __init__(self, resources, register, index, count):
if self.can_write():
resources.block_addresses.write_set(self.internal_address, self)

# Figure out our bus address.
self._address = resources.block_addresses.signals.split_address(
self._internal_address)[AddressSignalMap.BUS]

# If there are multiple blocks, register each block in the register
# namespace as well. The blocks will get their own definitions and such
# in the generated software, so they need to be unique.
Expand Down Expand Up @@ -224,12 +228,26 @@ def offset(self):
register bit indices."""
return self._offset

@property
def address(self):
"""The bus address for this logical register as a `MaskedAddress`. If
this register has multiple blocks, this corresponds to the address of
the first block, which is the least significant block in little-endian
mode, or the most significant in big-endian mode."""
return self._address

@property
def internal_address(self):
"""Internal address of this block (concatenation of the bus address and
any other match conditions)."""
return self._internal_address

def doc_address(self):
"""Formats documentation for this block's internal address. Returns a
tuple of the formatted address and a list of string representations of
any additional match conditions."""
return self.register.regfile.doc_represent_address(self.internal_address)

@property
def mappings(self):
"""Tuple of all the `FieldMapping`s, ordered from read to write and
Expand Down
2 changes: 1 addition & 1 deletion vhdmmio/core/interrupt.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def active(self):
- `'low'`: the interrupt is level/strobe-sensitive, active-low;
- `'rising'`: the interrupt is rising-edge sensitive;
- `'falling'`: the interrupt is falling-edge sensitive;
- `'any-edge'`: the interrupt is sensitive to any edge.
- `'edge'`: the interrupt is sensitive to any edge.
"""
return self.cfg.active

Expand Down
22 changes: 19 additions & 3 deletions vhdmmio/core/logical_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,17 @@ def find_reg_meta(fields):

# Handle the read-write case with identical metadata.
if read_meta.name == write_meta.name and read_meta.mnemonic == write_meta.mnemonic:
read_reg = write_reg = LogicalRegister(
resources, regfile, read_meta, 'R/W', read_fields | write_fields)
if not write_fields:
read_reg = LogicalRegister(
resources, regfile, read_meta, 'R/O', read_fields)
write_reg = None
elif not read_fields:
read_reg = None
write_reg = LogicalRegister(
resources, regfile, read_meta, 'W/O', write_fields)
else:
read_reg = write_reg = LogicalRegister(
resources, regfile, read_meta, 'R/W', read_fields | write_fields)
else:
# Construct the read-only register, if any.
read_reg = (
Expand Down Expand Up @@ -121,7 +130,8 @@ def __init__(self, resources, regfile, metadata, mode, fields):
# Figure out the number of blocks in the logical register.
bus_width = regfile.cfg.features.bus_width
msb = max((field.bitrange.high for field in self.fields))
num_blocks = (msb + bus_width - 1) // bus_width
num_blocks = (msb + bus_width) // bus_width
assert num_blocks

# Construct the blocks.
self._blocks = tuple((
Expand Down Expand Up @@ -235,6 +245,12 @@ def internal_address(self):
big-endian mode."""
return self.fields[0].internal_address

def doc_address(self):
"""Formats documentation for this register's internal address. Returns
a tuple of the formatted address and a list of string representations
of any additional match conditions."""
return self.regfile.doc_represent_address(self.internal_address)

@property
def endianness(self):
"""Returns the endianness of this register, either `'little'` or
Expand Down
6 changes: 6 additions & 0 deletions vhdmmio/core/register_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ def doc_iter_registers(self):
write-only/read-only."""
return self._resources.addresses.doc_iter()

def doc_represent_address(self, internal_address):
"""Formats documentation for the given internal address. Returns a
tuple of the formatted address and a list of string representations of
any additional match conditions."""
return self._resources.addresses.doc_represent_address(internal_address)

@property
def interrupts(self):
"""Returns the interrupts of this register file as a tuple."""
Expand Down
Loading

0 comments on commit 6db4558

Please # to comment.