Skip to content
4 changes: 2 additions & 2 deletions Bender.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
13 changes: 9 additions & 4 deletions floogen/model/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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

Expand Down Expand Up @@ -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")
Expand All @@ -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
32 changes: 26 additions & 6 deletions floogen/model/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 = [], []
Expand Down
12 changes: 8 additions & 4 deletions floogen/model/network_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
#
# Author: Tim Fischer <[email protected]>

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
Expand All @@ -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:
Expand All @@ -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}"
Expand Down
46 changes: 28 additions & 18 deletions floogen/model/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#
# Author: Tim Fischer <[email protected]>

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."""
Expand All @@ -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."""
Expand All @@ -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, \
Expand All @@ -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")


Expand Down Expand Up @@ -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."""
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions floogen/model/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
Loading
Loading