Signals working!
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
|
||||
================================================================================
|
||||
Signal wrapper classes
|
||||
================================================================================
|
||||
Define a signal wrapper class that is easier to use in templates.
|
||||
|
||||
Provides the following properties:
|
||||
.is_async
|
||||
.is_activehigh
|
||||
.identifier
|
||||
Returns the Verilog identifier string for this signal
|
||||
.activehigh_identifier
|
||||
Normalizes identifier to active-high logic
|
||||
same as .identifier, but prepends '~' if is_activehigh = False
|
||||
.width
|
||||
|
||||
Default reset class instance:
|
||||
Extends the base class
|
||||
Hardcodes as follows:
|
||||
.is_async = True
|
||||
.is_activehigh = True
|
||||
.identifier = "rst"
|
||||
.width = 1
|
||||
|
||||
Wrapper classes
|
||||
Wrap around a systemrdl.SignalNode
|
||||
|
||||
|
||||
================================================================================
|
||||
CPU Interface Class
|
||||
================================================================================
|
||||
Entry point class for a given CPU interface type (APB, AXI, etc..)
|
||||
|
||||
Does the following:
|
||||
- Provide linkage to the logic implementation Jinja template
|
||||
- Interface signal identifier properties
|
||||
Aliases for signal identifiers to allow flat or sv-interface style
|
||||
eg:
|
||||
self.psel --> "s_apb_psel" or "s_apb.psel"
|
||||
if sv interface, use the interface name class prpoerty
|
||||
- Port declaration text property
|
||||
declare as sv interface, or flat port list
|
||||
If flattened, should use signal identifier properties
|
||||
If sv interface, I should breakout the interface & modport name as
|
||||
class properties for easy user-override
|
||||
@@ -117,6 +117,7 @@ X incrvalue/decrvalue needs to be the same or narrower than counter itself
|
||||
|
||||
! incrwidth/decrwidth must be between 1 and the width of the counter
|
||||
|
||||
|
||||
================================================================================
|
||||
Things that need validation by this exporter
|
||||
================================================================================
|
||||
|
||||
@@ -5,11 +5,37 @@ Summary
|
||||
RTL interface that provides access to per-field context signals
|
||||
|
||||
Regarding signals:
|
||||
I think RDL-declared signals should actually be part of the hwif input
|
||||
structure.
|
||||
Exceptions:
|
||||
- if the signal instance is at the top-level, it will get promoted to the
|
||||
top level port list for convenience, and therefore omitted from the struct
|
||||
RDL-declared signals are part of the hwif input structure.
|
||||
Only include them if they are referenced by the design (need to scan the
|
||||
full design anyways, so may as well filter out unreferenced ones)
|
||||
|
||||
It is possible to use signals declared in a parent scope.
|
||||
This means that not all signals will be discovered by a hierarchical listener alone
|
||||
Need to scan ALL assigned properties for signal references too.
|
||||
- get signal associated with top node's cpuif_reset helper property, if any
|
||||
- collect all field_resets
|
||||
X check all signal instances in the hier tree
|
||||
- search parents of top node for the first field_reset signal, if any
|
||||
This is WAY less expensive than querying EACH field's resetsignal property
|
||||
X Check all explicitly assigned properties
|
||||
only need to do this for fields
|
||||
Collect all of these into the following:
|
||||
- If inside the hier, add to a list of paths
|
||||
- if outside the hier, add to a dict of path:SignalNode
|
||||
These are all the signals in-use by the design
|
||||
|
||||
Pass list into the hwif generator
|
||||
If the hwif generator encounters a signal during traversal:
|
||||
check if it exists in the signal path list
|
||||
|
||||
out-of-hier signals are inserted outside of the hwif_in as standalone signals.
|
||||
For now, just use their plain inst names. If I need to uniquify them i can add that later.
|
||||
I should at least check against a list of known "dirty words". Seems very likely someone will choose
|
||||
a signal called "rst".
|
||||
Prefix with usersig_ if needed
|
||||
|
||||
|
||||
|
||||
|
||||
================================================================================
|
||||
Naming Scheme
|
||||
|
||||
@@ -446,8 +446,8 @@ integer
|
||||
|OK|
|
||||
|
||||
reference
|
||||
|EX|
|
||||
|OK|
|
||||
|
||||
resetsignal
|
||||
^^^^^^^^^^^
|
||||
|EX|
|
||||
|OK|
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -6,7 +6,7 @@ module tb;
|
||||
logic rst = '1;
|
||||
logic clk = '0;
|
||||
initial forever begin
|
||||
#10ns;
|
||||
#5ns;
|
||||
clk = ~clk;
|
||||
end
|
||||
|
||||
|
||||
0
test/test_reset_signals/__init__.py
Normal file
0
test/test_reset_signals/__init__.py
Normal file
80
test/test_reset_signals/regblock.rdl
Normal file
80
test/test_reset_signals/regblock.rdl
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
signal {
|
||||
cpuif_reset; activehigh;
|
||||
} root_cpuif_reset;
|
||||
|
||||
signal {} r5f2_resetvalue[16];
|
||||
|
||||
addrmap top {
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f1[16] = 0x1234;
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f2[16] = 0x5678;
|
||||
} r1;
|
||||
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f1[16] = 0x1234;
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f2[16] = 0x5678;
|
||||
|
||||
signal {
|
||||
field_reset; activehigh; sync;
|
||||
} my_reset;
|
||||
} r2;
|
||||
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f1[16] = 0x1234;
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f2[16] = 0x5678;
|
||||
|
||||
signal {
|
||||
field_reset; activehigh; async;
|
||||
} my_areset;
|
||||
} r3;
|
||||
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f1[16] = 0x1234;
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f2[16] = 0x5678;
|
||||
|
||||
signal {
|
||||
field_reset; activelow; sync;
|
||||
} my_reset_n;
|
||||
} r4;
|
||||
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
} f1[16] = 0x1234;
|
||||
field {
|
||||
sw=rw; hw=na;
|
||||
reset = r5f2_resetvalue;
|
||||
} f2[16];
|
||||
|
||||
signal {
|
||||
field_reset; activelow; async;
|
||||
} my_areset_n;
|
||||
} r5;
|
||||
|
||||
signal {
|
||||
activehigh; sync;
|
||||
} f2_reset;
|
||||
|
||||
r1.f2->resetsignal = f2_reset;
|
||||
r2.f2->resetsignal = f2_reset;
|
||||
r3.f2->resetsignal = f2_reset;
|
||||
r4.f2->resetsignal = f2_reset;
|
||||
r5.f2->resetsignal = f2_reset;
|
||||
};
|
||||
128
test/test_reset_signals/tb_template.sv
Normal file
128
test/test_reset_signals/tb_template.sv
Normal file
@@ -0,0 +1,128 @@
|
||||
{% extends "lib/tb_base.sv" %}
|
||||
|
||||
{%- block declarations %}
|
||||
logic root_cpuif_reset;
|
||||
logic [15:0] r5f2_resetvalue;
|
||||
{%- endblock %}
|
||||
|
||||
{%- block clocking_dirs %}
|
||||
output root_cpuif_reset;
|
||||
output r5f2_resetvalue;
|
||||
{%- endblock %}
|
||||
|
||||
{% block seq %}
|
||||
{% sv_line_anchor %}
|
||||
cb.root_cpuif_reset <= '1;
|
||||
cb.hwif_in.r2.my_reset <= '1;
|
||||
cb.hwif_in.r3.my_areset <= '1;
|
||||
cb.hwif_in.r4.my_reset_n <= '0;
|
||||
cb.hwif_in.r5.my_areset_n <= '0;
|
||||
cb.hwif_in.f2_reset <= '1;
|
||||
cb.r5f2_resetvalue <= 'hABCD;
|
||||
##2;
|
||||
cb.rst <= '0;
|
||||
cb.root_cpuif_reset <= '0;
|
||||
cb.hwif_in.r2.my_reset <= '0;
|
||||
cb.hwif_in.r3.my_areset <= '0;
|
||||
cb.hwif_in.r4.my_reset_n <= '1;
|
||||
cb.hwif_in.r5.my_areset_n <= '1;
|
||||
cb.hwif_in.f2_reset <= '0;
|
||||
##1;
|
||||
|
||||
|
||||
cpuif.assert_read('h00, 'h5678_1234);
|
||||
cpuif.assert_read('h04, 'h5678_1234);
|
||||
cpuif.assert_read('h08, 'h5678_1234);
|
||||
cpuif.assert_read('h0c, 'h5678_1234);
|
||||
cpuif.assert_read('h10, 'hABCD_1234);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_0000);
|
||||
cpuif.assert_read('h04, 'h0000_0000);
|
||||
cpuif.assert_read('h08, 'h0000_0000);
|
||||
cpuif.assert_read('h0c, 'h0000_0000);
|
||||
cpuif.assert_read('h10, 'h0000_0000);
|
||||
|
||||
cb.rst <= '1;
|
||||
@cb;
|
||||
cb.rst <= '0;
|
||||
@cb;
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_1234);
|
||||
cpuif.assert_read('h04, 'h0000_0000);
|
||||
cpuif.assert_read('h08, 'h0000_0000);
|
||||
cpuif.assert_read('h0c, 'h0000_0000);
|
||||
cpuif.assert_read('h10, 'h0000_0000);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
cb.hwif_in.r2.my_reset <= '1;
|
||||
@cb;
|
||||
cb.hwif_in.r2.my_reset <= '0;
|
||||
@cb;
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_0000);
|
||||
cpuif.assert_read('h04, 'h0000_1234);
|
||||
cpuif.assert_read('h08, 'h0000_0000);
|
||||
cpuif.assert_read('h0c, 'h0000_0000);
|
||||
cpuif.assert_read('h10, 'h0000_0000);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
##1;
|
||||
#2ns;
|
||||
hwif_in.r3.my_areset = '1;
|
||||
#1ns;
|
||||
hwif_in.r3.my_areset = '0;
|
||||
##1;
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_0000);
|
||||
cpuif.assert_read('h04, 'h0000_0000);
|
||||
cpuif.assert_read('h08, 'h0000_1234);
|
||||
cpuif.assert_read('h0c, 'h0000_0000);
|
||||
cpuif.assert_read('h10, 'h0000_0000);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
cb.hwif_in.r4.my_reset_n <= '0;
|
||||
@cb;
|
||||
cb.hwif_in.r4.my_reset_n <= '1;
|
||||
@cb;
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_0000);
|
||||
cpuif.assert_read('h04, 'h0000_0000);
|
||||
cpuif.assert_read('h08, 'h0000_0000);
|
||||
cpuif.assert_read('h0c, 'h0000_1234);
|
||||
cpuif.assert_read('h10, 'h0000_0000);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
##1;
|
||||
#2ns;
|
||||
hwif_in.r5.my_areset_n = '0;
|
||||
#1ns;
|
||||
hwif_in.r5.my_areset_n = '1;
|
||||
##1;
|
||||
|
||||
cpuif.assert_read('h00, 'h0000_0000);
|
||||
cpuif.assert_read('h04, 'h0000_0000);
|
||||
cpuif.assert_read('h08, 'h0000_0000);
|
||||
cpuif.assert_read('h0c, 'h0000_0000);
|
||||
cpuif.assert_read('h10, 'h0000_1234);
|
||||
|
||||
for(int i=0; i<5; i++) cpuif.write(i*4, 0);
|
||||
|
||||
@cb;
|
||||
cb.hwif_in.f2_reset <= '1;
|
||||
cb.r5f2_resetvalue <= 'h3210;
|
||||
@cb;
|
||||
cb.hwif_in.f2_reset <= '0;
|
||||
|
||||
cpuif.assert_read('h00, 'h5678_0000);
|
||||
cpuif.assert_read('h04, 'h5678_0000);
|
||||
cpuif.assert_read('h08, 'h5678_0000);
|
||||
cpuif.assert_read('h0c, 'h5678_0000);
|
||||
cpuif.assert_read('h10, 'h3210_0000);
|
||||
|
||||
{% endblock %}
|
||||
5
test/test_reset_signals/testcase.py
Normal file
5
test/test_reset_signals/testcase.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..lib.regblock_testcase import RegblockTestCase
|
||||
|
||||
class Test(RegblockTestCase):
|
||||
def test_dut(self):
|
||||
self.run_test()
|
||||
Reference in New Issue
Block a user