prop reference infrastructure, and other things
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user