Signals working!
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// Request
|
||||
logic is_active;
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{cpuif.reset.activehigh_identifier}}) begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
is_active <= '0;
|
||||
cpuif_req <= '0;
|
||||
cpuif_req_is_wr <= '0;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from ..utils import get_always_ff_event, clog2
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..exporter import RegblockExporter
|
||||
from ..signals import SignalBase
|
||||
from systemrdl import SignalNode
|
||||
|
||||
class CpuifBase:
|
||||
template_path = "cpuif/base_tmpl.sv"
|
||||
|
||||
def __init__(self, exp:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
|
||||
def __init__(self, exp:'RegblockExporter', cpuif_reset:Optional['SignalNode'], data_width:int=32, addr_width:int=32):
|
||||
self.exp = exp
|
||||
self.reset = cpuif_reset
|
||||
self.data_width = data_width
|
||||
@@ -22,7 +22,8 @@ class CpuifBase:
|
||||
def get_implementation(self) -> str:
|
||||
context = {
|
||||
"cpuif": self,
|
||||
"get_always_ff_event": get_always_ff_event,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
|
||||
"clog2": clog2,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TYPE_CHECKING, Union
|
||||
from typing import TYPE_CHECKING, Union, Optional
|
||||
from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode
|
||||
from systemrdl.rdltypes import PropertyReference
|
||||
|
||||
@@ -200,3 +200,17 @@ class Dereferencer:
|
||||
Returns the Verilog string that represents the register's access strobe
|
||||
"""
|
||||
return self.address_decode.get_access_strobe(obj)
|
||||
|
||||
def get_resetsignal(self, obj: Optional[SignalNode]) -> str:
|
||||
"""
|
||||
Returns a normalized active-high reset signal
|
||||
"""
|
||||
if isinstance(obj, SignalNode):
|
||||
s = self.get_value(obj)
|
||||
if obj.get_property('activehigh'):
|
||||
return s
|
||||
else:
|
||||
return f"~{s}"
|
||||
|
||||
# default reset signal
|
||||
return "rst"
|
||||
|
||||
@@ -3,13 +3,11 @@ from typing import Union
|
||||
|
||||
import jinja2 as jj
|
||||
from systemrdl.node import AddrmapNode, RootNode
|
||||
from systemrdl.walker import RDLWalker
|
||||
|
||||
from .addr_decode import AddressDecode
|
||||
from .field_logic import FieldLogic
|
||||
from .dereferencer import Dereferencer
|
||||
from .readback import Readback
|
||||
from .signals import InferredSignal, RDLSignal
|
||||
|
||||
from .cpuif import CpuifBase
|
||||
from .cpuif.apb3 import APB3_Cpuif
|
||||
@@ -33,8 +31,6 @@ class RegblockExporter:
|
||||
self.field_logic = FieldLogic(self)
|
||||
self.readback = None # type: Readback
|
||||
self.dereferencer = Dereferencer(self)
|
||||
self.default_resetsignal = InferredSignal("rst")
|
||||
|
||||
|
||||
if user_template_dir:
|
||||
loader = jj.ChoiceLoader([
|
||||
@@ -84,23 +80,11 @@ class RegblockExporter:
|
||||
# Scan the design for any unsupported features
|
||||
# Also collect pre-export information
|
||||
scanner = DesignScanner(self)
|
||||
RDLWalker().walk(self.top_node, scanner)
|
||||
if scanner.msg.had_error:
|
||||
scanner.msg.fatal(
|
||||
"Unable to export due to previous errors"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
cpuif_reset_tmp = self.top_node.cpuif_reset
|
||||
if cpuif_reset_tmp:
|
||||
cpuif_reset = RDLSignal(cpuif_reset_tmp)
|
||||
else:
|
||||
cpuif_reset = self.default_resetsignal
|
||||
reset_signals = set([cpuif_reset, self.default_resetsignal])
|
||||
scanner.do_scan()
|
||||
|
||||
self.cpuif = cpuif_cls(
|
||||
self,
|
||||
cpuif_reset=cpuif_reset,
|
||||
cpuif_reset=self.top_node.cpuif_reset,
|
||||
data_width=scanner.cpuif_data_width,
|
||||
addr_width=self.top_node.size.bit_length()
|
||||
)
|
||||
@@ -108,7 +92,9 @@ class RegblockExporter:
|
||||
self.hwif = Hwif(
|
||||
self,
|
||||
package_name=package_name,
|
||||
reuse_typedefs=reuse_hwif_typedefs
|
||||
in_hier_signal_paths=scanner.in_hier_signal_paths,
|
||||
out_of_hier_signals=scanner.out_of_hier_signals,
|
||||
reuse_typedefs=reuse_hwif_typedefs,
|
||||
)
|
||||
|
||||
self.readback = Readback(
|
||||
@@ -119,15 +105,15 @@ class RegblockExporter:
|
||||
# Build Jinja template context
|
||||
context = {
|
||||
"module_name": module_name,
|
||||
"reset_signals": reset_signals,
|
||||
"user_signals": [], # TODO:
|
||||
"user_out_of_hier_signals": scanner.out_of_hier_signals.values(),
|
||||
"interrupts": [], # TODO:
|
||||
"cpuif": self.cpuif,
|
||||
"hwif": self.hwif,
|
||||
"get_resetsignal": self.dereferencer.get_resetsignal,
|
||||
"address_decode": self.address_decode,
|
||||
"field_logic": self.field_logic,
|
||||
"readback": self.readback,
|
||||
"get_always_ff_event": get_always_ff_event,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.dereferencer, resetsignal),
|
||||
"retime_read_response": retime_read_response,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ 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
|
||||
@@ -92,11 +91,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
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.exp.default_resetsignal
|
||||
resetsignal = node.get_property('resetsignal')
|
||||
|
||||
reset_value = node.get_property('reset')
|
||||
if reset_value is not None:
|
||||
@@ -114,8 +109,9 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
'extra_combo_signals': extra_combo_signals,
|
||||
'conditionals': conditionals,
|
||||
'resetsignal': resetsignal,
|
||||
'get_always_ff_event': get_always_ff_event,
|
||||
'get_always_ff_event': lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
'get_value': self.exp.dereferencer.get_value,
|
||||
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
|
||||
}
|
||||
self.add_content(self.field_storage_template.render(context))
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ always_comb begin
|
||||
field_combo.{{field_path}}.load_next = load_next_c;
|
||||
end
|
||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||
{% if resetsignal is not none -%}
|
||||
if({{resetsignal.activehigh_identifier}}) begin
|
||||
{% if reset is not none -%}
|
||||
if({{get_resetsignal(resetsignal)}}) begin
|
||||
field_storage.{{field_path}} <= {{reset}};
|
||||
end else {% endif %}if(field_combo.{{field_path}}.load_next) begin
|
||||
field_storage.{{field_path}} <= field_combo.{{field_path}}.next;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TYPE_CHECKING, Union, List
|
||||
from typing import TYPE_CHECKING, Union, List, Set, Dict
|
||||
|
||||
from systemrdl.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode
|
||||
from systemrdl.rdltypes import PropertyReference
|
||||
@@ -19,13 +19,19 @@ class Hwif:
|
||||
- Signal inputs (except those that are promoted to the top)
|
||||
"""
|
||||
|
||||
def __init__(self, exp: 'RegblockExporter', package_name: str, reuse_typedefs: bool):
|
||||
def __init__(
|
||||
self, exp: 'RegblockExporter', package_name: str,
|
||||
in_hier_signal_paths: Set[str], out_of_hier_signals: Dict[str, SignalNode],
|
||||
reuse_typedefs: bool
|
||||
):
|
||||
self.exp = exp
|
||||
self.package_name = package_name
|
||||
|
||||
self.has_input_struct = None
|
||||
self.has_output_struct = None
|
||||
self._indent_level = 0
|
||||
|
||||
self.in_hier_signal_paths = in_hier_signal_paths
|
||||
self.out_of_hier_signals = out_of_hier_signals
|
||||
|
||||
if reuse_typedefs:
|
||||
self._gen_in_cls = InputStructGenerator_TypeScope
|
||||
@@ -45,7 +51,7 @@ class Hwif:
|
||||
"""
|
||||
lines = []
|
||||
|
||||
gen_in = self._gen_in_cls(self.top_node)
|
||||
gen_in = self._gen_in_cls(self)
|
||||
structs_in = gen_in.get_struct(
|
||||
self.top_node,
|
||||
f"{self.top_node.inst_name}__in_t"
|
||||
@@ -56,7 +62,7 @@ class Hwif:
|
||||
else:
|
||||
self.has_input_struct = False
|
||||
|
||||
gen_out = self._gen_out_cls(self.top_node)
|
||||
gen_out = self._gen_out_cls(self)
|
||||
structs_out = gen_out.get_struct(
|
||||
self.top_node,
|
||||
f"{self.top_node.inst_name}__out_t"
|
||||
@@ -129,8 +135,10 @@ class Hwif:
|
||||
path = get_indexed_path(self.top_node, obj)
|
||||
return "hwif_in." + path + ".value"
|
||||
elif isinstance(obj, SignalNode):
|
||||
# TODO: Implement this
|
||||
raise NotImplementedError()
|
||||
if obj.get_path() in self.out_of_hier_signals:
|
||||
return obj.inst_name
|
||||
path = get_indexed_path(self.top_node, obj)
|
||||
return "hwif_in." + path
|
||||
elif isinstance(obj, PropertyReference):
|
||||
return self.get_implied_prop_input_identifier(obj.node, obj.name)
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@ from ..struct_generator import RDLFlatStructGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node, SignalNode, FieldNode
|
||||
from . import Hwif
|
||||
|
||||
class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
||||
def __init__(self, top_node: 'Node'):
|
||||
def __init__(self, hwif: 'Hwif'):
|
||||
super().__init__()
|
||||
self.top_node = top_node
|
||||
self.hwif = hwif
|
||||
self.top_node = hwif.top_node
|
||||
|
||||
def get_typdef_name(self, node:'Node') -> str:
|
||||
base = node.get_rel_path(
|
||||
@@ -19,7 +21,10 @@ class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
||||
return f'{base}__in_t'
|
||||
|
||||
def enter_Signal(self, node: 'SignalNode') -> None:
|
||||
self.add_member(node.inst_name, node.width)
|
||||
# only emit the signal if design scanner detected it is actually being used
|
||||
path = node.get_path()
|
||||
if path in self.hwif.in_hier_signal_paths:
|
||||
self.add_member(node.inst_name, node.width)
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
// TODO: Add a banner
|
||||
module {{module_name}} (
|
||||
input wire clk,
|
||||
{%- for signal in reset_signals %}
|
||||
{{signal.port_declaration}},
|
||||
{%- endfor %}
|
||||
input wire rst,
|
||||
|
||||
{%- for signal in user_signals %}
|
||||
{{signal.port_declaration}},
|
||||
{%- for signal in user_out_of_hier_signals %}
|
||||
{%- if signal.width == 1 %}
|
||||
input wire {{signal.inst_name}},
|
||||
{%- else %}
|
||||
input wire [{{signal.width-1}}:0] {{signal.inst_name}},
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{%- for interrupt in interrupts %}
|
||||
{{interrupt.port_declaration}},
|
||||
// TODO:
|
||||
{%- endfor %}
|
||||
|
||||
{{cpuif.port_declaration|indent(8)}}
|
||||
@@ -77,7 +79,7 @@ module {{module_name}} (
|
||||
|
||||
{% if retime_read_response %}
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{cpuif.reset.activehigh_identifier}}) begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
cpuif_rd_ack <= '0;
|
||||
cpuif_rd_data <= '0;
|
||||
cpuif_rd_err <= '0;
|
||||
|
||||
@@ -30,7 +30,7 @@ class Readback:
|
||||
context = {
|
||||
"array_assignments" : array_assignments,
|
||||
"array_size" : array_size,
|
||||
"get_always_ff_event": get_always_ff_event,
|
||||
"get_always_ff_event": lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
|
||||
"cpuif": self.exp.cpuif,
|
||||
"do_fanin_stage": self.do_fanin_stage,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from collections import OrderedDict
|
||||
|
||||
from systemrdl.walker import RDLListener
|
||||
from systemrdl.node import AddrmapNode
|
||||
from systemrdl.walker import RDLListener, RDLWalker
|
||||
from systemrdl.node import AddrmapNode, SignalNode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node, RegNode, SignalNode, MemNode
|
||||
from systemrdl.node import Node, RegNode, MemNode, FieldNode
|
||||
from .exporter import RegblockExporter
|
||||
|
||||
|
||||
@@ -20,13 +21,45 @@ class DesignScanner(RDLListener):
|
||||
self.cpuif_data_width = 0
|
||||
self.msg = exp.top_node.env.msg
|
||||
|
||||
# Collections of signals that were actually referenced by the design
|
||||
self.in_hier_signal_paths = set()
|
||||
self.out_of_hier_signals = OrderedDict()
|
||||
|
||||
def _get_out_of_hier_field_reset(self):
|
||||
current_node = self.exp.top_node.parent
|
||||
while current_node is not None:
|
||||
for signal in current_node.signals():
|
||||
if signal.get_property('field_reset'):
|
||||
path = signal.get_path()
|
||||
self.out_of_hier_signals[path] = signal
|
||||
return
|
||||
current_node = current_node.parent
|
||||
|
||||
def do_scan(self):
|
||||
# Collect cpuif reset, if any.
|
||||
cpuif_reset = self.exp.top_node.cpuif_reset
|
||||
if cpuif_reset is not None:
|
||||
path = cpuif_reset.get_path()
|
||||
rel_path = cpuif_reset.get_rel_path(self.exp.top_node)
|
||||
if rel_path.startswith("^"):
|
||||
self.out_of_hier_signals[path] = cpuif_reset
|
||||
else:
|
||||
self.in_hier_signal_paths.add(path)
|
||||
|
||||
# collect out-of-hier field_reset, if any
|
||||
self._get_out_of_hier_field_reset()
|
||||
|
||||
RDLWalker().walk(self.exp.top_node, self)
|
||||
if self.msg.had_error:
|
||||
self.msg.fatal(
|
||||
"Unable to export due to previous errors"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
def enter_Reg(self, node: 'RegNode') -> None:
|
||||
# The CPUIF's bus width is sized according to the largest register in the design
|
||||
self.cpuif_data_width = max(self.cpuif_data_width, node.get_property('regwidth'))
|
||||
|
||||
# TODO: Collect any references to signals that lie outside of the hierarchy
|
||||
# These will be added as top-level signals
|
||||
|
||||
def enter_Component(self, node: 'Node') -> None:
|
||||
if not isinstance(node, AddrmapNode) and node.external:
|
||||
self.msg.error(
|
||||
@@ -46,6 +79,21 @@ class DesignScanner(RDLListener):
|
||||
node.inst.inst_src_ref
|
||||
)
|
||||
|
||||
if node.get_property('field_reset'):
|
||||
path = node.get_path()
|
||||
self.in_hier_signal_paths.add(path)
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
for prop_name in node.list_properties():
|
||||
value = node.get_property(prop_name)
|
||||
if isinstance(value, SignalNode):
|
||||
path = value.get_path()
|
||||
rel_path = value.get_rel_path(self.exp.top_node)
|
||||
if rel_path.startswith("^"):
|
||||
self.out_of_hier_signals[path] = value
|
||||
else:
|
||||
self.in_hier_signal_paths.add(path)
|
||||
|
||||
def enter_Mem(self, node: 'MemNode') -> None:
|
||||
self.msg.error(
|
||||
"Cannot export a register block that contains a memory",
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl import SignalNode
|
||||
|
||||
class SignalBase:
|
||||
|
||||
@property
|
||||
def is_async(self) -> bool:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def is_activehigh(self) -> bool:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def activehigh_identifier(self) -> str:
|
||||
"""
|
||||
Normalizes the identifier reference to be active-high logic
|
||||
"""
|
||||
if not self.is_activehigh:
|
||||
return "~%s" % self.identifier
|
||||
return self.identifier
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
"""
|
||||
Returns the port delcaration text for this signal.
|
||||
In the context of this exporter, all signal objects happen to be inputs.
|
||||
"""
|
||||
if self.width > 1:
|
||||
return "input wire [%d:0] %s" % (self.width - 1, self.identifier)
|
||||
return "input wire %s" % self.identifier
|
||||
|
||||
|
||||
class RDLSignal(SignalBase):
|
||||
"""
|
||||
Wrapper around a SystemRDL signal object
|
||||
"""
|
||||
def __init__(self, rdl_signal:'SignalNode'):
|
||||
self.rdl_signal = rdl_signal
|
||||
|
||||
@property
|
||||
def is_async(self) -> bool:
|
||||
return self.rdl_signal.get_property('async')
|
||||
|
||||
@property
|
||||
def is_activehigh(self) -> bool:
|
||||
return self.rdl_signal.get_property('activehigh')
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
return self.rdl_signal.width
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
# TODO: uniquify this somehow
|
||||
# TODO: Deal with different hierarchies
|
||||
return "TODO_%s" % self.rdl_signal.inst_name
|
||||
|
||||
|
||||
class InferredSignal(SignalBase):
|
||||
def __init__(self, identifier:str, width:int=1, is_async:bool=False, is_activehigh=True):
|
||||
self._identifier = identifier
|
||||
self._width = width
|
||||
self._is_async = is_async
|
||||
self._is_activehigh = is_activehigh
|
||||
|
||||
@property
|
||||
def is_async(self) -> bool:
|
||||
return self._is_async
|
||||
|
||||
@property
|
||||
def is_activehigh(self) -> bool:
|
||||
return self._is_activehigh
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
return self._width
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return self._identifier
|
||||
@@ -2,9 +2,9 @@ import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node
|
||||
from .signals import SignalBase
|
||||
from systemrdl.node import Node, SignalNode
|
||||
from typing import Optional
|
||||
from .dereferencer import Dereferencer
|
||||
|
||||
def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
||||
"""
|
||||
@@ -22,13 +22,13 @@ def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
||||
return re.sub(r'!', repl(), path)
|
||||
|
||||
|
||||
def get_always_ff_event(resetsignal: 'Optional[SignalBase]') -> str:
|
||||
def get_always_ff_event(dereferencer: 'Dereferencer', resetsignal: 'Optional[SignalNode]') -> str:
|
||||
if resetsignal is None:
|
||||
return "@(posedge clk)"
|
||||
if resetsignal.is_async and resetsignal.is_activehigh:
|
||||
return f"@(posedge clk or posedge {resetsignal.identifier})"
|
||||
elif resetsignal.is_async and not resetsignal.is_activehigh:
|
||||
return f"@(posedge clk or negedge {resetsignal.identifier})"
|
||||
if resetsignal.get_property('async') and resetsignal.get_property('activehigh'):
|
||||
return f"@(posedge clk or posedge {dereferencer.get_value(resetsignal)})"
|
||||
elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'):
|
||||
return f"@(posedge clk or negedge {dereferencer.get_value(resetsignal)})"
|
||||
return "@(posedge clk)"
|
||||
|
||||
def clog2(n: int) -> int:
|
||||
|
||||
Reference in New Issue
Block a user