prop reference infrastructure, and other things

This commit is contained in:
Alex Mykyta
2021-07-16 12:43:58 -07:00
parent 0d5b663f98
commit 5f2319860f
14 changed files with 338 additions and 68 deletions

View File

@@ -27,3 +27,19 @@ the template would do something like:
{{output_signal(field, "anded")}} = &{{field_value(field)}}; {{output_signal(field, "anded")}} = &{{field_value(field)}};
Basically, i'd define a ton of helper functions that return the signal identifier. Basically, i'd define a ton of helper functions that return the signal identifier.
================================================================================
Dev Todo list
================================================================================
- Lots to do in HWIF layer
- reorg base.py+struct.py.
- I have no desire to do flattened I/O. No need to split off a base class
- User Signals
Generate these in the io struct? I forget what I decided
- get_input/output_identifier() functions
- Field logic
Basically need all aspects of the dereferencer to be done first

View File

@@ -0,0 +1,67 @@
--------------------------------------------------------------------------------
Preserve Hierarchy
--------------------------------------------------------------------------------
I *reaaaally* want to be able to make deferred RDL parameters a reality in the
future. (https://github.com/SystemRDL/systemrdl-compiler/issues/58)
Proactively design templates to retain "real" hierarchy. This means:
- Do not flatten/unroll signals. Use SV structs & arrays
- Do not flatten/unroll logic. Use nested for loops
Sticking to the above should make adding parameter support somewhat less painful.
--------------------------------------------------------------------------------
Indexing & references
--------------------------------------------------------------------------------
Need to define a consistent scheme for referencing hierarchical elements.
When inside a nesting of for loops, and array indexes are intended to increment,
always use an incrementing indexing scheme when generating iterators:
i0, i1, i2, i3, ... i9, i10, i11, etc...
For example:
access_strb.2d_array[i0][i1].array[i3]
Sometimes, an RDL input may create the need to reference an element with
partially constant indexes.
For example, given this RDL:
addrmap top {
regfile {
reg {
field {} f1;
} x[8];
reg {
field {} f2;
} y;
y.f2->next = x[3].f1;
} rf_loop[16];
};
The 'next' assignment will have a reference that has the following hierarchcial
path:
top.rf_loop[].x[3].f1
| |
| +--- known index
+--- unknown index
It is provable that any RDL references will always follow these truths:
- a reference may have a mix of known/unknown indexes in its path
- unknown indexes (if any) will always precede known indexes
- unknown indexes are not actually part of the relative reference path, and
represent replication of the reference.
It is impossible for the reference itself to introduce unknown indexes.
When generating SystemVerilog, be sure to generate code such that "unknown" indexes
are always implicitly known due to the reference being used from within a for loop.
For example:
for(int i0=0; i0<16; i0++) begin : rf_loop_array
top.rf_loop[i0].y.f2 = top.rf_loop[i0].x[3].f1
end
This should be a reasonable thing to accomplish, since unknown indexes should
only show up in situations where the consumer of the reference is being
replicated as well, and is therefore implicitly going to be inside a for loop.

View File

@@ -17,3 +17,6 @@ Values:
If X is a property reference... do whats right... If X is a property reference... do whats right...
my_field->anded === (&path.to.my_field) my_field->anded === (&path.to.my_field)
if X is a static value, return the literal if X is a static value, return the literal
See `Hierarchy and Indexing` on details onhow to build path references to stuff

View File

@@ -4,11 +4,12 @@ Things that need validation by the compiler
================================================================================ ================================================================================
Many of these are probably already covered, but being paranoid. Many of these are probably already covered, but being paranoid.
Make a list of things as I think of them. Make a list of things as I think of them.
Keep them here in case I forget and re-think of them.
Mark these as follows: Mark these as follows:
X = Yes, confirmed that the compiler covers this X = Yes, confirmed that the compiler covers this
! = No! Confirmed that the compiler does not check this ! = No! Confirmed that the compiler does not check this, and should.
(blank) = TBD ? = TBD
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -23,11 +24,26 @@ X Field has no knowable value
--> emit a warning? --> emit a warning?
! multiple field_reset in the same hierarchy X References to a component or component property must use unambiguous array indexing
For example, if "array_o_regs" is an array...
The following is illegal:
my_reg.my_field->next = array_o_regs.thing
my_reg.my_field->next = array_o_regs.thing->anded
This is ok:
my_reg.my_field->next = array_o_regs[2].thing
my_reg.my_field->next = array_o_regs[2].thing->anded
NEVERMIND - compiler does not allow indefinite array references at all!
References are guaranteed to be unambiguous:
"Incompatible number of index dimensions after 'CTRL'. Expected 1, found 0."
X Clause 10.6.1-f (wide registers cannot have access side-effects)
X multiple field_reset in the same hierarchy
there can only be one signal declared with field_reset there can only be one signal declared with field_reset
in a given hierarchy in a given hierarchy
! multiple cpuif_reset in the same hierarchy X multiple cpuif_reset in the same hierarchy
there can only be one signal declared with cpuif_reset there can only be one signal declared with cpuif_reset
in a given hierarchy in a given hierarchy
@@ -39,13 +55,26 @@ X Field has no knowable value
... or, make these properties clear each-other on assignment ... or, make these properties clear each-other on assignment
? If a node ispresent=true, and any of it's properties are a reference,
then thse references' its ispresent shall also be true
Pretty sure this is an explicit clause in the spec.
Make sure it is actually implemented
? Illegal property references:
- reference any of the counter property references to something that isn't a counter
- reference hwclr or hwset, but the owner node has them set to False
means that the actual inferred signal doesnt exist!
- reference swwe/swwel or we/wel, but the owner node has them, AND their complement set to False
means that the actual inferred signal doesnt exist!
================================================================================ ================================================================================
Things that need validation by this exporter Things that need validation by this exporter
================================================================================ ================================================================================
List of stuff in case I forget. List of stuff in case I forget.
X = Yes! I already implemented this.
! = No! exporter does not enforce this yet ! = No! exporter does not enforce this yet
x = Yes! I already implemented this.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
! "bridge" addrmap not supported ! "bridge" addrmap not supported
@@ -56,5 +85,25 @@ cpuif_resets
! Warn/error on any signal with cpuif_reset set, that is not in the top-level ! Warn/error on any signal with cpuif_reset set, that is not in the top-level
addrmap. At the very least, warn that it will be ignored addrmap. At the very least, warn that it will be ignored
! multiple cpuif_reset ! async data signals
there can be only one cpuif reset Only supporting async signals if they are exclusively used in resets.
Anyhting else declared as "async" shall be an error
I have zero interest in implementing resynchronizers
! regwidth/accesswidth is sane
! accesswidth is the same for all registers in the regblock
- accesswidth is what implies the cpuif bus width
! accesswidth == regwidth
Enforce this for now. Dont feel like supporting fancy modes yet
X regwidth < accesswidth
This is illegal and is enforced by the compiler.
! regwidth > accesswidth
Need to extend address decode strobes to have multiple bits
this is where looking at endinaness matters to determine field placement
! Contents of target are all internal. No external regs
! Does not contain any mem components
! Do not allow unaligned addresses
All offsets & strides shall be a multiple of the accesswidth used

View File

@@ -28,6 +28,16 @@ TODO:
this may actually only apply to counters... this may actually only apply to counters...
This is trivial in a 2-process implementation, but i'd rather avoid the overheads This is trivial in a 2-process implementation, but i'd rather avoid the overheads
TODO:
Scour the RDL spec.
Does this "next state" precedence model hold true in all situations?
TODO:
Think about user-extensibility
Provide a mechanism for users to extend/override field behavior
TODO:
Does the endinness the user sets matter anywhere?
Implementation Implementation
Makes sense to use a listener class Makes sense to use a listener class

View File

@@ -62,4 +62,8 @@ WARNING:
Forwards response strobe back up to cpu interface layer Forwards response strobe back up to cpu interface layer
TODO:
Dont forget about alias registers here Dont forget about alias registers here
TODO:
Does the endinness the user sets matter anywhere?

View File

@@ -1,7 +1,7 @@
import re import re
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List, Union
from systemrdl.node import Node, AddressableNode, RegNode from systemrdl.node import Node, AddressableNode, RegNode, FieldNode
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -27,6 +27,26 @@ class AddressDecode:
self._do_address_decode_node(lines, self.top_node) self._do_address_decode_node(lines, self.top_node)
return "\n".join(lines) return "\n".join(lines)
def get_access_strobe(self, node: Union[RegNode, FieldNode]) -> str:
"""
Returns the Verilog string that represents the register/field's access strobe.
"""
if isinstance(node, FieldNode):
node = node.parent
path = node.get_rel_path(self.top_node, empty_array_suffix="[!]")
# replace unknown indexes with incrementing iterators i0, i1, ...
class repl:
def __init__(self):
self.i = 0
def __call__(self, match):
s = f'i{self.i}'
self.i += 1
return s
path = re.sub(r'!', repl(), path)
return "decoded_reg_strb." + path
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Struct generation functions # Struct generation functions
@@ -55,7 +75,7 @@ class AddressDecode:
self._indent_level -= 1 self._indent_level -= 1
if is_top: if is_top:
lines.append(f"{self._indent}}} access_strb_t;") lines.append(f"{self._indent}}} decoded_reg_strb_t;")
else: else:
lines.append(f"{self._indent}}} {node.inst_name}{self._get_node_array_suffix(node)};") lines.append(f"{self._indent}}} {node.inst_name}{self._get_node_array_suffix(node)};")
@@ -96,26 +116,11 @@ class AddressDecode:
a += f" + i{i}*'h{stride:x}" a += f" + i{i}*'h{stride:x}"
return a return a
def _get_strobe_str(self, node:AddressableNode) -> str:
path = node.get_rel_path(self.top_node, array_suffix="[!]", empty_array_suffix="[!]")
class repl:
def __init__(self):
self.i = 0
def __call__(self, match):
s = f'i{self.i}'
self.i += 1
return s
path = re.sub(r'!', repl(), path)
strb = "access_strb." + path
return strb
def _do_address_decode_node(self, lines:List[str], node:AddressableNode) -> None: def _do_address_decode_node(self, lines:List[str], node:AddressableNode) -> None:
for child in node.children(): for child in node.children():
if isinstance(child, RegNode): if isinstance(child, RegNode):
self._push_array_dims(lines, child) self._push_array_dims(lines, child)
lines.append(f"{self._indent}{self._get_strobe_str(child)} = cpuif_req & (cpuif_addr == {self._get_address_str(child)});") lines.append(f"{self._indent}{self.get_access_strobe(child)} = cpuif_req & (cpuif_addr == {self._get_address_str(child)});")
self._pop_array_dims(lines, child) self._pop_array_dims(lines, child)
elif isinstance(child, AddressableNode): elif isinstance(child, AddressableNode):
self._push_array_dims(lines, child) self._push_array_dims(lines, child)

View File

@@ -16,16 +16,16 @@ class APB4_Cpuif_flattened(APB4_Cpuif):
def port_declaration(self) -> str: def port_declaration(self) -> str:
# TODO: Reference data/addr width from verilog parameter perhaps? # TODO: Reference data/addr width from verilog parameter perhaps?
lines = [ lines = [
"input wire %s" % self.signal("psel"), "input wire " + self.signal("psel"),
"input wire %s" % self.signal("penable"), "input wire " + self.signal("penable"),
"input wire %s" % self.signal("pwrite"), "input wire " + self.signal("pwrite"),
"input wire %s" % self.signal("pprot"), "input wire " + self.signal("pprot"),
"input wire [%d-1:0] %s" % (self.addr_width, self.signal("paddr")), f"input wire [{self.addr_width-1}:0] " + self.signal("paddr"),
"input wire [%d-1:0] %s" % (self.data_width, self.signal("pwdata")), f"input wire [{self.data_width-1}:0] " + self.signal("pwdata"),
"input wire [%d-1:0] %s" % (self.data_width / 8, self.signal("pstrb")), f"input wire [{(self.data_width / 8)-1}:0] " + self.signal("pstrb"),
"output logic %s" % self.signal("pready"), "output logic " + self.signal("pready"),
"output logic [%d-1:0] %s" % (self.data_width, self.signal("prdata")), f"output logic [{self.data_width-1}:0] " + self.signal("prdata"),
"output logic %s" % self.signal("pslverr"), "output logic " + self.signal("pslverr"),
] ]
return ",\n".join(lines) return ",\n".join(lines)

View File

@@ -6,21 +6,24 @@ if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import RegblockExporter
from .hwif.base import HwifBase from .hwif.base import HwifBase
from .field_logic import FieldLogic from .field_logic import FieldLogic
from .addr_decode import AddressDecode
class Dereferencer: class Dereferencer:
""" """
This class provides an interface to convert conceptual SystemRDL references This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers into Verilog identifiers
""" """
def __init__(self, exporter:'RegblockExporter', hwif:'HwifBase', field_logic: "FieldLogic", top_node:Node): def __init__(self, exporter:'RegblockExporter', top_node:Node, hwif:'HwifBase', address_decode: 'AddressDecode', field_logic: 'FieldLogic'):
self.exporter = exporter self.exporter = exporter
self.hwif = hwif self.hwif = hwif
self.address_decode = address_decode
self.field_logic = field_logic self.field_logic = field_logic
self.top_node = top_node self.top_node = top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str: def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
""" """
Returns the Verilog string that represents the value associated with the object. Returns the Verilog string that represents the readable value associated
with the object.
If given a simple scalar value, then the corresponding Verilog literal is returned. If given a simple scalar value, then the corresponding Verilog literal is returned.
@@ -31,7 +34,7 @@ class Dereferencer:
# Is a simple scalar value # Is a simple scalar value
return f"'h{obj:x}" return f"'h{obj:x}"
elif isinstance(obj, FieldNode): if isinstance(obj, FieldNode):
if obj.implements_storage: if obj.implements_storage:
return self.field_logic.get_storage_identifier(obj) return self.field_logic.get_storage_identifier(obj)
@@ -48,14 +51,14 @@ class Dereferencer:
# Fall back to a value of 0 # Fall back to a value of 0
return "'h0" return "'h0"
elif isinstance(obj, SignalNode): if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif # Signals are always inputs from the hwif
return self.hwif.get_input_identifier(obj) return self.hwif.get_input_identifier(obj)
elif isinstance(obj, PropertyReference): if isinstance(obj, PropertyReference):
# TODO: Table G1 describes other possible ref targets
# Value reduction properties # Value reduction properties.
# Wrap with the appropriate Verilog reduction operator
val = self.get_value(obj.node) val = self.get_value(obj.node)
if obj.name == "anded": if obj.name == "anded":
return f"&({val})" return f"&({val})"
@@ -63,15 +66,98 @@ class Dereferencer:
return f"|({val})" return f"|({val})"
elif obj.name == "xored": elif obj.name == "xored":
return f"^({val})" return f"^({val})"
else:
raise RuntimeError
# references that directly access a property value
if obj.name in {
'decrvalue',
'enable',
'haltenable',
'haltmask',
'hwenable',
'hwmask',
'incrvalue',
'mask',
'reset',
'resetsignal',
}:
return self.get_value(obj.node.get_property(obj.name))
elif obj.name in {'incr', 'decr'}:
prop_value = obj.node.get_property(obj.name)
if prop_value is None:
# unset by the user, points to the implied internal signal
raise NotImplementedError # TODO: Implement this
else: else:
raise RuntimeError return self.get_value(prop_value)
elif obj.name == "next":
prop_value = obj.node.get_property(obj.name)
if prop_value is None:
# unset by the user, points to the implied internal signal
raise NotImplementedError # TODO: Implement this
else:
return self.get_value(prop_value)
def get_access_strobe(self, reg: RegNode) -> str: # References to another component value, or an implied input
if obj.name in {'hwclr', 'hwset'}:
prop_value = obj.node.get_property(obj.name)
if prop_value is True:
# Points to inferred hwif input
return self.hwif.get_input_identifier(obj)
elif prop_value is False:
# This should never happen, as this is checked by the compiler's validator
raise RuntimeError
else:
return self.get_value(prop_value)
# References to another component value, or an implied input
# May have a complementary partner property
complementary_pairs = {
"we": "wel",
"wel": "we",
"swwe": "swwel",
"swwel": "swwe",
}
if obj.name in complementary_pairs:
prop_value = obj.node.get_property(obj.name)
if prop_value is True:
# Points to inferred hwif input
return self.hwif.get_input_identifier(obj)
elif prop_value is False:
# Try complementary property
prop_value = obj.node.get_property(complementary_pairs[obj.name])
if prop_value is True:
# Points to inferred hwif input
return f"!({self.hwif.get_input_identifier(obj)})"
raise NotImplementedError # TODO: Implement this
elif prop_value is False:
# This should never happen, as this is checked by the compiler's validator
raise RuntimeError
else:
return f"!({self.get_value(prop_value)})"
else:
return self.get_value(prop_value)
"""
TODO:
Resolves to an internal signal used in the field's logic
decrsaturate
decrthreshold
halt
incrsaturate
incrthreshold
intr
overflow
saturate
swacc
swmod
threshold
"""
raise RuntimeError("Unhandled reference to: %s", obj)
def get_access_strobe(self, obj: Union[RegNode, FieldNode]) -> str:
""" """
Returns the Verilog string that represents the register's access strobe Returns the Verilog string that represents the register's access strobe
""" """
# TODO: Implement me return self.address_decode.get_access_strobe(obj)
raise NotImplementedError

View File

@@ -69,7 +69,7 @@ class RegblockExporter:
cpuif = cpuif_cls( cpuif = cpuif_cls(
self, self,
cpuif_reset=cpuif_reset, # TODO: cpuif_reset=cpuif_reset, # TODO:
data_width=32, # TODO: data_width=32, # TODO: derive from the accesswidth used by regs
addr_width=32 # TODO: addr_width=32 # TODO:
) )
@@ -82,7 +82,7 @@ class RegblockExporter:
address_decode = AddressDecode(self, node) address_decode = AddressDecode(self, node)
field_logic = FieldLogic(self, node) field_logic = FieldLogic(self, node)
readback_mux = ReadbackMux(self, node) readback_mux = ReadbackMux(self, node)
dereferencer = Dereferencer(self, hwif, field_logic, node) dereferencer = Dereferencer(self, node, hwif, address_decode, field_logic)
# Build Jinja template context # Build Jinja template context
context = { context = {

View File

@@ -1,3 +1,4 @@
import re
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List
from systemrdl.node import Node, AddressableNode, RegNode, FieldNode from systemrdl.node import Node, AddressableNode, RegNode, FieldNode
@@ -27,10 +28,22 @@ class FieldLogic:
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Field utility functions # Field utility functions
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
def get_storage_identifier(self, obj: FieldNode): def get_storage_identifier(self, node: FieldNode):
assert obj.implements_storage assert node.implements_storage
return "TODO: implement get_storage_identifier()" path = node.get_rel_path(self.top_node, empty_array_suffix="[!]")
# replace unknown indexes with incrementing iterators i0, i1, ...
class repl:
def __init__(self):
self.i = 0
def __call__(self, match):
s = f'i{self.i}'
self.i += 1
return s
path = re.sub(r'!', repl(), path)
return "field_storage." + path
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------

View File

@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING, Union from typing import TYPE_CHECKING, Union
from systemrdl.node import Node, SignalNode, FieldNode from systemrdl.node import Node, SignalNode, FieldNode
from systemrdl.rdltypes import AccessType from systemrdl.rdltypes import AccessType, PropertyReference
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import RegblockExporter from ..exporter import RegblockExporter
@@ -13,7 +13,7 @@ class HwifBase:
- Signal inputs (except those that are promoted to the top) - Signal inputs (except those that are promoted to the top)
""" """
def __init__(self, exporter:'RegblockExporter', top_node:'Node', package_name:str): def __init__(self, exporter: 'RegblockExporter', top_node: Node, package_name: str):
self.exporter = exporter self.exporter = exporter
self.top_node = top_node self.top_node = top_node
self.package_name = package_name self.package_name = package_name
@@ -41,9 +41,10 @@ class HwifBase:
Returns True if the object infers an input wire in the hwif Returns True if the object infers an input wire in the hwif
""" """
if isinstance(obj, FieldNode): if isinstance(obj, FieldNode):
return obj.get_property("hw") in {AccessType.rw, AccessType.w} return obj.is_hw_writable
elif isinstance(obj, SignalNode): elif isinstance(obj, SignalNode):
raise NotImplementedError # TODO: # Signals are implicitly always inputs
return True
else: else:
raise RuntimeError raise RuntimeError
@@ -52,29 +53,35 @@ class HwifBase:
""" """
Returns True if the object infers an output wire in the hwif Returns True if the object infers an output wire in the hwif
""" """
return obj.get_property("hw") in {AccessType.rw, AccessType.r} # TODO: Extend this for signals and prop references?
return obj.is_hw_readable
def get_input_identifier(self, obj) -> str: def get_input_identifier(self, obj: Union[FieldNode, SignalNode, PropertyReference]) -> str:
""" """
Returns the identifier string that best represents the input object. Returns the identifier string that best represents the input object.
if obj is: if obj is:
Field: the fields input value port Field: the fields input value port
Signal: signal input value Signal: signal input value
TODO: finish this Prop reference:
could be an implied hwclr/hwset/swwe/swwel/we/wel input
Raise a runtime error if an illegal prop ref is requested, or if
the prop ref is not actually implied, but explicitly ref a component
TODO: finish this
raises an exception if obj is invalid raises an exception if obj is invalid
""" """
raise NotImplementedError() raise NotImplementedError()
def get_output_identifier(self, obj) -> str: def get_output_identifier(self, obj: FieldNode) -> str:
""" """
Returns the identifier string that best represents the output object. Returns the identifier string that best represents the output object.
if obj is: if obj is:
Field: the fields output value port Field: the fields output value port
Property ref: this is also part of the struct
TODO: finish this TODO: finish this
raises an exception if obj is invalid raises an exception if obj is invalid

View File

@@ -26,7 +26,7 @@ class StructHwif(HwifBase):
self.has_output_struct = self._do_struct_addressable(lines, self.top_node, is_input=False) self.has_output_struct = self._do_struct_addressable(lines, self.top_node, is_input=False)
self._indent_level -= 1 self._indent_level -= 1
lines.append("") lines.append("")
lines.append(f"endpackage") lines.append("endpackage")
return "\n".join(lines) return "\n".join(lines)

View File

@@ -48,16 +48,26 @@ module {{module_name}} #(
// Address Decode // Address Decode
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
{{address_decode.get_strobe_struct()|indent}} {{address_decode.get_strobe_struct()|indent}}
access_strb_t access_strb; decoded_reg_strb_t decoded_reg_strb;
logic decoded_req;
logic decoded_req_is_wr;
logic [DATA_WIDTH-1:0] decoded_wr_data;
logic [DATA_WIDTH-1:0] decoded_wr_bitstrb;
always_comb begin always_comb begin
{{address_decode.get_implementation()|indent(8)}} {{address_decode.get_implementation()|indent(8)}}
end end
// Writes are always posted with no error response // Writes are always granted with no error response
assign cpuif_wr_ack = cpuif_req & cpuif_req_is_wr; assign cpuif_wr_ack = cpuif_req & cpuif_req_is_wr;
assign cpuif_wr_err = '0; assign cpuif_wr_err = '0;
// Pass down signals to next stage
assign decoded_req = cpuif_req;
assign decoded_req_is_wr = cpuif_req_is_wr;
assign decoded_wr_data = cpuif_wr_data;
assign decoded_wr_bitstrb = cpuif_wr_bitstrb;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Field logic // Field logic
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------