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

@@ -1,7 +1,7 @@
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:
@@ -27,6 +27,26 @@ class AddressDecode:
self._do_address_decode_node(lines, self.top_node)
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
@@ -55,7 +75,7 @@ class AddressDecode:
self._indent_level -= 1
if is_top:
lines.append(f"{self._indent}}} access_strb_t;")
lines.append(f"{self._indent}}} decoded_reg_strb_t;")
else:
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}"
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:
for child in node.children():
if isinstance(child, RegNode):
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)
elif isinstance(child, AddressableNode):
self._push_array_dims(lines, child)

View File

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

View File

@@ -6,21 +6,24 @@ if TYPE_CHECKING:
from .exporter import RegblockExporter
from .hwif.base import HwifBase
from .field_logic import FieldLogic
from .addr_decode import AddressDecode
class Dereferencer:
"""
This class provides an interface to convert conceptual SystemRDL references
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.hwif = hwif
self.address_decode = address_decode
self.field_logic = field_logic
self.top_node = top_node
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.
@@ -31,7 +34,7 @@ class Dereferencer:
# Is a simple scalar value
return f"'h{obj:x}"
elif isinstance(obj, FieldNode):
if isinstance(obj, FieldNode):
if obj.implements_storage:
return self.field_logic.get_storage_identifier(obj)
@@ -48,14 +51,14 @@ class Dereferencer:
# Fall back to a value of 0
return "'h0"
elif isinstance(obj, SignalNode):
if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif
return self.hwif.get_input_identifier(obj)
elif isinstance(obj, PropertyReference):
# TODO: Table G1 describes other possible ref targets
if isinstance(obj, PropertyReference):
# Value reduction properties
# Value reduction properties.
# Wrap with the appropriate Verilog reduction operator
val = self.get_value(obj.node)
if obj.name == "anded":
return f"&({val})"
@@ -63,15 +66,98 @@ class Dereferencer:
return f"|({val})"
elif obj.name == "xored":
return f"^({val})"
else:
raise RuntimeError
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:
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
"""
# TODO: Implement me
raise NotImplementedError
return self.address_decode.get_access_strobe(obj)

View File

@@ -69,7 +69,7 @@ class RegblockExporter:
cpuif = cpuif_cls(
self,
cpuif_reset=cpuif_reset, # TODO:
data_width=32, # TODO:
data_width=32, # TODO: derive from the accesswidth used by regs
addr_width=32 # TODO:
)
@@ -82,7 +82,7 @@ class RegblockExporter:
address_decode = AddressDecode(self, node)
field_logic = FieldLogic(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
context = {

View File

@@ -1,3 +1,4 @@
import re
from typing import TYPE_CHECKING, List
from systemrdl.node import Node, AddressableNode, RegNode, FieldNode
@@ -27,10 +28,22 @@ class FieldLogic:
#---------------------------------------------------------------------------
# Field utility functions
#---------------------------------------------------------------------------
def get_storage_identifier(self, obj: FieldNode):
assert obj.implements_storage
def get_storage_identifier(self, node: FieldNode):
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 systemrdl.node import Node, SignalNode, FieldNode
from systemrdl.rdltypes import AccessType
from systemrdl.rdltypes import AccessType, PropertyReference
if TYPE_CHECKING:
from ..exporter import RegblockExporter
@@ -13,7 +13,7 @@ class HwifBase:
- 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.top_node = top_node
self.package_name = package_name
@@ -41,9 +41,10 @@ class HwifBase:
Returns True if the object infers an input wire in the hwif
"""
if isinstance(obj, FieldNode):
return obj.get_property("hw") in {AccessType.rw, AccessType.w}
return obj.is_hw_writable
elif isinstance(obj, SignalNode):
raise NotImplementedError # TODO:
# Signals are implicitly always inputs
return True
else:
raise RuntimeError
@@ -52,29 +53,35 @@ class HwifBase:
"""
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.
if obj is:
Field: the fields input value port
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
"""
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.
if obj is:
Field: the fields output value port
Property ref: this is also part of the struct
TODO: finish this
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._indent_level -= 1
lines.append("")
lines.append(f"endpackage")
lines.append("endpackage")
return "\n".join(lines)

View File

@@ -48,16 +48,26 @@ module {{module_name}} #(
// Address Decode
//--------------------------------------------------------------------------
{{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
{{address_decode.get_implementation()|indent(8)}}
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_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
//--------------------------------------------------------------------------