185 lines
7.3 KiB
Python
185 lines
7.3 KiB
Python
import os
|
|
from typing import Union
|
|
|
|
import jinja2 as jj
|
|
from systemrdl.node import AddrmapNode, RootNode
|
|
|
|
from .addr_decode import AddressDecode
|
|
from .field_logic import FieldLogic
|
|
from .dereferencer import Dereferencer
|
|
from .readback import Readback
|
|
|
|
from .cpuif import CpuifBase
|
|
from .cpuif.apb3 import APB3_Cpuif
|
|
from .hwif import Hwif
|
|
from .utils import get_always_ff_event
|
|
from .scan_design import DesignScanner
|
|
|
|
class RegblockExporter:
|
|
def __init__(self, **kwargs) -> None:
|
|
user_template_dir = kwargs.pop("user_template_dir", None)
|
|
|
|
# Check for stray kwargs
|
|
if kwargs:
|
|
raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0])
|
|
|
|
|
|
self.top_node = None # type: AddrmapNode
|
|
self.hwif = None # type: Hwif
|
|
self.cpuif = None # type: CpuifBase
|
|
self.address_decode = AddressDecode(self)
|
|
self.field_logic = FieldLogic(self)
|
|
self.readback = None # type: Readback
|
|
self.dereferencer = Dereferencer(self)
|
|
self.min_read_latency = 0
|
|
self.min_write_latency = 0
|
|
|
|
if user_template_dir:
|
|
loader = jj.ChoiceLoader([
|
|
jj.FileSystemLoader(user_template_dir),
|
|
jj.FileSystemLoader(os.path.dirname(__file__)),
|
|
jj.PrefixLoader({
|
|
'user': jj.FileSystemLoader(user_template_dir),
|
|
'base': jj.FileSystemLoader(os.path.dirname(__file__)),
|
|
}, delimiter=":")
|
|
])
|
|
else:
|
|
loader = jj.ChoiceLoader([
|
|
jj.FileSystemLoader(os.path.dirname(__file__)),
|
|
jj.PrefixLoader({
|
|
'base': jj.FileSystemLoader(os.path.dirname(__file__)),
|
|
}, delimiter=":")
|
|
])
|
|
|
|
self.jj_env = jj.Environment(
|
|
loader=loader,
|
|
undefined=jj.StrictUndefined,
|
|
)
|
|
|
|
|
|
def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs) -> None:
|
|
"""
|
|
Parameters
|
|
----------
|
|
node: AddrmapNode
|
|
Top-level SystemRDL node to export.
|
|
output_dir: str
|
|
Path to the output directory where generated SystemVerilog will be written.
|
|
Output includes two files: a module definition and package definition.
|
|
cpuif_cls: :class:`peakrdl.regblock.cpuif.CpuifBase`
|
|
Specify the class type that implements the CPU interface of your choice.
|
|
Defaults to AMBA APB3.
|
|
module_name: str
|
|
Override the SystemVerilog module name. By default, the module name
|
|
is the top-level node's name.
|
|
package_name: str
|
|
Override the SystemVerilog package name. By default, the package name
|
|
is the top-level node's name with a "_pkg" suffix.
|
|
reuse_hwif_typedefs: bool
|
|
By default, the exporter will attempt to re-use hwif struct definitions for
|
|
nodes that are equivalent. This allows for better modularity and type reuse.
|
|
Struct type names are derived using the SystemRDL component's type
|
|
name and declared lexical scope path.
|
|
|
|
If this is not desireable, override this parameter to ``False`` and structs
|
|
will be generated more naively using their hierarchical paths.
|
|
retime_read_fanin: bool
|
|
Set this to ``True`` to enable additional read path retiming.
|
|
For large register blocks that operate at demanding clock rates, this
|
|
may be necessary in order to manage large readback fan-in.
|
|
|
|
The retiming flop stage is automatically placed in the most optimal point in the
|
|
readback path so that logic-levels and fanin are minimized.
|
|
|
|
Enabling this option will increase read transfer latency by 1 clock cycle.
|
|
retime_read_response: bool
|
|
Set this to ``True`` to enable an additional retiming flop stage between
|
|
the readback mux and the CPU interface response logic.
|
|
This option may be beneficial for some CPU interfaces that implement the
|
|
response logic fully combinationally. Enabling this stage can better
|
|
isolate timing paths in the register file from the rest of your system.
|
|
|
|
Enabling this when using CPU interfaces that already implement the
|
|
response path sequentially may not result in any meaningful timing improvement.
|
|
|
|
Enabling this option will increase read transfer latency by 1 clock cycle.
|
|
"""
|
|
# If it is the root node, skip to top addrmap
|
|
if isinstance(node, RootNode):
|
|
self.top_node = node.top
|
|
else:
|
|
self.top_node = node
|
|
|
|
|
|
cpuif_cls = kwargs.pop("cpuif_cls", APB3_Cpuif)
|
|
module_name = kwargs.pop("module_name", self.top_node.inst_name)
|
|
package_name = kwargs.pop("package_name", module_name + "_pkg")
|
|
reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True)
|
|
|
|
# Pipelining options
|
|
retime_read_fanin = kwargs.pop("retime_read_fanin", False)
|
|
retime_read_response = kwargs.pop("retime_read_response", True)
|
|
|
|
# Check for stray kwargs
|
|
if kwargs:
|
|
raise TypeError("got an unexpected keyword argument '%s'" % 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
|
|
|
|
# Scan the design for any unsupported features
|
|
# Also collect pre-export information
|
|
scanner = DesignScanner(self)
|
|
scanner.do_scan()
|
|
|
|
self.cpuif = cpuif_cls(
|
|
self,
|
|
cpuif_reset=self.top_node.cpuif_reset,
|
|
data_width=scanner.cpuif_data_width,
|
|
addr_width=self.top_node.size.bit_length()
|
|
)
|
|
|
|
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,
|
|
reuse_typedefs=reuse_hwif_typedefs,
|
|
)
|
|
|
|
self.readback = Readback(
|
|
self,
|
|
retime_read_fanin
|
|
)
|
|
|
|
# Build Jinja template context
|
|
context = {
|
|
"module_name": module_name,
|
|
"user_out_of_hier_signals": scanner.out_of_hier_signals.values(),
|
|
"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": lambda resetsignal : get_always_ff_event(self.dereferencer, resetsignal),
|
|
"retime_read_response": retime_read_response,
|
|
"min_read_latency": self.min_read_latency,
|
|
"min_write_latency": self.min_write_latency,
|
|
}
|
|
|
|
# Write out design
|
|
package_file_path = os.path.join(output_dir, 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")
|
|
template = self.jj_env.get_template("module_tmpl.sv")
|
|
stream = template.stream(context)
|
|
stream.dump(module_file_path)
|