more field logic
This commit is contained in:
@@ -32,15 +32,14 @@ Basically, i'd define a ton of helper functions that return the signal identifie
|
|||||||
Dev Todo list
|
Dev Todo list
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
- tidy up stuff
|
- Link more functions to the dereferencer
|
||||||
- merge FieldBuilder and FieldLogic classes. It makes no sense for these to be separate
|
I shouldn't have to go to the hwif or whatever
|
||||||
- propagate the exporter class EVERYWHERE
|
dereferencer should have all the query functions
|
||||||
shorten it to simply "self.exp"
|
|
||||||
- Build out a few more NextStateConditional implementations
|
|
||||||
- hw we
|
|
||||||
|
|
||||||
- readback mux
|
- readback mux
|
||||||
|
|
||||||
|
- Other field output assignments
|
||||||
|
|
||||||
- HWIF layer
|
- HWIF layer
|
||||||
- User Signals
|
- User Signals
|
||||||
Generate these in the io struct? I forget what I decided
|
Generate these in the io struct? I forget what I decided
|
||||||
|
|||||||
@@ -82,7 +82,19 @@ X If a node ispresent=true, and any of it's properties are a reference,
|
|||||||
then those references' ispresent shall also be true
|
then those references' ispresent shall also be true
|
||||||
This is an explicit clause in the spec: 5.3.1-i
|
This is an explicit clause in the spec: 5.3.1-i
|
||||||
|
|
||||||
|
! Flag illegal sw actions if not readable/writable
|
||||||
|
The following combinations dont get flagged currently:
|
||||||
|
sw=w; rclr;
|
||||||
|
sw=w; rset;
|
||||||
|
sw=r; woset;
|
||||||
|
sw=r; woclr;
|
||||||
|
their counterparts do get flagged. such as:
|
||||||
|
sw=w; onread=rclr;
|
||||||
|
|
||||||
|
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
|
||||||
|
y->hwclr = y;
|
||||||
|
y->we = y;
|
||||||
|
... it works, but should it be allowed? Seems like user-error
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ NextStateConditional Class
|
|||||||
it instructs the FieldBuider that code for this conditional shall be emitted
|
it instructs the FieldBuider that code for this conditional shall be emitted
|
||||||
TODO: better name than "is_match"? More like "is this relevant"
|
TODO: better name than "is_match"? More like "is this relevant"
|
||||||
|
|
||||||
- get_conditional(self, field: FieldNode) -> str:
|
- get_predicate(self, field: FieldNode) -> str:
|
||||||
Returns the rendered conditional text
|
Returns the rendered conditional text
|
||||||
|
|
||||||
- get_assignments(self, field: FieldNode) -> List[str]:
|
- get_assignments(self, field: FieldNode) -> List[str]:
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ if TYPE_CHECKING:
|
|||||||
from .exporter import RegblockExporter
|
from .exporter import RegblockExporter
|
||||||
|
|
||||||
class AddressDecode:
|
class AddressDecode:
|
||||||
def __init__(self, exporter:'RegblockExporter'):
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def top_node(self) -> AddrmapNode:
|
def top_node(self) -> AddrmapNode:
|
||||||
return self.exporter.top_node
|
return self.exp.top_node
|
||||||
|
|
||||||
def get_strobe_struct(self) -> str:
|
def get_strobe_struct(self) -> str:
|
||||||
struct_gen = DecodeStructGenerator()
|
struct_gen = DecodeStructGenerator()
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ if TYPE_CHECKING:
|
|||||||
class CpuifBase:
|
class CpuifBase:
|
||||||
template_path = "cpuif/base_tmpl.sv"
|
template_path = "cpuif/base_tmpl.sv"
|
||||||
|
|
||||||
def __init__(self, exporter:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
|
def __init__(self, exp:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
self.cpuif_reset = cpuif_reset
|
self.cpuif_reset = cpuif_reset
|
||||||
self.data_width = data_width
|
self.data_width = data_width
|
||||||
self.addr_width = addr_width
|
self.addr_width = addr_width
|
||||||
@@ -28,5 +28,5 @@ class CpuifBase:
|
|||||||
"get_always_ff_event": get_always_ff_event,
|
"get_always_ff_event": get_always_ff_event,
|
||||||
}
|
}
|
||||||
|
|
||||||
template = self.exporter.jj_env.get_template(self.template_path)
|
template = self.exp.jj_env.get_template(self.template_path)
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|||||||
@@ -13,24 +13,24 @@ 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'):
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hwif(self) -> 'Hwif':
|
def hwif(self) -> 'Hwif':
|
||||||
return self.exporter.hwif
|
return self.exp.hwif
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def address_decode(self) -> 'AddressDecode':
|
def address_decode(self) -> 'AddressDecode':
|
||||||
return self.exporter.address_decode
|
return self.exp.address_decode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def field_logic(self) -> 'FieldLogic':
|
def field_logic(self) -> 'FieldLogic':
|
||||||
return self.exporter.field_logic
|
return self.exp.field_logic
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def top_node(self) -> AddrmapNode:
|
def top_node(self) -> AddrmapNode:
|
||||||
return self.exporter.top_node
|
return self.exp.top_node
|
||||||
|
|
||||||
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
|
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class RegblockExporter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def export(self, node: Union[RootNode, AddrmapNode], output_path:str, **kwargs):
|
def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs):
|
||||||
# If it is the root node, skip to top addrmap
|
# If it is the root node, skip to top addrmap
|
||||||
if isinstance(node, RootNode):
|
if isinstance(node, RootNode):
|
||||||
self.top_node = node.top
|
self.top_node = node.top
|
||||||
@@ -67,6 +67,8 @@ class RegblockExporter:
|
|||||||
hwif_cls = kwargs.pop("hwif_cls", Hwif)
|
hwif_cls = kwargs.pop("hwif_cls", Hwif)
|
||||||
module_name = kwargs.pop("module_name", self.top_node.inst_name)
|
module_name = kwargs.pop("module_name", self.top_node.inst_name)
|
||||||
package_name = kwargs.pop("package_name", module_name + "_pkg")
|
package_name = kwargs.pop("package_name", module_name + "_pkg")
|
||||||
|
module_file_path = os.path.join(output_dir, module_name + ".sv")
|
||||||
|
package_file_path = os.path.join(output_dir, package_name + ".sv")
|
||||||
|
|
||||||
# Check for stray kwargs
|
# Check for stray kwargs
|
||||||
if kwargs:
|
if kwargs:
|
||||||
@@ -110,6 +112,10 @@ class RegblockExporter:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Write out design
|
# Write out design
|
||||||
|
template = self.jj_env.get_template("package_tmpl.sv")
|
||||||
|
stream = template.stream(context)
|
||||||
|
stream.dump(package_file_path)
|
||||||
|
|
||||||
template = self.jj_env.get_template("module_tmpl.sv")
|
template = self.jj_env.get_template("module_tmpl.sv")
|
||||||
stream = template.stream(context)
|
stream = template.stream(context)
|
||||||
stream.dump(output_path)
|
stream.dump(module_file_path)
|
||||||
|
|||||||
@@ -1,24 +1,34 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from systemrdl.node import AddrmapNode, FieldNode
|
from systemrdl.rdltypes import PropertyReference, PrecedenceType
|
||||||
from systemrdl.rdltypes import PropertyReference
|
|
||||||
|
from .bases import AssignmentPrecedence, NextStateConditional
|
||||||
|
from . import sw_onread
|
||||||
|
from . import sw_onwrite
|
||||||
|
from . import hw_write
|
||||||
|
from . import hw_set_clr
|
||||||
|
|
||||||
from ..utils import get_indexed_path
|
from ..utils import get_indexed_path
|
||||||
from .field_builder import FieldBuilder, FieldStorageStructGenerator
|
|
||||||
from .field_builder import CombinationalStructGenerator, FieldLogicGenerator
|
|
||||||
|
|
||||||
|
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Dict, List
|
||||||
|
from systemrdl.node import AddrmapNode, FieldNode
|
||||||
from ..exporter import RegblockExporter
|
from ..exporter import RegblockExporter
|
||||||
|
|
||||||
class FieldLogic:
|
class FieldLogic:
|
||||||
def __init__(self, exporter:'RegblockExporter'):
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
self.field_builder = FieldBuilder(exporter)
|
|
||||||
|
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
||||||
|
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
||||||
|
|
||||||
|
self.init_conditionals()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def top_node(self) -> AddrmapNode:
|
def top_node(self) -> 'AddrmapNode':
|
||||||
return self.exporter.top_node
|
return self.exp.top_node
|
||||||
|
|
||||||
def get_storage_struct(self) -> str:
|
def get_storage_struct(self) -> str:
|
||||||
struct_gen = FieldStorageStructGenerator()
|
struct_gen = FieldStorageStructGenerator()
|
||||||
@@ -31,7 +41,7 @@ class FieldLogic:
|
|||||||
return s + "\nfield_storage_t field_storage;"
|
return s + "\nfield_storage_t field_storage;"
|
||||||
|
|
||||||
def get_combo_struct(self) -> str:
|
def get_combo_struct(self) -> str:
|
||||||
struct_gen = CombinationalStructGenerator(self.field_builder)
|
struct_gen = CombinationalStructGenerator(self)
|
||||||
s = struct_gen.get_struct(self.top_node, "field_combo_t")
|
s = struct_gen.get_struct(self.top_node, "field_combo_t")
|
||||||
|
|
||||||
# Only declare the storage struct if it exists
|
# Only declare the storage struct if it exists
|
||||||
@@ -41,7 +51,7 @@ class FieldLogic:
|
|||||||
return s + "\nfield_combo_t field_combo;"
|
return s + "\nfield_combo_t field_combo;"
|
||||||
|
|
||||||
def get_implementation(self) -> str:
|
def get_implementation(self) -> str:
|
||||||
gen = FieldLogicGenerator(self.field_builder)
|
gen = FieldLogicGenerator(self)
|
||||||
s = gen.get_content(self.top_node)
|
s = gen.get_content(self.top_node)
|
||||||
if s is None:
|
if s is None:
|
||||||
return ""
|
return ""
|
||||||
@@ -50,22 +60,23 @@ class FieldLogic:
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Field utility functions
|
# Field utility functions
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
def get_storage_identifier(self, node: FieldNode) -> str:
|
def get_storage_identifier(self, node: 'FieldNode') -> str:
|
||||||
"""
|
"""
|
||||||
Returns the Verilog string that represents the storage register element
|
Returns the Verilog string that represents the storage register element
|
||||||
for the referenced field
|
for the referenced field
|
||||||
"""
|
"""
|
||||||
assert node.implements_storage
|
assert node.implements_storage
|
||||||
path = get_indexed_path(self.top_node, node)
|
path = get_indexed_path(self.top_node, node)
|
||||||
return "field_storage." + path
|
return f"field_storage.{path}"
|
||||||
|
|
||||||
def get_field_next_identifier(self, node: FieldNode) -> str:
|
def get_field_next_identifier(self, node: 'FieldNode') -> str:
|
||||||
"""
|
"""
|
||||||
Returns a Verilog string that represents the field's next-state.
|
Returns a Verilog string that represents the field's next-state.
|
||||||
This is specifically for use in Field->next property references.
|
This is specifically for use in Field->next property references.
|
||||||
"""
|
"""
|
||||||
# TODO: Implement this
|
assert node.implements_storage
|
||||||
raise NotImplementedError
|
path = get_indexed_path(self.top_node, node)
|
||||||
|
return f"field_combo.{path}.next"
|
||||||
|
|
||||||
def get_counter_control_identifier(self, prop_ref: PropertyReference) -> str:
|
def get_counter_control_identifier(self, prop_ref: PropertyReference) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -75,3 +86,103 @@ class FieldLogic:
|
|||||||
"""
|
"""
|
||||||
# TODO: Implement this
|
# TODO: Implement this
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Field Logic Conditionals
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
|
||||||
|
"""
|
||||||
|
Register a NextStateConditional action for hardware-triggered field updates.
|
||||||
|
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
|
||||||
|
property can be reliably honored.
|
||||||
|
|
||||||
|
The ``precedence`` argument determines the conditional assignment's priority over
|
||||||
|
other assignments of differing precedence.
|
||||||
|
|
||||||
|
If multiple conditionals of the same precedence are registered, they are
|
||||||
|
searched sequentially and only the first to match the given field is used.
|
||||||
|
Conditionals are searched in reverse order that they were registered.
|
||||||
|
"""
|
||||||
|
if precedence not in self._hw_conditionals:
|
||||||
|
self._hw_conditionals[precedence] = []
|
||||||
|
self._hw_conditionals[precedence].append(conditional)
|
||||||
|
|
||||||
|
|
||||||
|
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
|
||||||
|
"""
|
||||||
|
Register a NextStateConditional action for software-triggered field updates.
|
||||||
|
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
|
||||||
|
property can be reliably honored.
|
||||||
|
|
||||||
|
The ``precedence`` argument determines the conditional assignment's priority over
|
||||||
|
other assignments of differing precedence.
|
||||||
|
|
||||||
|
If multiple conditionals of the same precedence are registered, they are
|
||||||
|
searched sequentially and only the first to match the given field is used.
|
||||||
|
Conditionals are searched in reverse order that they were registered.
|
||||||
|
"""
|
||||||
|
if precedence not in self._sw_conditionals:
|
||||||
|
self._sw_conditionals[precedence] = []
|
||||||
|
self._sw_conditionals[precedence].append(conditional)
|
||||||
|
|
||||||
|
|
||||||
|
def init_conditionals(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize all possible conditionals here.
|
||||||
|
|
||||||
|
Remember: The order in which conditionals are added matters within the
|
||||||
|
same assignment precedence.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: Add all the other things
|
||||||
|
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
||||||
|
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
|
||||||
|
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
|
||||||
|
|
||||||
|
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
|
||||||
|
|
||||||
|
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
|
||||||
|
|
||||||
|
self.add_hw_conditional(hw_set_clr.HWSet(self.exp), AssignmentPrecedence.HWSET)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
|
||||||
|
result = []
|
||||||
|
precedences = sorted(conditionals.keys(), reverse=True)
|
||||||
|
for precedence in precedences:
|
||||||
|
for conditional in conditionals[precedence]:
|
||||||
|
if conditional.is_match(field):
|
||||||
|
result.append(conditional)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
|
||||||
|
"""
|
||||||
|
Get a list of NextStateConditional objects that apply to the given field.
|
||||||
|
|
||||||
|
The returned list is sorted in priority order - the conditional with highest
|
||||||
|
precedence is first in the list.
|
||||||
|
"""
|
||||||
|
sw_precedence = (field.get_property('precedence') == PrecedenceType.sw)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if sw_precedence:
|
||||||
|
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
|
||||||
|
|
||||||
|
result.extend(self._get_X_conditionals(self._hw_conditionals, field))
|
||||||
|
|
||||||
|
if not sw_precedence:
|
||||||
|
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class AssignmentPrecedence(enum.IntEnum):
|
|||||||
SW_ONWRITE = 4000
|
SW_ONWRITE = 4000
|
||||||
|
|
||||||
# Hardware access assignment groups
|
# Hardware access assignment groups
|
||||||
HW_WE = 3000
|
HW_WRITE = 3000
|
||||||
HWSET = 2000
|
HWSET = 2000
|
||||||
HWCLR = 1000
|
HWCLR = 1000
|
||||||
COUNTER_INCR_DECR = 0
|
COUNTER_INCR_DECR = 0
|
||||||
@@ -62,8 +62,10 @@ class NextStateConditional:
|
|||||||
<assignments>
|
<assignments>
|
||||||
end
|
end
|
||||||
"""
|
"""
|
||||||
def __init__(self, exporter:'RegblockExporter'):
|
|
||||||
self.exporter = exporter
|
comment = ""
|
||||||
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
|
self.exp = exp
|
||||||
|
|
||||||
def is_match(self, field: 'FieldNode') -> bool:
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -74,9 +76,9 @@ class NextStateConditional:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_field_path(self, field:'FieldNode') -> str:
|
def get_field_path(self, field:'FieldNode') -> str:
|
||||||
return get_indexed_path(self.exporter.top_node, field)
|
return get_indexed_path(self.exp.top_node, field)
|
||||||
|
|
||||||
def get_conditional(self, field: 'FieldNode') -> str:
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
"""
|
"""
|
||||||
Returns the rendered conditional text
|
Returns the rendered conditional text
|
||||||
"""
|
"""
|
||||||
@@ -92,4 +94,8 @@ class NextStateConditional:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
|
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
|
||||||
|
"""
|
||||||
|
Return any additional combinational signals that this conditional
|
||||||
|
will assign if present.
|
||||||
|
"""
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -1,176 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from systemrdl.rdltypes import PrecedenceType
|
|
||||||
|
|
||||||
from .bases import AssignmentPrecedence, NextStateConditional
|
|
||||||
from . import sw_onread
|
|
||||||
from . import sw_onwrite
|
|
||||||
|
|
||||||
from ..struct_generator import RDLStructGenerator
|
|
||||||
from ..forloop_generator import RDLForLoopGenerator
|
|
||||||
from ..utils import get_indexed_path, get_always_ff_event
|
|
||||||
from ..signals import RDLSignal
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from systemrdl.node import FieldNode, AddrmapNode
|
|
||||||
|
|
||||||
from ..exporter import RegblockExporter
|
|
||||||
|
|
||||||
|
|
||||||
class FieldBuilder:
|
|
||||||
|
|
||||||
def __init__(self, exporter:'RegblockExporter'):
|
|
||||||
self.exporter = exporter
|
|
||||||
|
|
||||||
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
|
||||||
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
|
|
||||||
|
|
||||||
self.init_conditionals()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def top_node(self) -> 'AddrmapNode':
|
|
||||||
return self.exporter.top_node
|
|
||||||
|
|
||||||
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
|
|
||||||
# TODO: Add docstring!
|
|
||||||
if precedence not in self._hw_conditionals:
|
|
||||||
self._hw_conditionals[precedence] = []
|
|
||||||
self._hw_conditionals[precedence].append(conditional)
|
|
||||||
|
|
||||||
|
|
||||||
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
|
|
||||||
# TODO: Add docstring!
|
|
||||||
if precedence not in self._sw_conditionals:
|
|
||||||
self._sw_conditionals[precedence] = []
|
|
||||||
self._sw_conditionals[precedence].append(conditional)
|
|
||||||
|
|
||||||
|
|
||||||
def init_conditionals(self) -> None:
|
|
||||||
# TODO: Add docstring!
|
|
||||||
|
|
||||||
# TODO: Add all the other things
|
|
||||||
self.add_sw_conditional(sw_onread.ClearOnRead(self.exporter), AssignmentPrecedence.SW_ONREAD)
|
|
||||||
self.add_sw_conditional(sw_onread.SetOnRead(self.exporter), AssignmentPrecedence.SW_ONREAD)
|
|
||||||
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteOneSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteOneClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteOneToggle(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteZeroSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteZeroClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteZeroToggle(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.WriteSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
self.add_hw_conditional(sw_onwrite.Write(self.exporter), AssignmentPrecedence.SW_ONWRITE)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
|
|
||||||
result = []
|
|
||||||
precedences = sorted(conditionals.keys(), reverse=True)
|
|
||||||
for precedence in precedences:
|
|
||||||
for conditional in conditionals[precedence]:
|
|
||||||
if conditional.is_match(field):
|
|
||||||
result.append(conditional)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
|
|
||||||
# TODO: Add docstring! - list of NextStateConditional. Highest precedence comes first
|
|
||||||
sw_precedence = (field.get_property('precedence') == PrecedenceType.sw)
|
|
||||||
result = []
|
|
||||||
|
|
||||||
if sw_precedence:
|
|
||||||
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
|
|
||||||
|
|
||||||
result.extend(self._get_X_conditionals(self._hw_conditionals, field))
|
|
||||||
|
|
||||||
if not sw_precedence:
|
|
||||||
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class CombinationalStructGenerator(RDLStructGenerator):
|
|
||||||
|
|
||||||
def __init__(self, field_builder: FieldBuilder):
|
|
||||||
super().__init__()
|
|
||||||
self.field_builder = field_builder
|
|
||||||
|
|
||||||
|
|
||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
|
||||||
# If a field doesn't implement storage, it is not relevant here
|
|
||||||
if not node.implements_storage:
|
|
||||||
return
|
|
||||||
|
|
||||||
# collect any extra combo signals that this field requires
|
|
||||||
extra_combo_signals = OrderedDict()
|
|
||||||
for conditional in self.field_builder.get_conditionals(node):
|
|
||||||
for signal in conditional.get_extra_combo_signals(node):
|
|
||||||
if signal.name in extra_combo_signals:
|
|
||||||
# Assert that subsequent declarations of the same signal
|
|
||||||
# are identical
|
|
||||||
assert signal == extra_combo_signals[signal.name]
|
|
||||||
else:
|
|
||||||
extra_combo_signals[signal.name] = signal
|
|
||||||
|
|
||||||
self.push_struct(node.inst_name)
|
|
||||||
self.add_member("next", node.width)
|
|
||||||
self.add_member("load_next")
|
|
||||||
for signal in extra_combo_signals.values():
|
|
||||||
self.add_member(signal.name, signal.width)
|
|
||||||
self.pop_struct()
|
|
||||||
|
|
||||||
|
|
||||||
class FieldStorageStructGenerator(RDLStructGenerator):
|
|
||||||
|
|
||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
|
||||||
if node.implements_storage:
|
|
||||||
self.add_member(node.inst_name, node.width)
|
|
||||||
|
|
||||||
|
|
||||||
class FieldLogicGenerator(RDLForLoopGenerator):
|
|
||||||
i_type = "genvar"
|
|
||||||
def __init__(self, field_builder: FieldBuilder):
|
|
||||||
super().__init__()
|
|
||||||
self.field_builder = field_builder
|
|
||||||
self.template = self.field_builder.exporter.jj_env.get_template(
|
|
||||||
"field_logic/templates/field_storage.sv"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
|
||||||
# If a field doesn't implement storage, it is not relevant here
|
|
||||||
if not node.implements_storage:
|
|
||||||
return
|
|
||||||
|
|
||||||
conditionals = self.field_builder.get_conditionals(node)
|
|
||||||
extra_combo_signals = OrderedDict()
|
|
||||||
for conditional in conditionals:
|
|
||||||
for signal in conditional.get_extra_combo_signals(node):
|
|
||||||
extra_combo_signals[signal.name] = signal
|
|
||||||
|
|
||||||
reset_value = node.get_property("reset") or 0
|
|
||||||
|
|
||||||
sig = node.get_property("resetsignal")
|
|
||||||
if sig is not None:
|
|
||||||
resetsignal = RDLSignal(sig)
|
|
||||||
else:
|
|
||||||
resetsignal = self.field_builder.exporter.default_resetsignal
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'node': node,
|
|
||||||
'reset': self.field_builder.exporter.dereferencer.get_value(reset_value),
|
|
||||||
'field_path': get_indexed_path(self.field_builder.top_node, node),
|
|
||||||
'extra_combo_signals': extra_combo_signals,
|
|
||||||
'conditionals': conditionals,
|
|
||||||
'resetsignal': resetsignal,
|
|
||||||
'get_always_ff_event': get_always_ff_event,
|
|
||||||
'has_value_output': self.field_builder.exporter.hwif.has_value_output,
|
|
||||||
'get_output_identifier': self.field_builder.exporter.hwif.get_output_identifier,
|
|
||||||
|
|
||||||
}
|
|
||||||
self.add_content(self.template.render(context))
|
|
||||||
99
peakrdl/regblock/field_logic/generators.py
Normal file
99
peakrdl/regblock/field_logic/generators.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from ..struct_generator import RDLStructGenerator
|
||||||
|
from ..forloop_generator import RDLForLoopGenerator
|
||||||
|
from ..utils import get_indexed_path, get_always_ff_event
|
||||||
|
from ..signals import RDLSignal
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import FieldLogic
|
||||||
|
from systemrdl.node import FieldNode
|
||||||
|
|
||||||
|
class CombinationalStructGenerator(RDLStructGenerator):
|
||||||
|
|
||||||
|
def __init__(self, field_logic: 'FieldLogic'):
|
||||||
|
super().__init__()
|
||||||
|
self.field_logic = field_logic
|
||||||
|
|
||||||
|
|
||||||
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
# If a field doesn't implement storage, it is not relevant here
|
||||||
|
if not node.implements_storage:
|
||||||
|
return
|
||||||
|
|
||||||
|
# collect any extra combo signals that this field requires
|
||||||
|
extra_combo_signals = OrderedDict()
|
||||||
|
for conditional in self.field_logic.get_conditionals(node):
|
||||||
|
for signal in conditional.get_extra_combo_signals(node):
|
||||||
|
if signal.name in extra_combo_signals:
|
||||||
|
# Assert that subsequent declarations of the same signal
|
||||||
|
# are identical
|
||||||
|
assert signal == extra_combo_signals[signal.name]
|
||||||
|
else:
|
||||||
|
extra_combo_signals[signal.name] = signal
|
||||||
|
|
||||||
|
self.push_struct(node.inst_name)
|
||||||
|
self.add_member("next", node.width)
|
||||||
|
self.add_member("load_next")
|
||||||
|
for signal in extra_combo_signals.values():
|
||||||
|
self.add_member(signal.name, signal.width)
|
||||||
|
self.pop_struct()
|
||||||
|
|
||||||
|
|
||||||
|
class FieldStorageStructGenerator(RDLStructGenerator):
|
||||||
|
|
||||||
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
if node.implements_storage:
|
||||||
|
self.add_member(node.inst_name, node.width)
|
||||||
|
|
||||||
|
|
||||||
|
class FieldLogicGenerator(RDLForLoopGenerator):
|
||||||
|
i_type = "genvar"
|
||||||
|
def __init__(self, field_logic: 'FieldLogic'):
|
||||||
|
super().__init__()
|
||||||
|
self.field_logic = field_logic
|
||||||
|
self.template = self.field_logic.exp.jj_env.get_template(
|
||||||
|
"field_logic/templates/field_storage.sv"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
# If a field doesn't implement storage, it is not relevant here
|
||||||
|
if not node.implements_storage:
|
||||||
|
return
|
||||||
|
|
||||||
|
conditionals = self.field_logic.get_conditionals(node)
|
||||||
|
extra_combo_signals = OrderedDict()
|
||||||
|
for conditional in conditionals:
|
||||||
|
for signal in conditional.get_extra_combo_signals(node):
|
||||||
|
extra_combo_signals[signal.name] = signal
|
||||||
|
|
||||||
|
sig = node.get_property("resetsignal")
|
||||||
|
if sig is not None:
|
||||||
|
resetsignal = RDLSignal(sig)
|
||||||
|
else:
|
||||||
|
resetsignal = self.field_logic.exp.default_resetsignal
|
||||||
|
|
||||||
|
reset_value = node.get_property("reset")
|
||||||
|
if reset_value is not None:
|
||||||
|
reset_value_str = self.field_logic.exp.dereferencer.get_value(reset_value)
|
||||||
|
else:
|
||||||
|
# 5.9.1-g: If no reset value given, the field is not reset, even if it has a resetsignal.
|
||||||
|
reset_value_str = None
|
||||||
|
resetsignal = None
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'node': node,
|
||||||
|
'reset': reset_value_str,
|
||||||
|
'field_path': get_indexed_path(self.field_logic.top_node, node),
|
||||||
|
'extra_combo_signals': extra_combo_signals,
|
||||||
|
'conditionals': conditionals,
|
||||||
|
'resetsignal': resetsignal,
|
||||||
|
'get_always_ff_event': get_always_ff_event,
|
||||||
|
'has_value_output': self.field_logic.exp.hwif.has_value_output,
|
||||||
|
'get_output_identifier': self.field_logic.exp.hwif.get_output_identifier,
|
||||||
|
|
||||||
|
}
|
||||||
|
self.add_content(self.template.render(context))
|
||||||
76
peakrdl/regblock/field_logic/hw_set_clr.py
Normal file
76
peakrdl/regblock/field_logic/hw_set_clr.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from typing import TYPE_CHECKING, List
|
||||||
|
|
||||||
|
from .bases import NextStateConditional
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from systemrdl.node import FieldNode
|
||||||
|
|
||||||
|
|
||||||
|
class HWSet(NextStateConditional):
|
||||||
|
comment = "HW Set"
|
||||||
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
|
return bool(field.get_property('hwset'))
|
||||||
|
|
||||||
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
|
prop = field.get_property('hwset')
|
||||||
|
if isinstance(prop, bool):
|
||||||
|
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwset")
|
||||||
|
else:
|
||||||
|
# signal or field
|
||||||
|
identifier = self.exp.dereferencer.get_value(prop)
|
||||||
|
return identifier
|
||||||
|
|
||||||
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
|
field_path = self.get_field_path(field)
|
||||||
|
|
||||||
|
hwmask = field.get_property('hwmask')
|
||||||
|
hwenable = field.get_property('hwenable')
|
||||||
|
R = f"field_storage.{field_path}"
|
||||||
|
if hwmask is not None:
|
||||||
|
M = self.exp.dereferencer.get_value(hwmask)
|
||||||
|
next_val = f"{R} | ~{M}"
|
||||||
|
elif hwenable is not None:
|
||||||
|
E = self.exp.dereferencer.get_value(hwenable)
|
||||||
|
next_val = f"{R} | {E}"
|
||||||
|
else:
|
||||||
|
next_val = "'1"
|
||||||
|
|
||||||
|
return [
|
||||||
|
f"field_combo.{field_path}.next = {next_val};",
|
||||||
|
f"field_combo.{field_path}.load_next = '1;",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class HWClear(NextStateConditional):
|
||||||
|
comment = "HW Clear"
|
||||||
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
|
return bool(field.get_property('hwclr'))
|
||||||
|
|
||||||
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
|
prop = field.get_property('hwclr')
|
||||||
|
if isinstance(prop, bool):
|
||||||
|
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwclr")
|
||||||
|
else:
|
||||||
|
# signal or field
|
||||||
|
identifier = self.exp.dereferencer.get_value(prop)
|
||||||
|
return identifier
|
||||||
|
|
||||||
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
|
field_path = self.get_field_path(field)
|
||||||
|
|
||||||
|
hwmask = field.get_property('hwmask')
|
||||||
|
hwenable = field.get_property('hwenable')
|
||||||
|
R = f"field_storage.{field_path}"
|
||||||
|
if hwmask is not None:
|
||||||
|
M = self.exp.dereferencer.get_value(hwmask)
|
||||||
|
next_val = f"{R} & {M}"
|
||||||
|
elif hwenable is not None:
|
||||||
|
E = self.exp.dereferencer.get_value(hwenable)
|
||||||
|
next_val = f"{R} & ~{E}"
|
||||||
|
else:
|
||||||
|
next_val = "'0"
|
||||||
|
|
||||||
|
return [
|
||||||
|
f"field_combo.{field_path}.next = {next_val};",
|
||||||
|
f"field_combo.{field_path}.load_next = '1;",
|
||||||
|
]
|
||||||
78
peakrdl/regblock/field_logic/hw_write.py
Normal file
78
peakrdl/regblock/field_logic/hw_write.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from typing import TYPE_CHECKING, List
|
||||||
|
|
||||||
|
from .bases import NextStateConditional
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from systemrdl.node import FieldNode
|
||||||
|
|
||||||
|
|
||||||
|
class AlwaysWrite(NextStateConditional):
|
||||||
|
"""
|
||||||
|
hw writable, without any qualifying we/wel
|
||||||
|
"""
|
||||||
|
comment = "HW Write"
|
||||||
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
|
return (
|
||||||
|
field.is_hw_writable
|
||||||
|
and not field.get_property('we')
|
||||||
|
and not field.get_property('wel')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
|
# TODO: make exporter promote this to an "else"?
|
||||||
|
return "1"
|
||||||
|
|
||||||
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
|
field_path = self.get_field_path(field)
|
||||||
|
|
||||||
|
hwmask = field.get_property('hwmask')
|
||||||
|
hwenable = field.get_property('hwenable')
|
||||||
|
I = self.exp.hwif.get_input_identifier(field)
|
||||||
|
R = f"field_storage.{field_path}"
|
||||||
|
if hwmask is not None:
|
||||||
|
M = self.exp.dereferencer.get_value(hwmask)
|
||||||
|
next_val = f"{I} & ~{M} | {R} & {M}"
|
||||||
|
elif hwenable is not None:
|
||||||
|
E = self.exp.dereferencer.get_value(hwenable)
|
||||||
|
next_val = f"{I} & {E} | {R} & ~{E}"
|
||||||
|
else:
|
||||||
|
next_val = self.exp.hwif.get_input_identifier(field)
|
||||||
|
|
||||||
|
return [
|
||||||
|
f"field_combo.{field_path}.next = {next_val};",
|
||||||
|
f"field_combo.{field_path}.load_next = '1;",
|
||||||
|
]
|
||||||
|
|
||||||
|
class WEWrite(AlwaysWrite):
|
||||||
|
comment = "HW Write - we"
|
||||||
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
|
return (
|
||||||
|
field.is_hw_writable
|
||||||
|
and field.get_property('we')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
|
prop = field.get_property('we')
|
||||||
|
if isinstance(prop, bool):
|
||||||
|
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "we")
|
||||||
|
else:
|
||||||
|
# signal or field
|
||||||
|
identifier = self.exp.dereferencer.get_value(prop)
|
||||||
|
return identifier
|
||||||
|
|
||||||
|
class WELWrite(AlwaysWrite):
|
||||||
|
comment = "HW Write - wel"
|
||||||
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
|
return (
|
||||||
|
field.is_hw_writable
|
||||||
|
and field.get_property('wel')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
|
prop = field.get_property('wel')
|
||||||
|
if isinstance(prop, bool):
|
||||||
|
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "wel")
|
||||||
|
else:
|
||||||
|
# signal or field
|
||||||
|
identifier = self.exp.dereferencer.get_value(prop)
|
||||||
|
return f"!{identifier}"
|
||||||
@@ -12,11 +12,12 @@ class _OnRead(NextStateConditional):
|
|||||||
def is_match(self, field: 'FieldNode') -> bool:
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
return field.get_property("onread") == self.onreadtype
|
return field.get_property("onread") == self.onreadtype
|
||||||
|
|
||||||
def get_conditional(self, field: 'FieldNode') -> str:
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
strb = self.exporter.dereferencer.get_access_strobe(field)
|
strb = self.exp.dereferencer.get_access_strobe(field)
|
||||||
return f"decoded_req && !decoded_req_is_wr && {strb}"
|
return f"decoded_req && !decoded_req_is_wr && {strb}"
|
||||||
|
|
||||||
class ClearOnRead(_OnRead):
|
class ClearOnRead(_OnRead):
|
||||||
|
comment = "SW clear on read"
|
||||||
onreadtype = OnReadType.rclr
|
onreadtype = OnReadType.rclr
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -28,6 +29,7 @@ class ClearOnRead(_OnRead):
|
|||||||
|
|
||||||
|
|
||||||
class SetOnRead(_OnRead):
|
class SetOnRead(_OnRead):
|
||||||
|
comment = "SW set on read"
|
||||||
onreadtype = OnReadType.rset
|
onreadtype = OnReadType.rset
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
|
|||||||
@@ -7,16 +7,19 @@ from .bases import NextStateConditional
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import FieldNode
|
from systemrdl.node import FieldNode
|
||||||
|
|
||||||
|
# TODO: implement sw=w1 "write once" fields
|
||||||
|
|
||||||
class _OnWrite(NextStateConditional):
|
class _OnWrite(NextStateConditional):
|
||||||
onwritetype = None
|
onwritetype = None
|
||||||
def is_match(self, field: 'FieldNode') -> bool:
|
def is_match(self, field: 'FieldNode') -> bool:
|
||||||
return field.get_property("onwrite") == self.onwritetype
|
return field.get_property("onwrite") == self.onwritetype
|
||||||
|
|
||||||
def get_conditional(self, field: 'FieldNode') -> str:
|
def get_predicate(self, field: 'FieldNode') -> str:
|
||||||
strb = self.exporter.dereferencer.get_access_strobe(field)
|
strb = self.exp.dereferencer.get_access_strobe(field)
|
||||||
return f"decoded_req && decoded_req_is_wr && {strb}"
|
return f"decoded_req && decoded_req_is_wr && {strb}"
|
||||||
|
|
||||||
class WriteOneSet(_OnWrite):
|
class WriteOneSet(_OnWrite):
|
||||||
|
comment = "SW write 1 set"
|
||||||
onwritetype = OnWriteType.woset
|
onwritetype = OnWriteType.woset
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -27,6 +30,7 @@ class WriteOneSet(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteOneClear(_OnWrite):
|
class WriteOneClear(_OnWrite):
|
||||||
|
comment = "SW write 1 clear"
|
||||||
onwritetype = OnWriteType.woclr
|
onwritetype = OnWriteType.woclr
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -37,6 +41,7 @@ class WriteOneClear(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteOneToggle(_OnWrite):
|
class WriteOneToggle(_OnWrite):
|
||||||
|
comment = "SW write 1 toggle"
|
||||||
onwritetype = OnWriteType.wot
|
onwritetype = OnWriteType.wot
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -47,6 +52,7 @@ class WriteOneToggle(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroSet(_OnWrite):
|
class WriteZeroSet(_OnWrite):
|
||||||
|
comment = "SW write 0 set"
|
||||||
onwritetype = OnWriteType.wzs
|
onwritetype = OnWriteType.wzs
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -57,6 +63,7 @@ class WriteZeroSet(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroClear(_OnWrite):
|
class WriteZeroClear(_OnWrite):
|
||||||
|
comment = "SW write 0 clear"
|
||||||
onwritetype = OnWriteType.wzc
|
onwritetype = OnWriteType.wzc
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -67,6 +74,7 @@ class WriteZeroClear(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteZeroToggle(_OnWrite):
|
class WriteZeroToggle(_OnWrite):
|
||||||
|
comment = "SW write 0 toggle"
|
||||||
onwritetype = OnWriteType.wzt
|
onwritetype = OnWriteType.wzt
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -77,6 +85,7 @@ class WriteZeroToggle(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteClear(_OnWrite):
|
class WriteClear(_OnWrite):
|
||||||
|
comment = "SW write clear"
|
||||||
onwritetype = OnWriteType.wclr
|
onwritetype = OnWriteType.wclr
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -87,6 +96,7 @@ class WriteClear(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class WriteSet(_OnWrite):
|
class WriteSet(_OnWrite):
|
||||||
|
comment = "SW write set"
|
||||||
onwritetype = OnWriteType.wset
|
onwritetype = OnWriteType.wset
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
@@ -97,6 +107,7 @@ class WriteSet(_OnWrite):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class Write(_OnWrite):
|
class Write(_OnWrite):
|
||||||
|
comment = "SW write"
|
||||||
onwritetype = None
|
onwritetype = None
|
||||||
|
|
||||||
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
def get_assignments(self, field: 'FieldNode') -> List[str]:
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Field: {{node.get_path()}}
|
// Field: {{node.get_path()}}
|
||||||
always_comb begin
|
always_comb begin
|
||||||
field_combo.{{field_path}}.next = '0;
|
field_combo.{{field_path}}.next = field_storage.{{field_path}};
|
||||||
field_combo.{{field_path}}.load_next = '0;
|
field_combo.{{field_path}}.load_next = '0;
|
||||||
{%- for signal in extra_combo_signals %}
|
{%- for signal in extra_combo_signals %}
|
||||||
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
|
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- for conditional in conditionals %}
|
{% for conditional in conditionals %}
|
||||||
{% if not loop.first %}end else {% endif %}if({{conditional.get_conditional(node)}}) begin
|
{%- if not loop.first %} else {% endif %}if({{conditional.get_predicate(node)}}) begin // {{conditional.comment}}
|
||||||
{%- for assignment in conditional.get_assignments(node) %}
|
{%- for assignment in conditional.get_assignments(node) %}
|
||||||
{{assignment|indent}}
|
{{assignment|indent}}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
@@ -14,9 +14,10 @@ always_comb begin
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
end
|
end
|
||||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||||
|
{% if resetsignal is not none -%}
|
||||||
if({{resetsignal.activehigh_identifier}}) begin
|
if({{resetsignal.activehigh_identifier}}) begin
|
||||||
field_storage.{{field_path}} <= {{reset}};
|
field_storage.{{field_path}} <= {{reset}};
|
||||||
end else if(field_combo.{{field_path}}.load_next) begin
|
end else {% endif %}if(field_combo.{{field_path}}.load_next) begin
|
||||||
field_storage.{{field_path}} <= field_combo.{{field_path}}.next;
|
field_storage.{{field_path}} <= field_combo.{{field_path}}.next;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ class Hwif:
|
|||||||
- 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', package_name: str):
|
def __init__(self, exp: 'RegblockExporter', package_name: str):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
self.package_name = package_name
|
self.package_name = package_name
|
||||||
|
|
||||||
self.has_input_struct = None
|
self.has_input_struct = None
|
||||||
@@ -26,22 +26,17 @@ class Hwif:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def top_node(self) -> AddrmapNode:
|
def top_node(self) -> AddrmapNode:
|
||||||
return self.exporter.top_node
|
return self.exp.top_node
|
||||||
|
|
||||||
|
|
||||||
def get_package_declaration(self) -> str:
|
def get_package_contents(self) -> str:
|
||||||
"""
|
"""
|
||||||
If this hwif requires a package, generate the string
|
If this hwif requires a package, generate the string
|
||||||
"""
|
"""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
lines.append(f"package {self.package_name};")
|
|
||||||
self._indent_level += 1
|
|
||||||
self.has_input_struct = self._do_struct_addressable(lines, self.top_node, is_input=True)
|
self.has_input_struct = self._do_struct_addressable(lines, self.top_node, is_input=True)
|
||||||
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
|
|
||||||
lines.append("")
|
|
||||||
lines.append("endpackage")
|
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
@@ -259,6 +254,12 @@ class Hwif:
|
|||||||
raise RuntimeError("Unhandled reference to: %s", obj)
|
raise RuntimeError("Unhandled reference to: %s", obj)
|
||||||
|
|
||||||
|
|
||||||
|
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
|
||||||
|
assert prop in {'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel'}
|
||||||
|
path = get_indexed_path(self.top_node, field)
|
||||||
|
return "hwif_in." + path + "." + prop
|
||||||
|
|
||||||
|
|
||||||
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
|
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the identifier string that best represents the output object.
|
Returns the identifier string that best represents the output object.
|
||||||
@@ -279,3 +280,9 @@ class Hwif:
|
|||||||
return "hwif_out." + path + "." + obj.name
|
return "hwif_out." + path + "." + obj.name
|
||||||
|
|
||||||
raise RuntimeError("Unhandled reference to: %s", obj)
|
raise RuntimeError("Unhandled reference to: %s", obj)
|
||||||
|
|
||||||
|
|
||||||
|
def get_implied_prop_output_identifier(self, field: FieldNode, prop: str) -> str:
|
||||||
|
assert prop in {"anded", "ored", "xored", "swmod", "swacc"}
|
||||||
|
path = get_indexed_path(self.top_node, field)
|
||||||
|
return "hwif_out." + path + "." + prop
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
|
// TODO: Add a banner
|
||||||
{{hwif.get_package_declaration()}}
|
|
||||||
|
|
||||||
module {{module_name}} (
|
module {{module_name}} (
|
||||||
input wire clk,
|
input wire clk,
|
||||||
{%- for signal in reset_signals %}
|
{%- for signal in reset_signals %}
|
||||||
|
|||||||
4
peakrdl/regblock/package_tmpl.sv
Normal file
4
peakrdl/regblock/package_tmpl.sv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// TODO: Add a banner
|
||||||
|
package {{hwif.package_name}};
|
||||||
|
{{hwif.get_package_contents()|indent}}
|
||||||
|
endpackage
|
||||||
@@ -8,12 +8,12 @@ if TYPE_CHECKING:
|
|||||||
from .exporter import RegblockExporter
|
from .exporter import RegblockExporter
|
||||||
|
|
||||||
class ReadbackMux:
|
class ReadbackMux:
|
||||||
def __init__(self, exporter:'RegblockExporter'):
|
def __init__(self, exp:'RegblockExporter'):
|
||||||
self.exporter = exporter
|
self.exp = exp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def top_node(self) -> AddrmapNode:
|
def top_node(self) -> AddrmapNode:
|
||||||
return self.exporter.top_node
|
return self.exp.top_node
|
||||||
|
|
||||||
def get_implementation(self) -> str:
|
def get_implementation(self) -> str:
|
||||||
# TODO: Count the number of readable registers
|
# TODO: Count the number of readable registers
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import re
|
import re
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
import textwrap
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import Node
|
from systemrdl.node import Node
|
||||||
from .signals import SignalBase
|
from .signals import SignalBase
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
||||||
"""
|
"""
|
||||||
@@ -22,7 +22,9 @@ def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
|||||||
return re.sub(r'!', repl(), path)
|
return re.sub(r'!', repl(), path)
|
||||||
|
|
||||||
|
|
||||||
def get_always_ff_event(resetsignal: 'SignalBase') -> str:
|
def get_always_ff_event(resetsignal: 'Optional[SignalBase]') -> str:
|
||||||
|
if resetsignal is None:
|
||||||
|
return "@(posedge clk)"
|
||||||
if resetsignal.is_async and resetsignal.is_activehigh:
|
if resetsignal.is_async and resetsignal.is_activehigh:
|
||||||
return f"@(posedge clk or posedge {resetsignal.identifier})"
|
return f"@(posedge clk or posedge {resetsignal.identifier})"
|
||||||
elif resetsignal.is_async and not resetsignal.is_activehigh:
|
elif resetsignal.is_async and not resetsignal.is_activehigh:
|
||||||
|
|||||||
2
tst.py
2
tst.py
@@ -18,4 +18,4 @@ except RDLCompileError:
|
|||||||
|
|
||||||
R = RegblockExporter()
|
R = RegblockExporter()
|
||||||
|
|
||||||
R.export(root, "out.sv")
|
R.export(root, ".")
|
||||||
|
|||||||
26
x.rdl
26
x.rdl
@@ -2,26 +2,14 @@
|
|||||||
addrmap top {
|
addrmap top {
|
||||||
reg {
|
reg {
|
||||||
field {
|
field {
|
||||||
hw=r; sw=rw;
|
hw=w; sw=rw; precedence=sw;
|
||||||
} y = 0;
|
} y = 0;
|
||||||
} whee[2][8];
|
|
||||||
|
|
||||||
regfile {
|
|
||||||
reg {
|
|
||||||
field {
|
field {
|
||||||
hw=w; sw=r;
|
hw=na; sw=rw;
|
||||||
} abc[16:2] = 20;
|
} x = 1;
|
||||||
field {
|
|
||||||
hw=r; sw=rw;
|
y->we;
|
||||||
} def[4] = 0;
|
y->hwset;
|
||||||
} aaa[4];
|
} whee;
|
||||||
reg {
|
|
||||||
field {
|
|
||||||
hw=r; sw=rw;
|
|
||||||
} abc = 0;
|
|
||||||
field {
|
|
||||||
hw=r; sw=rw;
|
|
||||||
} def = 0;
|
|
||||||
} bbb;
|
|
||||||
} asdf[20];
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user