Signals working!

This commit is contained in:
Alex Mykyta
2021-12-15 22:03:57 -08:00
parent 7d0130078d
commit 769907404a
22 changed files with 376 additions and 212 deletions

View File

@@ -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;

View File

@@ -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,
}

View File

@@ -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"

View File

@@ -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,
}

View File

@@ -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))

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,
}

View File

@@ -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",

View File

@@ -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

View File

@@ -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: