diff --git a/Bender.lock b/Bender.lock index f9711ffa3..e72f22b04 100644 --- a/Bender.lock +++ b/Bender.lock @@ -32,8 +32,8 @@ packages: dependencies: - common_cells common_cells: - revision: 9afda9abb565971649c2aa0985639c096f351171 - version: 1.38.0 + revision: 1d5c6fc3da60f0a920a5113b143624770bc1a279 + version: null source: Git: https://github.com/pulp-platform/common_cells.git dependencies: diff --git a/Bender.yml b/Bender.yml index 9f2c473a8..c7832f795 100644 --- a/Bender.yml +++ b/Bender.yml @@ -11,7 +11,7 @@ package: dependencies: idma: { git: "https://github.com/pulp-platform/iDMA.git", version: 0.6.2 } - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.35.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: "master" } # Revert to a versioned release once available common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.7 } axi_riscv_atomics: { git: "https://github.com/pulp-platform/axi_riscv_atomics.git", version: 0.8.2 } diff --git a/floogen/model/endpoint.py b/floogen/model/endpoint.py index 34832f66d..dbfd7d0f6 100644 --- a/floogen/model/endpoint.py +++ b/floogen/model/endpoint.py @@ -7,7 +7,7 @@ from typing import Optional, List, Union, Tuple from pydantic import BaseModel, ConfigDict, field_validator, model_validator -from floogen.model.routing import AddrRange, Id, Coord +from floogen.model.routing import AddrRange, SimpleId, Coord from floogen.model.protocol import Protocols @@ -23,7 +23,7 @@ class EndpointDesc(BaseModel): array: Optional[Union[Tuple[int], Tuple[int, int]]] = None num: Optional[int] = None addr_range: List[AddrRange] = [] - xy_id_offset: Optional[Id] = None + xy_id_offset: Optional[Union[SimpleId, Coord]] = None mgr_port_protocol: Optional[List[str]] = None sbr_port_protocol: Optional[List[str]] = None @@ -90,6 +90,10 @@ def is_only_mgr(self) -> bool: """Return true if the endpoint is only a manager.""" return self.is_mgr() and not self.is_sbr() + def is_multicast_ep(self) -> bool: + """Return true if the endpoint supports multicast.""" + return any([b for b in self.addr_range if b.en_multicast]) + def get_ni_name(self, name: str) -> str: """Return the name of the NI.""" return name.replace(self.name, f"{self.name}_ni") @@ -114,8 +118,9 @@ def from_desc(cls, desc: EndpointDesc, def render_ports(self, pkg_name=""): """Render the ports of the endpoint.""" ports = [] + mcast_prefix = "mcast" if self.is_multicast_ep() else "" for port in self.mgr_ports: - ports += port.render_port(pkg_name) + ports += port.render_port(pkg_name, prefix=mcast_prefix) for port in self.sbr_ports: - ports += port.render_port(pkg_name) + ports += port.render_port(pkg_name, prefix=mcast_prefix) return ports diff --git a/floogen/model/network.py b/floogen/model/network.py index 50cc7f2c0..09531b341 100644 --- a/floogen/model/network.py +++ b/floogen/model/network.py @@ -15,7 +15,7 @@ from mako.lookup import Template from pydantic import BaseModel, ConfigDict, StringConstraints, field_validator, model_validator -from floogen.model.routing import Routing, RouteAlgo, RouteMapRule, RouteRule, RouteMap, RouteTable +from floogen.model.routing import Routing, RouteAlgo, RouteMapRule, RouteRule, RouteMap, RouteTable, RouteMapRuleMcast from floogen.model.routing import Coord, SimpleId, AddrRange, XYDirections from floogen.model.graph import Graph from floogen.model.endpoint import EndpointDesc, Endpoint @@ -102,10 +102,11 @@ def validate_protocols(self): if len(set(prot.data_width for prot in self.protocols if prot.type == "wide")) != 1: raise ValueError("All `wide` protocols must have the same data width") # Check that `narrow` and `wide` protocols have the same user width - if len(set(prot.user_width for prot in self.protocols if prot.type == "narrow")) != 1: - raise ValueError("All `narrow` protocols must have the same user width") - if len(set(prot.user_width for prot in self.protocols if prot.type == "wide")) != 1: - raise ValueError("All `wide` protocols must have the same user width") + if not self.routing.en_multicast: + if len(set(prot.user_width for prot in self.protocols if prot.type == "narrow")) != 1: + raise ValueError("All `narrow` protocols must have the same user width") + if len(set(prot.user_width for prot in self.protocols if prot.type == "wide")) != 1: + raise ValueError("All `wide` protocols must have the same user width") # Check that `type` is defined when using `narrow-wide` network if any(prot.type not in ["narrow", "wide"] for prot in self.protocols) and \ "narrow-wide" in self.network_type: @@ -584,6 +585,8 @@ def gen_routing_info(self): f"Routing algorithm {self.routing.route_algo} is not supported yet" ) self.routing.sam = self.gen_sam() + if self.routing.en_multicast: + self.routing.multicast_sam = self.gen_multicast_sam() # Provide the routing info to the network interfaces for ni in self.graph.get_ni_nodes(): ni.routing = self.routing @@ -667,10 +670,27 @@ def gen_sam(self): rule_name = ni.endpoint.name if addr_range.desc is not None: rule_name += f"_{addr_range.desc}" - addr_rule = RouteMapRule(dest=dest, addr_range=addr_range, desc=rule_name) + addr_rule = RouteMapRule(dest=dest, addr_range=addr_range, en_multicast=addr_range.en_multicast, desc=rule_name) addr_table.append(addr_rule) return RouteMap(name="sam", rules=addr_table) + def gen_multicast_sam(self): + """Generate the multicast system address map, which contains additional mask + information.""" + addr_table = [] + for addr_rule in self.routing.sam.rules: + mask_fields = {} + if addr_rule.addr_range.en_multicast: + mask_offset_x = clog2(addr_rule.addr_range.size) + mask_fields = { + "mask_len": (self.routing.num_x_bits, self.routing.num_y_bits), + "mask_offset": (mask_offset_x + self.routing.num_y_bits, mask_offset_x), + "base_id": (addr_rule.dest.x - addr_rule.addr_range.arr_idx[0], + addr_rule.dest.y - addr_rule.addr_range.arr_idx[1]), + } + addr_table.append(RouteMapRuleMcast(**addr_rule.model_dump(), **mask_fields)) + return RouteMap(name="mcast_sam", rules=addr_table) + def render_ports(self, pkg_name=""): """Render the ports in the generated code.""" ports, declared_ports = [], [] diff --git a/floogen/model/network_interface.py b/floogen/model/network_interface.py index 0281cf4ca..42e62faa6 100644 --- a/floogen/model/network_interface.py +++ b/floogen/model/network_interface.py @@ -5,13 +5,13 @@ # # Author: Tim Fischer -from typing import Optional, ClassVar, List +from typing import Optional, ClassVar, List, Union from importlib.resources import files, as_file from pydantic import BaseModel from mako.lookup import Template -from floogen.model.routing import Id, SimpleId, Coord, AddrRange, Routing, RouteMap +from floogen.model.routing import SimpleId, Coord, AddrRange, Routing, RouteMap from floogen.model.protocol import AXI4 from floogen.model.link import NarrowWideLink, AxiLink from floogen.model.endpoint import EndpointDesc @@ -26,9 +26,9 @@ class NetworkInterface(BaseModel): description: str = "" routing: Routing table: Optional[RouteMap] = None - id: Optional[Id] = None + id: Optional[Union[SimpleId, Coord]] = None uid: Optional[SimpleId] = None - arr_idx: Optional[Id] = None + arr_idx: Optional[Union[SimpleId, Coord]] = None addr_range: Optional[List[AddrRange]] = None def is_sbr(self) -> bool: @@ -47,6 +47,10 @@ def is_only_mgr(self) -> bool: """Return true if the network interface is only a manager.""" return self.endpoint.is_mgr() and not self.endpoint.is_sbr() + def is_multicast_ni(self) -> bool: + """Return true if the network interface supports multicast.""" + return any([b for b in self.addr_range if b.en_multicast]) + def render_enum_name(self) -> str: """Render the enum name.""" name = f"{self.endpoint.name}" diff --git a/floogen/model/protocol.py b/floogen/model/protocol.py index 98df7d9a3..8346c0fbb 100644 --- a/floogen/model/protocol.py +++ b/floogen/model/protocol.py @@ -5,11 +5,11 @@ # # Author: Tim Fischer -from typing import Optional, List, TypeVar, Union +from typing import Dict, Optional, List, TypeVar, Union from typing_extensions import Annotated from pydantic import BaseModel, StringConstraints -from floogen.utils import snake_to_camel, sv_param_decl, sv_typedef, sv_struct_render +from floogen.utils import snake_to_camel, sv_param_decl, sv_typedef, sv_struct_render, sv_struct_typedef class ProtocolDesc(BaseModel): """Protocol class to describe a protocol.""" @@ -26,14 +26,12 @@ class AXI4(ProtocolDesc): data_width: int addr_width: int id_width: int - user_width: int + user_width: Union[int, Dict[str, int]] = 1 type_prefix: Optional[str] = "axi" - def type_name(self) -> str: + def type_name(self, prefix="") -> str: """Return the full name of the protocol.""" - if self.type_prefix is not None: - return f"{self.type_prefix}_{self.name}" - return self.name + return "_".join(filter(None, [prefix, self.type_prefix, self.name])) def render_params(self) -> str: """Render the parameters of the protocol.""" @@ -44,14 +42,21 @@ def render_params(self) -> str: string += sv_param_decl(cfull_name + "UserWidth", self.user_width) return string + "\n" - def render_typedefs(self) -> str: + def render_typedefs(self, prefix="", ignored_user_fields=[]) -> str: """Render the typedefs of the protocol.""" - name_t = self.type_name() + name_t = self.type_name() if prefix == "" else f"{prefix}_{self.type_name()}" string = sv_typedef(name_t + "_addr_t", array_size=self.addr_width) string += sv_typedef(name_t + "_data_t", array_size=self.data_width) string += sv_typedef(name_t + "_strb_t", array_size=self.data_width // 8) string += sv_typedef(name_t + "_id_t", array_size=self.id_width) - string += sv_typedef(name_t + "_user_t", array_size=self.user_width) + + match self.user_width: + case int(v): + string += sv_typedef(name_t + "_user_t", array_size=v) + case dict(d): + fields = {k: f"logic [{v-1}:0]" for k, v in d.items() if k not in ignored_user_fields} + string += sv_struct_typedef(name_t + "_user_t", fields) + string += f"`AXI_TYPEDEF_ALL_CT({name_t}, \ {name_t}_req_t, \ {name_t}_rsp_t, \ @@ -68,10 +73,15 @@ def render_cfg(cls, name, mgr_axi, sbr_axi) -> str: fields = { "AddrWidth": mgr_axi.addr_width, "DataWidth": mgr_axi.data_width, - "UserWidth": mgr_axi.user_width, "InIdWidth": mgr_axi.id_width, "OutIdWidth": sbr_axi.id_width, } + match mgr_axi.user_width: + case int(v): + fields["UserWidth"] = v + case dict(d): + fields["UserWidth"] = sum([v for k, v in d.items() if k != "mcast_mask"]) + return sv_param_decl(name, sv_struct_render(fields), dtype="axi_cfg_t") @@ -106,13 +116,13 @@ def _idx_to_sv_idx(self): return string return "" - def req_type(self) -> str: + def req_type(self, prefix="") -> str: """Return the request type of the protocol.""" - return f"{self.type_name()}_req_t" + return f"{self.type_name(prefix=prefix)}_req_t" - def rsp_type(self) -> str: + def rsp_type(self, prefix="") -> str: """Return the response type of the protocol.""" - return f"{self.type_name()}_rsp_t" + return f"{self.type_name(prefix=prefix)}_rsp_t" def req_name(self, port=False, idx=False) -> str: """Return the request name of the protocol.""" @@ -134,16 +144,16 @@ def declare(self) -> str: string += f"{self.rsp_type()} {self.rsp_name()};\n" return string + "\n" - def render_port(self, pkg_name="") -> List[str]: + def render_port(self, pkg_name="", prefix="") -> List[str]: """Render the port of the protocol.""" rev_direction = self._invert_dir() ports = [] ports.append( - f"{self.direction} {pkg_name}{self.req_type()} \ + f"{self.direction} {pkg_name}{self.req_type(prefix=prefix)} \ {self._array_to_sv_array()} {self.req_name(port=True)}" ) ports.append( - f"{rev_direction} {pkg_name}{self.rsp_type()} \ + f"{rev_direction} {pkg_name}{self.rsp_type(prefix=prefix)} \ {self._array_to_sv_array()} {self.rsp_name(port=True)}" ) return ports diff --git a/floogen/model/router.py b/floogen/model/router.py index 533d42906..9bf7aacfb 100644 --- a/floogen/model/router.py +++ b/floogen/model/router.py @@ -13,7 +13,7 @@ from pydantic import BaseModel, ConfigDict, field_validator, model_validator from mako.lookup import Template -from floogen.model.routing import RouteMap, Id, Coord, RouteAlgo +from floogen.model.routing import RouteMap, SimpleId, Coord, RouteAlgo from floogen.model.link import Link import floogen.templates @@ -26,7 +26,7 @@ class RouterDesc(BaseModel): name: str array: Optional[Union[Tuple[int], Tuple[int, int]]] = None tree: Optional[List[int]] = None - xy_id_offset: Optional[Id] = None + xy_id_offset: Optional[Union[SimpleId, Coord]] = None auto_connect: Optional[bool] = True degree: Optional[int] = None diff --git a/floogen/model/routing.py b/floogen/model/routing.py index c85f54903..3f0dab4a2 100644 --- a/floogen/model/routing.py +++ b/floogen/model/routing.py @@ -6,8 +6,7 @@ # Author: Tim Fischer from enum import Enum -from typing import Optional, List, Tuple -from abc import ABC, abstractmethod +from typing import Optional, List, Tuple, Union from pydantic import BaseModel, Field, ConfigDict, model_validator, field_validator @@ -65,25 +64,7 @@ def __str__(self): def __int__(self): return self.value -class Id(BaseModel, ABC): - """ID class.""" - - @abstractmethod - def render(self): - """Declare the Id generated code.""" - - @classmethod - def __get_validators__(cls): - yield cls.validate - - @classmethod - def validate(cls, value): - if not issubclass(value, Id): - raise ValueError("Invalid Object") - return value - - -class SimpleId(Id): +class SimpleId(BaseModel): """ID class.""" id: int @@ -118,7 +99,7 @@ def render(self, as_index=False): return f"[{self.id}]" -class Coord(Id): +class Coord(BaseModel): """2D coordinate class.""" x: int @@ -179,16 +160,17 @@ def get_dir(node, neighbor) -> XYDirections: class AddrRange(BaseModel): """Address range class.""" - model_config = ConfigDict(extra="forbid") + model_config = ConfigDict(extra="forbid", validate_assignement=True) start: int = Field(ge=0) end: int = Field(ge=0) size: int base: Optional[int] = None - arr_idx: Optional[Tuple[int]] = None - arr_dim: Optional[Tuple[int]] = None - desc: Optional[str] = None + arr_idx: Optional[Tuple[int, ...]] = None + arr_dim: Optional[Tuple[int, ...]] = None rdl_name: Optional[str] = None + en_multicast: bool = False + desc: Optional[str] = None def __str__(self): return f"[{self.start:X}:{self.end:X}]" @@ -206,9 +188,9 @@ def validate_input(self): addr_dict["start"] = base + size * m addr_dict["end"] = addr_dict["start"] + size case (m, n): - if self.arr_dim is None: + if addr_dict["arr_dim"] is None: raise ValueError("Array dimension must be specified for 2D arrays") - addr_dict["start"] = base + size * (m * self.arr_dim[1] + n) + addr_dict["start"] = base + size * (m * addr_dict["arr_dim"][1] + n) addr_dict["end"] = addr_dict["start"] + size case _: raise ValueError("Invalid array index specification") @@ -254,7 +236,7 @@ class RouteMapRule(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) - dest: Id + dest: Union[SimpleId, Coord] addr_range: AddrRange desc: Optional[str] = None @@ -266,17 +248,17 @@ def __lt__(self, other): def render(self, aw=None): """Render the SystemVerilog routing rule.""" + struct_fields = { + "idx": self.dest.render(), + "start_addr": self.addr_range.start, + "end_addr": self.addr_range.end + } if aw is not None: - return ( - f"'{{idx: {self.dest.render()}, " - f"start_addr: {aw}'h{self.addr_range.start:0{cdiv(aw,4)}x}, " - f"end_addr: {aw}'h{self.addr_range.end:0{cdiv(aw,4)}x}}}" - ) - return ( - f"'{{idx: {self.dest.render()}, " - f"start_addr: {self.addr_range.start}, " - f"end_addr: {self.addr_range.end}}}" - ) + # Routing table use simple integers, and don't have any address width + struct_fields["start_addr"] = f"{aw}'h{self.addr_range.start:0{cdiv(aw,4)}x}" + struct_fields["end_addr"] = f"{aw}'h{self.addr_range.end:0{cdiv(aw,4)}x}" + + return sv_struct_render(struct_fields) def render_desc(self): '''Render the description of the routing rule.''' @@ -318,12 +300,42 @@ def get_rdl(self, instance_name, rdl_as_mem=False, rdl_memwidth=8): ] return [] +class RouteMapRuleMcast(RouteMapRule): + """Routing rule class for multicast information.""" + + mask_offset: Optional[Tuple[int, int]] = None + mask_len: Optional[Tuple[int, int]] = None + base_id: Optional[Tuple[int, int]] = None + + def render(self, aw=None): + """Render the SystemVerilog routing rule.""" + if aw is None: + raise ValueError("Address width must be specified for multicast routing") + + # In multicast, the `idx` field becomes a nested structure + # where the `id` is the original `idx` + struct_fields = { + "idx": {"id": self.dest.render()}, + "start_addr": f"{aw}'h{self.addr_range.start:0{cdiv(aw,4)}x}", + "end_addr": f"{aw}'h{self.addr_range.end:0{cdiv(aw,4)}x}", + } + + # Non-multicast nodes, don't need any mask information + if self.mask_offset is None: + struct_fields["idx"]["mask_x"] = {"default": "'0"} + struct_fields["idx"]["mask_y"] = {"default": "'0"} + else: + struct_fields["idx"]["mask_x"] = {"offset": self.mask_offset[0], "len": self.mask_len[0], "base_id": self.base_id[0]} + struct_fields["idx"]["mask_y"] = {"offset": self.mask_offset[1], "len": self.mask_len[1], "base_id": self.base_id[1]} + + return sv_struct_render(struct_fields) + class RouteRule(BaseModel): """Routing rule class.""" route: Optional[List[Tuple[int, int]]] - id: Id + id: Union[SimpleId, Coord] desc: Optional[str] = None def render(self, num_route_bits): @@ -469,8 +481,17 @@ def render(self, aw=None): string += sv_param_decl(f"{snake_to_camel(self.name)}NumRules", len(rules)) + "\n" addr_type = f"logic [{aw-1}:0]" if aw is not None else "id_t" rule_type_dict = {} - rule_type_dict = {"idx": "id_t", "start_addr": addr_type, "end_addr": addr_type} - string += sv_struct_typedef(self.rule_type(), rule_type_dict) + if not isinstance(rules[0], RouteMapRuleMcast): + rule_type_dict = {"idx": "id_t", "start_addr": addr_type, "end_addr": addr_type} + string += sv_struct_typedef(self.rule_type(), rule_type_dict) + else: + rule_type_dict = {"offset": "int unsigned", "len": "int unsigned", "base_id": "int unsigned"} + string += sv_struct_typedef("mcast_mask_sel_t", rule_type_dict) + rule_type_dict = {"id": "id_t", "mask_x": "mcast_mask_sel_t", "mask_y": "mcast_mask_sel_t"} + string += sv_struct_typedef("mcast_idx_t", rule_type_dict) + rule_type_dict = {"idx": "mcast_idx_t", "start_addr": addr_type, "end_addr": addr_type} + string += sv_struct_typedef(self.rule_type(), rule_type_dict) + rules_str = "" if not rules: string += sv_param_decl( @@ -555,7 +576,7 @@ class Routing(BaseModel): sam: Optional[RouteMap] = None table: Optional[RouteMap] = None addr_offset_bits: Optional[int] = None - xy_id_offset: Optional[Id] = None + xy_id_offset: Optional[Union[SimpleId, Coord]] = None num_endpoints: Optional[int] = None num_id_bits: Optional[int] = None num_x_bits: Optional[int] = None @@ -566,6 +587,7 @@ class Routing(BaseModel): port_id_bits: int = 1 num_vc_id_bits: int = 0 en_multicast: bool = False + multicast_sam: Optional[RouteMap] = None en_parallel_reduction: bool = False en_narrow_offload_reduction: bool = False en_wide_offload_reduction: bool = False diff --git a/floogen/templates/floo_nw_chimney.sv.mako b/floogen/templates/floo_nw_chimney.sv.mako index 45a0be208..9e0d2b72c 100644 --- a/floogen/templates/floo_nw_chimney.sv.mako +++ b/floogen/templates/floo_nw_chimney.sv.mako @@ -24,6 +24,7 @@ floo_nw_chimney #( .dst_t (route_t), % endif .hdr_t (hdr_t), +% if not ni.is_multicast_ni(): .sam_rule_t(sam_rule_t), .Sam(Sam), .axi_narrow_in_req_t(${narrow_in_prot.type_name()}_req_t), @@ -34,6 +35,22 @@ floo_nw_chimney #( .axi_wide_in_rsp_t(${wide_in_prot.type_name()}_rsp_t), .axi_wide_out_req_t(${wide_out_prot.type_name()}_req_t), .axi_wide_out_rsp_t(${wide_out_prot.type_name()}_rsp_t), +% else: + .sam_rule_t(mcast_sam_rule_t), + .sam_idx_t(mcast_idx_t), + .mask_sel_t(mcast_mask_sel_t), + .Sam(McastSam), + // TODO(fischeti/lleone): Extend to narrow/wide user structs + .user_struct_t(${narrow_in_prot.type_name(prefix="mcast")}_user_t), + .axi_narrow_in_req_t(${narrow_in_prot.type_name(prefix="mcast")}_req_t), + .axi_narrow_in_rsp_t(${narrow_in_prot.type_name(prefix="mcast")}_rsp_t), + .axi_narrow_out_req_t(${narrow_out_prot.type_name(prefix="mcast")}_req_t), + .axi_narrow_out_rsp_t(${narrow_out_prot.type_name(prefix="mcast")}_rsp_t), + .axi_wide_in_req_t(${wide_in_prot.type_name(prefix="mcast")}_req_t), + .axi_wide_in_rsp_t(${wide_in_prot.type_name(prefix="mcast")}_rsp_t), + .axi_wide_out_req_t(${wide_out_prot.type_name(prefix="mcast")}_req_t), + .axi_wide_out_rsp_t(${wide_out_prot.type_name(prefix="mcast")}_rsp_t), +% endif .floo_req_t(floo_req_t), .floo_rsp_t(floo_rsp_t), .floo_wide_t(floo_wide_t) diff --git a/floogen/templates/floo_top_noc_pkg.sv.mako b/floogen/templates/floo_top_noc_pkg.sv.mako index 26bddbe05..43e95ebd5 100644 --- a/floogen/templates/floo_top_noc_pkg.sv.mako +++ b/floogen/templates/floo_top_noc_pkg.sv.mako @@ -26,6 +26,9 @@ package floo_${noc.name}_noc_pkg; % if noc.routing.use_id_table: ${noc.routing.sam.render(aw=noc.routing.addr_width)} + % if noc.routing.en_multicast: + ${noc.routing.multicast_sam.render(aw=noc.routing.addr_width)} + %endif % else: localparam int unsigned NumSamRules = 1; typedef logic sam_rule_t; @@ -39,7 +42,12 @@ package floo_${noc.name}_noc_pkg; ${noc.routing.render_route_cfg(name="RouteCfg")} % for prot in noc.protocols: - ${prot.render_typedefs()} + % if not noc.routing.en_multicast: + ${prot.render_typedefs()} + % else: + ${prot.render_typedefs(prefix="mcast")} + ${prot.render_typedefs(ignored_user_fields=["mcast_mask"])} + % endif % endfor ${noc.routing.render_hdr_typedef(network_type=noc.network_type)} diff --git a/floogen/utils.py b/floogen/utils.py index 24174c3fc..43989a21d 100644 --- a/floogen/utils.py +++ b/floogen/utils.py @@ -90,9 +90,29 @@ def sv_struct_typedef(name: str, fields: dict, union=False) -> str: return typedef def sv_struct_render(fields: dict) -> str: - """Declare a SystemVerilog struct.""" + """ + Declare a SystemVerilog struct based on a (nested) dictionary, + where they keys of the dictionary are the field names, and the values are + the actual values to assign. + + Example: + fields = {'field1': '3'd0', + 'field2': {'subfield1': 'some_signal', + 'subfield2': 'SomeParam'}} + sv_struct_render(fields) -> + '{ + field1: 3'd0, + field2: '{ + subfield1: some_signal, + subfield2: SomeParam + }, + }' + """ decl = "'{" for field, value in fields.items(): + # If the `value` itself is a dict, recursively render it as a struct + if isinstance(value, dict): + value = sv_struct_render(value) decl += f" {field}: {value},\n" return decl[:-2] + "}" diff --git a/hw/floo_id_translation.sv b/hw/floo_id_translation.sv index 2ba904fb2..1743520ac 100644 --- a/hw/floo_id_translation.sv +++ b/hw/floo_id_translation.sv @@ -42,16 +42,10 @@ module floo_id_translation #( mask_sel_t x_mask_sel, y_mask_sel; addr_t x_addr_mask, y_addr_mask; - // This is simply to pass the assertions in addr_decode - // It is not used otherwise, since we specify `idx_t` - localparam int unsigned MaxPossibleId = 1 << $bits(idx_out); - - addr_decode_dync #( - .NoIndices ( MaxPossibleId ), + addr_decode #( .NoRules ( RouteCfg.NumSamRules ), .addr_t ( addr_t ), .rule_t ( addr_rule_t ), - .IdxWidth ( $bits(idx_out) ), .idx_t ( sam_idx_t ) ) i_addr_dst_decode ( .addr_i, @@ -60,8 +54,7 @@ module floo_id_translation #( .dec_valid_o ( ), .dec_error_o ( dec_error ), .en_default_idx_i ( 1'b0 ), - .default_idx_i ( '0 ), - .config_ongoing_i ( 1'b0 ) + .default_idx_i ( '0 ) ); `ASSERT(DecodeError, !(dec_error && valid_i), clk_i, !rst_ni, diff --git a/hw/floo_meta_buffer.sv b/hw/floo_meta_buffer.sv index 54c5ed421..64ba8e2ce 100644 --- a/hw/floo_meta_buffer.sv +++ b/hw/floo_meta_buffer.sv @@ -225,12 +225,12 @@ module floo_meta_buffer #( mask_sel_t x_mask_sel, y_mask_sel; addr_t x_addr_mask, y_addr_mask; addr_t in_addr; - id_t grp_base_id; + id_t base_id; assign in_mask = aw_buf_i.hdr.mask; assign in_id = aw_buf_i.hdr.dst_id; - assign grp_base_id = '{x: x_mask_sel.grp_base_id, y: y_mask_sel.grp_base_id, port_id: '0}; - assign out = ((~in_mask & in_id) | (in_mask & id_i)) & ~grp_base_id; + assign base_id = '{x: x_mask_sel.base_id, y: y_mask_sel.base_id, port_id: '0}; + assign out = ((~in_mask & in_id) | (in_mask & id_i)) & ~base_id; assign in_addr = axi_req_i.aw.addr; floo_id_translation #( .RouteCfg (RouteCfg),