Refactor exporter class to clean up the mess of random variables

This commit is contained in:
Alex Mykyta
2023-05-12 23:44:09 -07:00
parent 5b3cdd9d7a
commit 5e76956618
19 changed files with 210 additions and 217 deletions

View File

@@ -19,7 +19,7 @@ class AddressDecode:
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
return self.exp.ds.top_node
def get_strobe_struct(self) -> str:
struct_gen = DecodeStructGenerator()

View File

@@ -12,7 +12,7 @@ class AXI4Lite_Cpuif(CpuifBase):
@property
def regblock_latency(self) -> int:
return max(self.exp.min_read_latency, self.exp.min_write_latency)
return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency)
@property
def max_outstanding(self) -> int:

View File

@@ -14,11 +14,17 @@ class CpuifBase:
# Path is relative to the location of the class that assigns this variable
template_path = ""
def __init__(self, exp:'RegblockExporter', data_width:int=32, addr_width:int=32):
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
self.reset = exp.top_node.cpuif_reset
self.data_width = data_width
self.addr_width = addr_width
self.reset = exp.ds.top_node.cpuif_reset
@property
def addr_width(self) -> int:
return self.exp.ds.addr_width
@property
def data_width(self) -> int:
return self.exp.ds.cpuif_data_width
@property
def data_width_bytes(self) -> int:

View File

@@ -30,7 +30,7 @@ class Dereferencer:
@property
def top_node(self) -> AddrmapNode:
return self.exp.top_node
return self.exp.ds.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
"""

View File

@@ -1,5 +1,6 @@
import os
from typing import Union, Any, Type, Optional
from typing import TYPE_CHECKING, Union, Any, Type, Optional, Set, List
from collections import OrderedDict
import jinja2 as jj
from systemrdl.node import AddrmapNode, RootNode
@@ -19,14 +20,16 @@ from .write_buffering import WriteBuffering
from .read_buffering import ReadBuffering
from .external_acks import ExternalWriteAckGenerator, ExternalReadAckGenerator
if TYPE_CHECKING:
from systemrdl.node import SignalNode
from systemrdl.rdltypes import UserEnum
class RegblockExporter:
def __init__(self, **kwargs: Any) -> None:
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
self.top_node = None # type: AddrmapNode
self.hwif = None # type: Hwif
self.cpuif = None # type: CpuifBase
self.address_decode = None # type: AddressDecode
@@ -35,8 +38,7 @@ class RegblockExporter:
self.write_buffering = None # type: WriteBuffering
self.read_buffering = None # type: ReadBuffering
self.dereferencer = None # type: Dereferencer
self.min_read_latency = 0
self.min_write_latency = 0
self.ds = None # type: DesignState
loader = jj.ChoiceLoader([
jj.FileSystemLoader(os.path.dirname(__file__)),
@@ -114,83 +116,37 @@ class RegblockExporter:
"""
# If it is the root node, skip to top addrmap
if isinstance(node, RootNode):
self.top_node = node.top
top_node = node.top
else:
self.top_node = node
msg = self.top_node.env.msg
top_node = node
self.ds = DesignState(top_node, kwargs)
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase]
module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str
package_name = kwargs.pop("package_name", None) or (module_name + "_pkg") # type: str
reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool
user_addr_width = kwargs.pop("address_width", None) # type: Optional[int]
# Pipelining options
retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool
retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool
retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
self.min_read_latency = 0
self.min_write_latency = 0
if retime_read_fanin:
self.min_read_latency += 1
if retime_read_response:
self.min_read_latency += 1
addr_width = self.top_node.size.bit_length()
if user_addr_width is not None:
if user_addr_width < addr_width:
msg.fatal(f"User-specified address width shall be greater than or equal to {addr_width}.")
addr_width = user_addr_width
# Scan the design for pre-export information
scanner = DesignScanner(self)
scanner.do_scan()
DesignScanner(self).do_scan()
if generate_hwif_report:
path = os.path.join(output_dir, f"{module_name}_hwif.rpt")
path = os.path.join(output_dir, f"{self.ds.module_name}_hwif.rpt")
hwif_report_file = open(path, "w", encoding='utf-8') # pylint: disable=consider-using-with
else:
hwif_report_file = None
# Construct exporter components
self.cpuif = cpuif_cls(
self,
data_width=scanner.cpuif_data_width,
addr_width=addr_width
)
self.cpuif = cpuif_cls(self)
self.hwif = Hwif(
self,
package_name=package_name,
in_hier_signal_paths=scanner.in_hier_signal_paths,
out_of_hier_signals=scanner.out_of_hier_signals,
user_enums=scanner.user_enums,
reuse_typedefs=reuse_hwif_typedefs,
hwif_report_file=hwif_report_file,
data_width=scanner.cpuif_data_width,
)
self.readback = Readback(
self,
retime_read_fanin,
scanner.has_external_addressable
)
self.readback = Readback(self)
self.address_decode = AddressDecode(self)
self.field_logic = FieldLogic(
self,
retime_external_reg=retime_external_reg,
retime_external_regfile=retime_external_regfile,
retime_external_mem=retime_external_mem,
retime_external_addrmap=retime_external_addrmap,
)
self.field_logic = FieldLogic(self)
self.write_buffering = WriteBuffering(self)
self.read_buffering = ReadBuffering(self)
self.dereferencer = Dereferencer(self)
@@ -198,18 +154,10 @@ class RegblockExporter:
ext_read_acks = ExternalReadAckGenerator(self)
# Validate that there are no unsupported constructs
validator = DesignValidator(self)
validator.do_validate()
DesignValidator(self).do_validate()
# Build Jinja template context
context = {
"module_name": module_name,
"user_out_of_hier_signals": scanner.out_of_hier_signals.values(),
"has_writable_msb0_fields": scanner.has_writable_msb0_fields,
"has_buffered_write_regs": scanner.has_buffered_write_regs,
"has_buffered_read_regs": scanner.has_buffered_read_regs,
"has_external_addressable": scanner.has_external_addressable,
"has_external_block": scanner.has_external_block,
"cpuif": self.cpuif,
"hwif": self.hwif,
"write_buffering": self.write_buffering,
@@ -221,24 +169,82 @@ class RegblockExporter:
"ext_write_acks": ext_write_acks,
"ext_read_acks": ext_read_acks,
"get_always_ff_event": self.dereferencer.get_always_ff_event,
"retime_read_response": retime_read_response,
"retime_read_fanin": retime_read_fanin,
"min_read_latency": self.min_read_latency,
"min_write_latency": self.min_write_latency,
"ds": self.ds,
"kwf": kwf,
}
# Write out design
os.makedirs(output_dir, exist_ok=True)
package_file_path = os.path.join(output_dir, package_name + ".sv")
package_file_path = os.path.join(output_dir, self.ds.package_name + ".sv")
template = self.jj_env.get_template("package_tmpl.sv")
stream = template.stream(context)
stream.dump(package_file_path)
module_file_path = os.path.join(output_dir, module_name + ".sv")
module_file_path = os.path.join(output_dir, self.ds.module_name + ".sv")
template = self.jj_env.get_template("module_tmpl.sv")
stream = template.stream(context)
stream.dump(module_file_path)
if hwif_report_file:
hwif_report_file.close()
class DesignState:
"""
Dumping ground for all sorts of variables that are relevant to a particular
design.
"""
def __init__(self, top_node: AddrmapNode, kwargs: Any) -> None:
self.top_node = top_node
msg = top_node.env.msg
#------------------------
# Extract compiler args
#------------------------
self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
self.module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str
self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str
user_addr_width = kwargs.pop("address_width", None) # type: Optional[int]
# Pipelining options
self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
self.retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
self.retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool
self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool
self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool
#------------------------
# Info about the design
#------------------------
self.min_read_latency = 0
self.min_write_latency = 0
self.cpuif_data_width = 0
# Collections of signals that were actually referenced by the design
self.in_hier_signal_paths = set() # type: Set[str]
self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode]
self.has_writable_msb0_fields = False
self.has_buffered_write_regs = False
self.has_buffered_read_regs = False
self.has_external_block = False
self.has_external_addressable = False
# Track any referenced enums
self.user_enums = [] # type: List[Type[UserEnum]]
#------------------------
if self.retime_read_fanin:
self.min_read_latency += 1
if self.retime_read_response:
self.min_read_latency += 1
self.addr_width = self.top_node.size.bit_length()
if user_addr_width is not None:
if user_addr_width < self.addr_width:
msg.fatal(f"User-specified address width shall be greater than or equal to {self.addr_width}.")
self.addr_width = user_addr_width

View File

@@ -15,7 +15,7 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator):
self.exp = exp
def get_implementation(self) -> str:
content = self.get_content(self.exp.top_node)
content = self.get_content(self.exp.ds.top_node)
if content is None:
return ""
return content
@@ -36,7 +36,7 @@ class ExternalReadAckGenerator(RDLForLoopGenerator):
self.exp = exp
def get_implementation(self) -> str:
content = self.get_content(self.exp.top_node)
content = self.get_content(self.exp.ds.top_node)
if content is None:
return ""
return content

View File

@@ -17,31 +17,24 @@ from .generators import CombinationalStructGenerator, FieldStorageStructGenerato
if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import AddrmapNode, FieldNode
from ..exporter import RegblockExporter
from ..exporter import RegblockExporter, DesignState
class FieldLogic:
def __init__(
self,
exp:'RegblockExporter',
retime_external_reg: bool,
retime_external_regfile: bool,
retime_external_mem: bool,
retime_external_addrmap: bool,
):
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
self.retime_external_reg = retime_external_reg
self.retime_external_regfile = retime_external_regfile
self.retime_external_mem = retime_external_mem
self.retime_external_addrmap = retime_external_addrmap
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self.init_conditionals()
@property
def ds(self) -> 'DesignState':
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
struct_gen = FieldStorageStructGenerator(self)

View File

@@ -77,7 +77,7 @@ class NextStateConditional:
raise NotImplementedError
def get_field_path(self, field:'FieldNode') -> str:
return get_indexed_path(self.exp.top_node, field)
return get_indexed_path(self.exp.ds.top_node, field)
def get_predicate(self, field: 'FieldNode') -> str:
"""

View File

@@ -101,6 +101,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
super().__init__()
self.field_logic = field_logic
self.exp = field_logic.exp
self.ds = self.exp.ds
self.field_storage_template = self.exp.jj_env.get_template(
"field_logic/templates/field_storage.sv"
)
@@ -321,7 +322,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
def assign_external_reg_outputs(self, node: 'RegNode') -> None:
prefix = "hwif_out." + get_indexed_path(self.exp.top_node, node)
prefix = "hwif_out." + get_indexed_path(self.exp.ds.top_node, node)
strb = self.exp.dereferencer.get_access_strobe(node)
width = min(self.exp.cpuif.data_width, node.get_property('regwidth'))
@@ -334,25 +335,25 @@ class FieldLogicGenerator(RDLForLoopGenerator):
"prefix": prefix,
"strb": strb,
"bslice": bslice,
"retime": self.field_logic.retime_external_reg,
"retime": self.ds.retime_external_reg,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"resetsignal": self.exp.top_node.cpuif_reset,
"resetsignal": self.exp.ds.top_node.cpuif_reset,
}
self.add_content(self.external_reg_template.render(context))
def assign_external_block_outputs(self, node: 'AddressableNode') -> None:
prefix = "hwif_out." + get_indexed_path(self.exp.top_node, node)
prefix = "hwif_out." + get_indexed_path(self.exp.ds.top_node, node)
strb = self.exp.dereferencer.get_external_block_access_strobe(node)
addr_width = node.size.bit_length()
retime = False
if isinstance(node, RegfileNode):
retime = self.field_logic.retime_external_regfile
retime = self.ds.retime_external_regfile
elif isinstance(node, MemNode):
retime = self.field_logic.retime_external_mem
retime = self.ds.retime_external_mem
elif isinstance(node, AddrmapNode):
retime = self.field_logic.retime_external_addrmap
retime = self.ds.retime_external_addrmap
context = {
"prefix": prefix,
@@ -361,6 +362,6 @@ class FieldLogicGenerator(RDLForLoopGenerator):
"retime": retime,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"resetsignal": self.exp.top_node.cpuif_reset,
"resetsignal": self.exp.ds.top_node.cpuif_reset,
}
self.add_content(self.external_block_template.render(context))

View File

@@ -11,7 +11,7 @@ from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_Ty
from .generators import EnumGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import RegblockExporter, DesignState
class Hwif:
"""
@@ -22,36 +22,30 @@ class Hwif:
"""
def __init__(
self, exp: 'RegblockExporter', package_name: str,
user_enums: List[Type[UserEnum]],
in_hier_signal_paths: Set[str], out_of_hier_signals: Dict[str, SignalNode],
reuse_typedefs: bool, hwif_report_file: Optional[TextIO],
data_width: int
self, exp: 'RegblockExporter',
hwif_report_file: Optional[TextIO]
):
self.exp = exp
self.package_name = package_name
self.has_input_struct = False
self.has_output_struct = False
self.in_hier_signal_paths = in_hier_signal_paths
self.out_of_hier_signals = out_of_hier_signals
self.user_enums = user_enums
self.hwif_report_file = hwif_report_file
self.data_width = data_width
if reuse_typedefs:
if self.ds.reuse_hwif_typedefs:
self._gen_in_cls = InputStructGenerator_TypeScope
self._gen_out_cls = OutputStructGenerator_TypeScope
else:
self._gen_in_cls = InputStructGenerator_Hier
self._gen_out_cls = OutputStructGenerator_Hier
@property
def ds(self) -> 'DesignState':
return self.exp.ds
@property
def top_node(self) -> AddrmapNode:
return self.exp.top_node
return self.exp.ds.top_node
def get_package_contents(self) -> str:
@@ -83,9 +77,7 @@ class Hwif:
self.has_output_struct = False
gen_enum = EnumGenerator()
enums = gen_enum.get_enums(
self.user_enums
)
enums = gen_enum.get_enums(self.ds.user_enums)
if enums is not None:
lines.append(enums)
@@ -105,10 +97,10 @@ class Hwif:
lines = []
if self.has_input_struct:
type_name = f"{self.top_node.inst_name}__in_t"
lines.append(f"input {self.package_name}::{type_name} hwif_in")
lines.append(f"input {self.ds.package_name}::{type_name} hwif_in")
if self.has_output_struct:
type_name = f"{self.top_node.inst_name}__out_t"
lines.append(f"output {self.package_name}::{type_name} hwif_out")
lines.append(f"output {self.ds.package_name}::{type_name} hwif_out")
return ",\n".join(lines)
@@ -156,7 +148,7 @@ class Hwif:
path = get_indexed_path(self.top_node, obj)
return "hwif_in." + path + ".next"
elif isinstance(obj, SignalNode):
if obj.get_path() in self.out_of_hier_signals:
if obj.get_path() in self.ds.out_of_hier_signals:
return kwf(obj.inst_name)
path = get_indexed_path(self.top_node, obj)
return "hwif_in." + path

View File

@@ -63,12 +63,12 @@ class InputStructGenerator_Hier(HWIFStructGenerator):
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:
if path in self.hwif.ds.in_hier_signal_paths:
self.add_member(kwf(node.inst_name), node.width)
def _add_external_block_members(self, node: 'AddressableNode') -> None:
self.add_member("rd_ack")
self.add_member("rd_data", self.hwif.data_width)
self.add_member("rd_data", self.hwif.ds.cpuif_data_width)
self.add_member("wr_ack")
def enter_Addrmap(self, node: 'AddrmapNode') -> None:
@@ -95,7 +95,7 @@ class InputStructGenerator_Hier(HWIFStructGenerator):
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
super().enter_Reg(node)
if node.external:
width = min(self.hwif.data_width, node.get_property('regwidth'))
width = min(self.hwif.ds.cpuif_data_width, node.get_property('regwidth'))
self.add_member("rd_ack")
self.add_member("rd_data", width)
self.add_member("wr_ack")
@@ -162,8 +162,8 @@ class OutputStructGenerator_Hier(HWIFStructGenerator):
self.add_member("req")
self.add_member("addr", (node.size - 1).bit_length())
self.add_member("req_is_wr")
self.add_member("wr_data", self.hwif.data_width)
self.add_member("wr_biten", self.hwif.data_width)
self.add_member("wr_data", self.hwif.ds.cpuif_data_width)
self.add_member("wr_biten", self.hwif.ds.cpuif_data_width)
def enter_Addrmap(self, node: 'AddrmapNode') -> None:
super().enter_Addrmap(node)
@@ -189,7 +189,7 @@ class OutputStructGenerator_Hier(HWIFStructGenerator):
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
super().enter_Reg(node)
if node.external:
width = min(self.hwif.data_width, node.get_property('regwidth'))
width = min(self.hwif.ds.cpuif_data_width, node.get_property('regwidth'))
n_subwords = node.get_property("regwidth") // node.get_property("accesswidth")
self.add_member("req", n_subwords)
self.add_member("req_is_wr")

View File

@@ -1,11 +1,11 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
module {{module_name}} (
module {{ds.module_name}} (
input wire clk,
input wire rst,
{%- for signal in user_out_of_hier_signals %}
{%- for signal in ds.out_of_hier_signals.values() %}
{%- if signal.width == 1 %}
input wire {{kwf(signal.inst_name)}},
{%- else %}
@@ -40,7 +40,7 @@ module {{module_name}} (
{{cpuif.get_implementation()|indent}}
logic cpuif_req_masked;
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
logic external_req;
logic external_pending;
logic external_wr_ack;
@@ -54,9 +54,9 @@ module {{module_name}} (
end
end
{%- endif %}
{% if min_read_latency == min_write_latency %}
{% if ds.min_read_latency == ds.min_write_latency %}
// Read & write latencies are balanced. Stalls not required
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
// except if external
assign cpuif_req_stall_rd = external_pending;
assign cpuif_req_stall_wr = external_pending;
@@ -64,9 +64,9 @@ module {{module_name}} (
assign cpuif_req_stall_rd = '0;
assign cpuif_req_stall_wr = '0;
{%- endif %}
{%- elif min_read_latency > min_write_latency %}
{%- elif ds.min_read_latency > ds.min_write_latency %}
// Read latency > write latency. May need to delay next write that follows a read
logic [{{min_read_latency - min_write_latency - 1}}:0] cpuif_req_stall_sr;
logic [{{ds.min_read_latency - ds.min_write_latency - 1}}:0] cpuif_req_stall_sr;
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{get_resetsignal(cpuif.reset)}}) begin
cpuif_req_stall_sr <= '0;
@@ -76,7 +76,7 @@ module {{module_name}} (
cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1);
end
end
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
assign cpuif_req_stall_rd = external_pending;
assign cpuif_req_stall_wr = cpuif_req_stall_sr[0] | external_pending;
{%- else %}
@@ -85,7 +85,7 @@ module {{module_name}} (
{%- endif %}
{%- else %}
// Write latency > read latency. May need to delay next read that follows a write
logic [{{min_write_latency - min_read_latency - 1}}:0] cpuif_req_stall_sr;
logic [{{ds.min_write_latency - ds.min_read_latency - 1}}:0] cpuif_req_stall_sr;
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{get_resetsignal(cpuif.reset)}}) begin
cpuif_req_stall_sr <= '0;
@@ -95,7 +95,7 @@ module {{module_name}} (
cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1);
end
end
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
assign cpuif_req_stall_rd = cpuif_req_stall_sr[0] | external_pending;
assign cpuif_req_stall_wr = external_pending;
{%- else %}
@@ -112,10 +112,10 @@ module {{module_name}} (
//--------------------------------------------------------------------------
{{address_decode.get_strobe_struct()|indent}}
decoded_reg_strb_t decoded_reg_strb;
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
logic decoded_strb_is_external;
{% endif %}
{%- if has_external_block %}
{%- if ds.has_external_block %}
logic [{{cpuif.addr_width-1}}:0] decoded_addr;
{% endif %}
logic decoded_req;
@@ -124,25 +124,25 @@ module {{module_name}} (
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten;
always_comb begin
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
automatic logic is_external = '0;
{% endif %}
{{address_decode.get_implementation()|indent(8)}}
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
decoded_strb_is_external = is_external;
external_req = is_external;
{% endif %}
end
// Pass down signals to next stage
{%- if has_external_block %}
{%- if ds.has_external_block %}
assign decoded_addr = cpuif_addr;
{% endif %}
assign decoded_req = cpuif_req_masked;
assign decoded_req_is_wr = cpuif_req_is_wr;
assign decoded_wr_data = cpuif_wr_data;
assign decoded_wr_biten = cpuif_wr_biten;
{% if has_writable_msb0_fields %}
{% if ds.has_writable_msb0_fields %}
// bitswap for use by fields with msb0 ordering
logic [{{cpuif.data_width-1}}:0] decoded_wr_data_bswap;
logic [{{cpuif.data_width-1}}:0] decoded_wr_biten_bswap;
@@ -150,7 +150,7 @@ module {{module_name}} (
assign decoded_wr_biten_bswap = {<<{decoded_wr_biten}};
{%- endif %}
{%- if has_buffered_write_regs %}
{%- if ds.has_buffered_write_regs %}
//--------------------------------------------------------------------------
// Write double-buffers
@@ -168,7 +168,7 @@ module {{module_name}} (
{{field_logic.get_implementation()|indent}}
{%- if has_buffered_read_regs %}
{%- if ds.has_buffered_read_regs %}
//--------------------------------------------------------------------------
// Read double-buffers
@@ -181,7 +181,7 @@ module {{module_name}} (
//--------------------------------------------------------------------------
// Write response
//--------------------------------------------------------------------------
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
always_comb begin
automatic logic wr_ack;
wr_ack = '0;
@@ -198,7 +198,7 @@ module {{module_name}} (
//--------------------------------------------------------------------------
// Readback
//--------------------------------------------------------------------------
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
logic readback_external_rd_ack_c;
always_comb begin
automatic logic rd_ack;
@@ -208,7 +208,7 @@ module {{module_name}} (
end
logic readback_external_rd_ack;
{%- if retime_read_fanin %}
{%- if ds.retime_read_fanin %}
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{get_resetsignal(cpuif.reset)}}) begin
readback_external_rd_ack <= '0;
@@ -227,17 +227,17 @@ module {{module_name}} (
logic readback_done;
logic [{{cpuif.data_width-1}}:0] readback_data;
{{readback.get_implementation()|indent}}
{% if retime_read_response %}
{% if ds.retime_read_response %}
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{get_resetsignal(cpuif.reset)}}) begin
cpuif_rd_ack <= '0;
cpuif_rd_data <= '0;
cpuif_rd_err <= '0;
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
external_rd_ack <= '0;
{%- endif %}
end else begin
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
external_rd_ack <= readback_external_rd_ack;
cpuif_rd_ack <= readback_done | readback_external_rd_ack;
{%- else %}
@@ -248,7 +248,7 @@ module {{module_name}} (
end
end
{% else %}
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
assign external_rd_ack = readback_external_rd_ack;
assign cpuif_rd_ack = readback_done | readback_external_rd_ack;
{%- else %}

View File

@@ -1,6 +1,6 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
package {{hwif.package_name}};
package {{ds.package_name}};
{{hwif.get_package_contents()|indent}}
endpackage

View File

@@ -16,7 +16,7 @@ class ReadBuffering:
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
struct_gen = RBufStorageStructGenerator()

View File

@@ -4,18 +4,21 @@ import math
from .generators import ReadbackAssignmentGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import RegblockExporter, DesignState
from systemrdl.node import AddrmapNode
class Readback:
def __init__(self, exp:'RegblockExporter', do_fanin_stage: bool, has_external_addressable: bool):
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
self.do_fanin_stage = do_fanin_stage
self.has_external_addressable = has_external_addressable
self.do_fanin_stage = self.ds.retime_read_fanin
@property
def ds(self) -> 'DesignState':
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
return self.exp.ds.top_node
def get_implementation(self) -> str:
gen = ReadbackAssignmentGenerator(self.exp)
@@ -33,7 +36,7 @@ class Readback:
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
"cpuif": self.exp.cpuif,
"do_fanin_stage": self.do_fanin_stage,
"has_external_addressable": self.has_external_addressable,
"ds": self.ds,
}
if self.do_fanin_stage:

View File

@@ -35,7 +35,7 @@ always_ff @(posedge clk) begin
readback_done_r <= '0;
end else begin
readback_array_r <= readback_array_c;
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
readback_done_r <= decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
{%- else %}
readback_done_r <= decoded_req & ~decoded_req_is_wr;
@@ -58,7 +58,7 @@ end
// Reduce the array
always_comb begin
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
{%- if has_external_addressable %}
{%- if ds.has_external_addressable %}
readback_done = decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external;
{%- else %}
readback_done = decoded_req & ~decoded_req_is_wr;

View File

@@ -1,13 +1,11 @@
from typing import TYPE_CHECKING, Set, Optional, Type, List
from collections import OrderedDict
from typing import TYPE_CHECKING, Optional
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
from systemrdl.node import SignalNode, RegNode
if TYPE_CHECKING:
from systemrdl.node import Node, FieldNode, AddressableNode
from systemrdl.node import Node, FieldNode, AddressableNode, AddrmapNode
from .exporter import RegblockExporter
from systemrdl.rdltypes import UserEnum
class DesignScanner(RDLListener):
@@ -19,63 +17,53 @@ class DesignScanner(RDLListener):
"""
def __init__(self, exp:'RegblockExporter') -> None:
self.exp = exp
self.cpuif_data_width = 0
self.msg = exp.top_node.env.msg
self.ds = exp.ds
self.msg = self.top_node.env.msg
# Collections of signals that were actually referenced by the design
self.in_hier_signal_paths = set() # type: Set[str]
self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode]
self.has_writable_msb0_fields = False
self.has_buffered_write_regs = False
self.has_buffered_read_regs = False
self.has_external_block = False
self.has_external_addressable = False
# Track any referenced enums
self.user_enums = [] # type: List[Type[UserEnum]]
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.ds.top_node
def _get_out_of_hier_field_reset(self) -> None:
current_node = self.exp.top_node.parent
current_node = self.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
self.ds.out_of_hier_signals[path] = signal
return
current_node = current_node.parent
def do_scan(self) -> None:
# Collect cpuif reset, if any.
cpuif_reset = self.exp.top_node.cpuif_reset
cpuif_reset = self.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)
rel_path = cpuif_reset.get_rel_path(self.top_node)
if rel_path.startswith("^"):
self.out_of_hier_signals[path] = cpuif_reset
self.ds.out_of_hier_signals[path] = cpuif_reset
else:
self.in_hier_signal_paths.add(path)
self.ds.in_hier_signal_paths.add(path)
# collect out-of-hier field_reset, if any
self._get_out_of_hier_field_reset()
# Ensure addrmap is not a bridge. This concept does not make sense for
# terminal components.
if self.exp.top_node.get_property('bridge'):
if self.top_node.get_property('bridge'):
self.msg.error(
"Regblock generator does not support exporting bridge address maps",
self.exp.top_node.inst.property_src_ref.get('bridge', self.exp.top_node.inst.inst_src_ref)
self.top_node.inst.property_src_ref.get('bridge', self.top_node.inst.inst_src_ref)
)
RDLWalker().walk(self.exp.top_node, self)
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
if node.external and (node != self.exp.top_node):
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -84,37 +72,37 @@ class DesignScanner(RDLListener):
value = node.get_property(prop_name)
if isinstance(value, SignalNode):
path = value.get_path()
rel_path = value.get_rel_path(self.exp.top_node)
rel_path = value.get_rel_path(self.top_node)
if rel_path.startswith("^"):
self.out_of_hier_signals[path] = value
self.ds.out_of_hier_signals[path] = value
else:
self.in_hier_signal_paths.add(path)
self.ds.in_hier_signal_paths.add(path)
if prop_name == "encode":
if value not in self.user_enums:
self.user_enums.append(value)
if value not in self.ds.user_enums:
self.ds.user_enums.append(value)
return WalkerAction.Continue
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
if node.external and node != self.exp.top_node:
self.has_external_addressable = True
if node.external and node != self.top_node:
self.ds.has_external_addressable = True
if not isinstance(node, RegNode):
self.has_external_block = True
self.ds.has_external_block = True
def enter_Reg(self, node: 'RegNode') -> None:
# The CPUIF's bus width is sized according to the largest accesswidth in the design
accesswidth = node.get_property('accesswidth')
self.cpuif_data_width = max(self.cpuif_data_width, accesswidth)
self.exp.ds.cpuif_data_width = max(self.exp.ds.cpuif_data_width, accesswidth)
self.has_buffered_write_regs = self.has_buffered_write_regs or bool(node.get_property('buffer_writes'))
self.has_buffered_read_regs = self.has_buffered_read_regs or bool(node.get_property('buffer_reads'))
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(node.get_property('buffer_writes'))
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads'))
def enter_Signal(self, node: 'SignalNode') -> None:
if node.get_property('field_reset'):
path = node.get_path()
self.in_hier_signal_paths.add(path)
self.ds.in_hier_signal_paths.add(path)
def enter_Field(self, node: 'FieldNode') -> None:
if node.is_sw_writable and (node.msb < node.lsb):
self.has_writable_msb0_fields = True
self.ds.has_writable_msb0_fields = True

View File

@@ -19,20 +19,24 @@ class DesignValidator(RDLListener):
"""
def __init__(self, exp:'RegblockExporter') -> None:
self.exp = exp
self.msg = exp.top_node.env.msg
self.msg = self.top_node.env.msg
self._contains_external_block_stack = [] # type: List[bool]
self.contains_external_block = False
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.ds.top_node
def do_validate(self) -> None:
RDLWalker().walk(self.exp.top_node, self)
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
if node.external and (node != self.exp.top_node):
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -40,7 +44,7 @@ class DesignValidator(RDLListener):
for prop_name in node.list_properties():
value = node.get_property(prop_name)
if isinstance(value, (PropertyReference, Node)):
if not ref_is_internal(self.exp.top_node, value):
if not ref_is_internal(self.top_node, value):
if isinstance(value, PropertyReference):
src_ref = value.src_ref
else:
@@ -55,7 +59,7 @@ class DesignValidator(RDLListener):
# If encountering a CPUIF reset that is nested within the register model,
# warn that it will be ignored.
# Only cpuif resets in the top-level node or above will be honored
if node.get_property('cpuif_reset') and (node.parent != self.exp.top_node):
if node.get_property('cpuif_reset') and (node.parent != self.top_node):
self.msg.warning(
"Only cpuif_reset signals that are instantiated in the top-level "
"addrmap or above will be honored. Any cpuif_reset signals nested "
@@ -81,7 +85,7 @@ class DesignValidator(RDLListener):
if not isinstance(node, RegNode):
# Entering a block-like node
if node == self.exp.top_node:
if node == self.top_node:
# Ignore top addrmap's external property when entering
self._contains_external_block_stack.append(False)
else:

View File

@@ -16,7 +16,7 @@ class WriteBuffering:
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
return self.exp.ds.top_node
def get_storage_struct(self) -> str: