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
|
! incrwidth/decrwidth must be between 1 and the width of the counter
|
||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Things that need validation by this exporter
|
Things that need validation by this exporter
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|||||||
@@ -5,11 +5,37 @@ Summary
|
|||||||
RTL interface that provides access to per-field context signals
|
RTL interface that provides access to per-field context signals
|
||||||
|
|
||||||
Regarding signals:
|
Regarding signals:
|
||||||
I think RDL-declared signals should actually be part of the hwif input
|
RDL-declared signals are part of the hwif input structure.
|
||||||
structure.
|
Only include them if they are referenced by the design (need to scan the
|
||||||
Exceptions:
|
full design anyways, so may as well filter out unreferenced ones)
|
||||||
- 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
|
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
|
Naming Scheme
|
||||||
|
|||||||
@@ -446,8 +446,8 @@ integer
|
|||||||
|OK|
|
|OK|
|
||||||
|
|
||||||
reference
|
reference
|
||||||
|EX|
|
|OK|
|
||||||
|
|
||||||
resetsignal
|
resetsignal
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|EX|
|
|OK|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Request
|
// Request
|
||||||
logic is_active;
|
logic is_active;
|
||||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||||
if({{cpuif.reset.activehigh_identifier}}) begin
|
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||||
is_active <= '0;
|
is_active <= '0;
|
||||||
cpuif_req <= '0;
|
cpuif_req <= '0;
|
||||||
cpuif_req_is_wr <= '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
|
from ..utils import get_always_ff_event, clog2
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..exporter import RegblockExporter
|
from ..exporter import RegblockExporter
|
||||||
from ..signals import SignalBase
|
from systemrdl import SignalNode
|
||||||
|
|
||||||
class CpuifBase:
|
class CpuifBase:
|
||||||
template_path = "cpuif/base_tmpl.sv"
|
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.exp = exp
|
||||||
self.reset = cpuif_reset
|
self.reset = cpuif_reset
|
||||||
self.data_width = data_width
|
self.data_width = data_width
|
||||||
@@ -22,7 +22,8 @@ class CpuifBase:
|
|||||||
def get_implementation(self) -> str:
|
def get_implementation(self) -> str:
|
||||||
context = {
|
context = {
|
||||||
"cpuif": self,
|
"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,
|
"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.node import AddrmapNode, FieldNode, SignalNode, RegNode
|
||||||
from systemrdl.rdltypes import PropertyReference
|
from systemrdl.rdltypes import PropertyReference
|
||||||
|
|
||||||
@@ -200,3 +200,17 @@ class Dereferencer:
|
|||||||
Returns the Verilog string that represents the register's access strobe
|
Returns the Verilog string that represents the register's access strobe
|
||||||
"""
|
"""
|
||||||
return self.address_decode.get_access_strobe(obj)
|
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
|
import jinja2 as jj
|
||||||
from systemrdl.node import AddrmapNode, RootNode
|
from systemrdl.node import AddrmapNode, RootNode
|
||||||
from systemrdl.walker import RDLWalker
|
|
||||||
|
|
||||||
from .addr_decode import AddressDecode
|
from .addr_decode import AddressDecode
|
||||||
from .field_logic import FieldLogic
|
from .field_logic import FieldLogic
|
||||||
from .dereferencer import Dereferencer
|
from .dereferencer import Dereferencer
|
||||||
from .readback import Readback
|
from .readback import Readback
|
||||||
from .signals import InferredSignal, RDLSignal
|
|
||||||
|
|
||||||
from .cpuif import CpuifBase
|
from .cpuif import CpuifBase
|
||||||
from .cpuif.apb3 import APB3_Cpuif
|
from .cpuif.apb3 import APB3_Cpuif
|
||||||
@@ -33,8 +31,6 @@ class RegblockExporter:
|
|||||||
self.field_logic = FieldLogic(self)
|
self.field_logic = FieldLogic(self)
|
||||||
self.readback = None # type: Readback
|
self.readback = None # type: Readback
|
||||||
self.dereferencer = Dereferencer(self)
|
self.dereferencer = Dereferencer(self)
|
||||||
self.default_resetsignal = InferredSignal("rst")
|
|
||||||
|
|
||||||
|
|
||||||
if user_template_dir:
|
if user_template_dir:
|
||||||
loader = jj.ChoiceLoader([
|
loader = jj.ChoiceLoader([
|
||||||
@@ -84,23 +80,11 @@ class RegblockExporter:
|
|||||||
# Scan the design for any unsupported features
|
# Scan the design for any unsupported features
|
||||||
# Also collect pre-export information
|
# Also collect pre-export information
|
||||||
scanner = DesignScanner(self)
|
scanner = DesignScanner(self)
|
||||||
RDLWalker().walk(self.top_node, scanner)
|
scanner.do_scan()
|
||||||
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])
|
|
||||||
|
|
||||||
self.cpuif = cpuif_cls(
|
self.cpuif = cpuif_cls(
|
||||||
self,
|
self,
|
||||||
cpuif_reset=cpuif_reset,
|
cpuif_reset=self.top_node.cpuif_reset,
|
||||||
data_width=scanner.cpuif_data_width,
|
data_width=scanner.cpuif_data_width,
|
||||||
addr_width=self.top_node.size.bit_length()
|
addr_width=self.top_node.size.bit_length()
|
||||||
)
|
)
|
||||||
@@ -108,7 +92,9 @@ class RegblockExporter:
|
|||||||
self.hwif = Hwif(
|
self.hwif = Hwif(
|
||||||
self,
|
self,
|
||||||
package_name=package_name,
|
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(
|
self.readback = Readback(
|
||||||
@@ -119,15 +105,15 @@ class RegblockExporter:
|
|||||||
# Build Jinja template context
|
# Build Jinja template context
|
||||||
context = {
|
context = {
|
||||||
"module_name": module_name,
|
"module_name": module_name,
|
||||||
"reset_signals": reset_signals,
|
"user_out_of_hier_signals": scanner.out_of_hier_signals.values(),
|
||||||
"user_signals": [], # TODO:
|
|
||||||
"interrupts": [], # TODO:
|
"interrupts": [], # TODO:
|
||||||
"cpuif": self.cpuif,
|
"cpuif": self.cpuif,
|
||||||
"hwif": self.hwif,
|
"hwif": self.hwif,
|
||||||
|
"get_resetsignal": self.dereferencer.get_resetsignal,
|
||||||
"address_decode": self.address_decode,
|
"address_decode": self.address_decode,
|
||||||
"field_logic": self.field_logic,
|
"field_logic": self.field_logic,
|
||||||
"readback": self.readback,
|
"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,
|
"retime_read_response": retime_read_response,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from collections import OrderedDict
|
|||||||
from ..struct_generator import RDLStructGenerator
|
from ..struct_generator import RDLStructGenerator
|
||||||
from ..forloop_generator import RDLForLoopGenerator
|
from ..forloop_generator import RDLForLoopGenerator
|
||||||
from ..utils import get_indexed_path, get_always_ff_event
|
from ..utils import get_indexed_path, get_always_ff_event
|
||||||
from ..signals import RDLSignal
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import FieldLogic
|
from . import FieldLogic
|
||||||
@@ -92,11 +91,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
|||||||
for signal in conditional.get_extra_combo_signals(node):
|
for signal in conditional.get_extra_combo_signals(node):
|
||||||
extra_combo_signals[signal.name] = signal
|
extra_combo_signals[signal.name] = signal
|
||||||
|
|
||||||
sig = node.get_property('resetsignal')
|
resetsignal = node.get_property('resetsignal')
|
||||||
if sig is not None:
|
|
||||||
resetsignal = RDLSignal(sig)
|
|
||||||
else:
|
|
||||||
resetsignal = self.exp.default_resetsignal
|
|
||||||
|
|
||||||
reset_value = node.get_property('reset')
|
reset_value = node.get_property('reset')
|
||||||
if reset_value is not None:
|
if reset_value is not None:
|
||||||
@@ -114,8 +109,9 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
|||||||
'extra_combo_signals': extra_combo_signals,
|
'extra_combo_signals': extra_combo_signals,
|
||||||
'conditionals': conditionals,
|
'conditionals': conditionals,
|
||||||
'resetsignal': resetsignal,
|
'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_value': self.exp.dereferencer.get_value,
|
||||||
|
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
|
||||||
}
|
}
|
||||||
self.add_content(self.field_storage_template.render(context))
|
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;
|
field_combo.{{field_path}}.load_next = load_next_c;
|
||||||
end
|
end
|
||||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||||
{% if resetsignal is not none -%}
|
{% if reset is not none -%}
|
||||||
if({{resetsignal.activehigh_identifier}}) begin
|
if({{get_resetsignal(resetsignal)}}) begin
|
||||||
field_storage.{{field_path}} <= {{reset}};
|
field_storage.{{field_path}} <= {{reset}};
|
||||||
end else {% endif %}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;
|
||||||
|
|||||||
@@ -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.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode
|
||||||
from systemrdl.rdltypes import PropertyReference
|
from systemrdl.rdltypes import PropertyReference
|
||||||
@@ -19,13 +19,19 @@ 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, 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.exp = exp
|
||||||
self.package_name = package_name
|
self.package_name = package_name
|
||||||
|
|
||||||
self.has_input_struct = None
|
self.has_input_struct = None
|
||||||
self.has_output_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:
|
if reuse_typedefs:
|
||||||
self._gen_in_cls = InputStructGenerator_TypeScope
|
self._gen_in_cls = InputStructGenerator_TypeScope
|
||||||
@@ -45,7 +51,7 @@ class Hwif:
|
|||||||
"""
|
"""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
gen_in = self._gen_in_cls(self.top_node)
|
gen_in = self._gen_in_cls(self)
|
||||||
structs_in = gen_in.get_struct(
|
structs_in = gen_in.get_struct(
|
||||||
self.top_node,
|
self.top_node,
|
||||||
f"{self.top_node.inst_name}__in_t"
|
f"{self.top_node.inst_name}__in_t"
|
||||||
@@ -56,7 +62,7 @@ class Hwif:
|
|||||||
else:
|
else:
|
||||||
self.has_input_struct = False
|
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(
|
structs_out = gen_out.get_struct(
|
||||||
self.top_node,
|
self.top_node,
|
||||||
f"{self.top_node.inst_name}__out_t"
|
f"{self.top_node.inst_name}__out_t"
|
||||||
@@ -129,8 +135,10 @@ class Hwif:
|
|||||||
path = get_indexed_path(self.top_node, obj)
|
path = get_indexed_path(self.top_node, obj)
|
||||||
return "hwif_in." + path + ".value"
|
return "hwif_in." + path + ".value"
|
||||||
elif isinstance(obj, SignalNode):
|
elif isinstance(obj, SignalNode):
|
||||||
# TODO: Implement this
|
if obj.get_path() in self.out_of_hier_signals:
|
||||||
raise NotImplementedError()
|
return obj.inst_name
|
||||||
|
path = get_indexed_path(self.top_node, obj)
|
||||||
|
return "hwif_in." + path
|
||||||
elif isinstance(obj, PropertyReference):
|
elif isinstance(obj, PropertyReference):
|
||||||
return self.get_implied_prop_input_identifier(obj.node, obj.name)
|
return self.get_implied_prop_input_identifier(obj.node, obj.name)
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ from ..struct_generator import RDLFlatStructGenerator
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import Node, SignalNode, FieldNode
|
from systemrdl.node import Node, SignalNode, FieldNode
|
||||||
|
from . import Hwif
|
||||||
|
|
||||||
class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
||||||
def __init__(self, top_node: 'Node'):
|
def __init__(self, hwif: 'Hwif'):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.top_node = top_node
|
self.hwif = hwif
|
||||||
|
self.top_node = hwif.top_node
|
||||||
|
|
||||||
def get_typdef_name(self, node:'Node') -> str:
|
def get_typdef_name(self, node:'Node') -> str:
|
||||||
base = node.get_rel_path(
|
base = node.get_rel_path(
|
||||||
@@ -19,6 +21,9 @@ class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
|||||||
return f'{base}__in_t'
|
return f'{base}__in_t'
|
||||||
|
|
||||||
def enter_Signal(self, node: 'SignalNode') -> None:
|
def enter_Signal(self, node: 'SignalNode') -> None:
|
||||||
|
# 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)
|
self.add_member(node.inst_name, node.width)
|
||||||
|
|
||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
// TODO: Add a banner
|
// TODO: Add a banner
|
||||||
module {{module_name}} (
|
module {{module_name}} (
|
||||||
input wire clk,
|
input wire clk,
|
||||||
{%- for signal in reset_signals %}
|
input wire rst,
|
||||||
{{signal.port_declaration}},
|
|
||||||
{%- endfor %}
|
|
||||||
|
|
||||||
{%- for signal in user_signals %}
|
{%- for signal in user_out_of_hier_signals %}
|
||||||
{{signal.port_declaration}},
|
{%- if signal.width == 1 %}
|
||||||
|
input wire {{signal.inst_name}},
|
||||||
|
{%- else %}
|
||||||
|
input wire [{{signal.width-1}}:0] {{signal.inst_name}},
|
||||||
|
{%- endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{%- for interrupt in interrupts %}
|
{%- for interrupt in interrupts %}
|
||||||
{{interrupt.port_declaration}},
|
// TODO:
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{{cpuif.port_declaration|indent(8)}}
|
{{cpuif.port_declaration|indent(8)}}
|
||||||
@@ -77,7 +79,7 @@ module {{module_name}} (
|
|||||||
|
|
||||||
{% if retime_read_response %}
|
{% if retime_read_response %}
|
||||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
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_ack <= '0;
|
||||||
cpuif_rd_data <= '0;
|
cpuif_rd_data <= '0;
|
||||||
cpuif_rd_err <= '0;
|
cpuif_rd_err <= '0;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Readback:
|
|||||||
context = {
|
context = {
|
||||||
"array_assignments" : array_assignments,
|
"array_assignments" : array_assignments,
|
||||||
"array_size" : array_size,
|
"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,
|
"cpuif": self.exp.cpuif,
|
||||||
"do_fanin_stage": self.do_fanin_stage,
|
"do_fanin_stage": self.do_fanin_stage,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from systemrdl.walker import RDLListener
|
from systemrdl.walker import RDLListener, RDLWalker
|
||||||
from systemrdl.node import AddrmapNode
|
from systemrdl.node import AddrmapNode, SignalNode
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import Node, RegNode, SignalNode, MemNode
|
from systemrdl.node import Node, RegNode, MemNode, FieldNode
|
||||||
from .exporter import RegblockExporter
|
from .exporter import RegblockExporter
|
||||||
|
|
||||||
|
|
||||||
@@ -20,13 +21,45 @@ class DesignScanner(RDLListener):
|
|||||||
self.cpuif_data_width = 0
|
self.cpuif_data_width = 0
|
||||||
self.msg = exp.top_node.env.msg
|
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:
|
def enter_Reg(self, node: 'RegNode') -> None:
|
||||||
# The CPUIF's bus width is sized according to the largest register in the design
|
# 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'))
|
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:
|
def enter_Component(self, node: 'Node') -> None:
|
||||||
if not isinstance(node, AddrmapNode) and node.external:
|
if not isinstance(node, AddrmapNode) and node.external:
|
||||||
self.msg.error(
|
self.msg.error(
|
||||||
@@ -46,6 +79,21 @@ class DesignScanner(RDLListener):
|
|||||||
node.inst.inst_src_ref
|
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:
|
def enter_Mem(self, node: 'MemNode') -> None:
|
||||||
self.msg.error(
|
self.msg.error(
|
||||||
"Cannot export a register block that contains a memory",
|
"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
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import Node
|
from systemrdl.node import Node, SignalNode
|
||||||
from .signals import SignalBase
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from .dereferencer import Dereferencer
|
||||||
|
|
||||||
def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
|
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)
|
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:
|
if resetsignal is None:
|
||||||
return "@(posedge clk)"
|
return "@(posedge clk)"
|
||||||
if resetsignal.is_async and resetsignal.is_activehigh:
|
if resetsignal.get_property('async') and resetsignal.get_property('activehigh'):
|
||||||
return f"@(posedge clk or posedge {resetsignal.identifier})"
|
return f"@(posedge clk or posedge {dereferencer.get_value(resetsignal)})"
|
||||||
elif resetsignal.is_async and not resetsignal.is_activehigh:
|
elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'):
|
||||||
return f"@(posedge clk or negedge {resetsignal.identifier})"
|
return f"@(posedge clk or negedge {dereferencer.get_value(resetsignal)})"
|
||||||
return "@(posedge clk)"
|
return "@(posedge clk)"
|
||||||
|
|
||||||
def clog2(n: int) -> int:
|
def clog2(n: int) -> int:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ module tb;
|
|||||||
logic rst = '1;
|
logic rst = '1;
|
||||||
logic clk = '0;
|
logic clk = '0;
|
||||||
initial forever begin
|
initial forever begin
|
||||||
#10ns;
|
#5ns;
|
||||||
clk = ~clk;
|
clk = ~clk;
|
||||||
end
|
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