1use std::rc::Rc;
23
24use async_trait::async_trait;
25use gwr_components::delay::Delay;
26use gwr_components::{connect_dummy_rx, connect_dummy_tx, connect_port};
27use gwr_engine::engine::Engine;
28use gwr_engine::port::PortStateResult;
29use gwr_engine::sim_error;
30use gwr_engine::time::clock::Clock;
31use gwr_engine::traits::{Routable, SimObject};
32use gwr_engine::types::{SimError, SimResult};
33use gwr_model_builder::{EntityDisplay, EntityGet, Runnable};
34use gwr_track::entity::Entity;
35use gwr_track::tracker::aka::{Aka, populate_aka_from_string};
36
37use crate::fabric::node::{FabricNode, FabricRoutingAlgorithm};
38use crate::fabric::{Fabric, FabricConfig};
39
40#[derive(EntityGet, EntityDisplay, Runnable)]
41pub struct RoutedFabric<T>
42where
43 T: SimObject + Routable,
44{
45 entity: Rc<Entity>,
46 nodes: Vec<Vec<Rc<FabricNode<T>>>>,
47 config: Rc<FabricConfig>,
48}
49
50fn build_node_aka(
51 entity: &Rc<Entity>,
52 aka: Option<&Aka>,
53 new_aka: &mut Aka,
54 col: usize,
55 row: usize,
56 config: &Rc<FabricConfig>,
57) {
58 let mut renames = Vec::new();
59 for port in 0..config.num_ports_per_node() {
60 let fabric_port_index = config.col_row_port_to_fabric_port_index(col, row, port);
61 renames.push((
62 format!("ingress_{fabric_port_index}"),
63 format!("ingress_{port}"),
64 ));
65 renames.push((
66 format!("egress_{fabric_port_index}"),
67 format!("egress_{port}"),
68 ));
69 }
70 populate_aka_from_string(aka, Some(new_aka), entity, &renames);
71}
72
73type FabricNodesResult<T> = Result<Vec<Vec<Rc<FabricNode<T>>>>, SimError>;
74
75fn create_nodes<T>(
76 engine: &Engine,
77 clock: &Clock,
78 entity: &Rc<Entity>,
79 aka: Option<&Aka>,
80 config: &Rc<FabricConfig>,
81 fabric_algorithm: FabricRoutingAlgorithm,
82) -> FabricNodesResult<T>
83where
84 T: SimObject + Routable,
85{
86 let num_columns = config.num_columns();
87 let num_rows = config.num_rows();
88 let mut nodes = Vec::with_capacity(num_columns);
89
90 for c in 0..num_columns {
91 let mut col_nodes = Vec::with_capacity(num_rows);
92 for r in 0..num_rows {
93 let mut new_aka = Aka::default();
94 build_node_aka(entity, aka, &mut new_aka, c, r, config);
95 let node = FabricNode::new_and_register_with_renames(
96 engine,
97 clock,
98 entity,
99 &format!("node_{c}_{r}"),
100 Some(&new_aka),
101 c,
102 r,
103 config,
104 fabric_algorithm,
105 )?;
106 col_nodes.push(node);
107 }
108 nodes.push(col_nodes);
109 }
110 Ok(nodes)
111}
112
113fn connect_columns<T>(
115 engine: &Engine,
116 clock: &Clock,
117 entity: &Rc<Entity>,
118 config: &Rc<FabricConfig>,
119 nodes: &[Vec<Rc<FabricNode<T>>>],
120 delay_ticks: usize,
121) where
122 T: SimObject + Routable,
123{
124 for c in 1..config.num_columns {
125 let c_m1 = c - 1;
126 #[expect(clippy::needless_range_loop)]
132 for r in 0..config.num_rows {
133 let delay = Delay::new_and_register(
134 engine,
135 clock,
136 entity,
137 &format!("{c_m1}_{r}_to_{c}_{r}"),
138 delay_ticks,
139 );
140 connect_port!(nodes[c_m1][r], col_plus => delay, rx)
141 .expect("Internal ports should connect without error");
142 connect_port!(delay, tx => nodes[c][r], col_minus)
143 .expect("Internal ports should connect without error");
144
145 let delay = Delay::new_and_register(
146 engine,
147 clock,
148 entity,
149 &format!("{c}_{r}_to_{c_m1}_{r}"),
150 delay_ticks,
151 );
152 connect_port!(nodes[c][r], col_minus => delay, rx)
153 .expect("Internal ports should connect without error");
154 connect_port!(delay, tx => nodes[c_m1][r], col_plus)
155 .expect("Internal ports should connect without error");
156 }
157 }
158}
159
160fn connect_rows<T>(
162 engine: &Engine,
163 clock: &Clock,
164 entity: &Rc<Entity>,
165 config: &Rc<FabricConfig>,
166 nodes: &[Vec<Rc<FabricNode<T>>>],
167 delay_ticks: usize,
168) where
169 T: SimObject + Routable,
170{
171 for (c, col) in nodes.iter().enumerate() {
172 for r in 1..config.num_rows {
173 let r_m1 = r - 1;
174 let delay = Delay::new_and_register(
175 engine,
176 clock,
177 entity,
178 &format!("{c}_{r_m1}_to_{c}_{r}"),
179 delay_ticks,
180 );
181 connect_port!(col[r_m1], row_plus => delay, rx)
182 .expect("Internal ports should connect without error");
183 connect_port!(delay, tx => col[r], row_minus)
184 .expect("Internal ports should connect without error");
185
186 let delay = Delay::new_and_register(
187 engine,
188 clock,
189 entity,
190 &format!("{c}_{r}_to_{c}_{r_m1}"),
191 delay_ticks,
192 );
193 connect_port!(col[r], row_minus => delay, rx)
194 .expect("Internal ports should connect without error");
195 connect_port!(delay, tx => col[r_m1], row_plus)
196 .expect("Internal ports should connect without error");
197 }
198 }
199}
200
201fn create_dummy_ports<T>(
203 engine: &Engine,
204 clock: &Clock,
205 entity: &Rc<Entity>,
206 config: &Rc<FabricConfig>,
207 nodes: &[Vec<Rc<FabricNode<T>>>],
208) where
209 T: SimObject + Routable,
210{
211 let right = config.num_columns - 1;
213 #[expect(clippy::needless_range_loop)]
217 for r in 0..config.num_rows {
218 connect_dummy_tx!(entity => nodes[0][r], col_minus)
219 .expect("Internal ports should connect without error");
220 connect_dummy_rx!(nodes[0][r], col_minus => engine, clock, entity)
221 .expect("Internal ports should connect without error");
222 connect_dummy_tx!(entity => nodes[right][r], col_plus)
223 .expect("Internal ports should connect without error");
224 connect_dummy_rx!(nodes[right][r], col_plus => engine, clock, entity)
225 .expect("Internal ports should connect without error");
226 }
227
228 let bottom = config.num_rows - 1;
230 for col in nodes {
231 connect_dummy_tx!(entity => col[0], row_minus)
232 .expect("Internal ports should connect without error");
233 connect_dummy_rx!(col[0], row_minus => engine, clock, entity)
234 .expect("Internal ports should connect without error");
235 connect_dummy_tx!(entity => col[bottom], row_plus)
236 .expect("Internal ports should connect without error");
237 connect_dummy_rx!(col[bottom], row_plus => engine, clock, entity)
238 .expect("Internal ports should connect without error");
239 }
240}
241
242impl<T> RoutedFabric<T>
243where
244 T: SimObject + Routable,
245{
246 pub fn new_and_register_with_renames(
247 engine: &Engine,
248 clock: &Clock,
249 parent: &Rc<Entity>,
250 name: &str,
251 aka: Option<&Aka>,
252 config: Rc<FabricConfig>,
253 fabric_algorithm: FabricRoutingAlgorithm,
254 ) -> Result<Rc<Self>, SimError> {
255 let entity = Rc::new(Entity::new(parent, name));
256 let num_ports = config.num_columns * config.num_rows * config.num_ports_per_node;
257 if num_ports < 2 {
258 return sim_error!("Cannot create fabric with less than 2 ports");
259 }
260
261 let nodes = create_nodes(engine, clock, &entity, aka, &config, fabric_algorithm)?;
262 connect_columns(
263 engine,
264 clock,
265 &entity,
266 &config,
267 &nodes,
268 config.cycles_per_hop,
269 );
270 connect_rows(
271 engine,
272 clock,
273 &entity,
274 &config,
275 &nodes,
276 config.cycles_per_hop,
277 );
278 create_dummy_ports(engine, clock, &entity, &config, &nodes);
279
280 let rc_self = Rc::new(Self {
281 entity,
282 nodes,
283 config,
284 });
285
286 engine.register(rc_self.clone());
287 Ok(rc_self)
288 }
289
290 pub fn new_and_register(
291 engine: &Engine,
292 clock: &Clock,
293 parent: &Rc<Entity>,
294 name: &str,
295 config: Rc<FabricConfig>,
296 fabric_algorithm: FabricRoutingAlgorithm,
297 ) -> Result<Rc<Self>, SimError> {
298 Self::new_and_register_with_renames(
299 engine,
300 clock,
301 parent,
302 name,
303 None,
304 config,
305 fabric_algorithm,
306 )
307 }
308}
309
310impl<T> Fabric<T> for RoutedFabric<T>
311where
312 T: SimObject + Routable,
313{
314 fn connect_port_egress_i(&self, i: usize, port_state: PortStateResult<T>) -> SimResult {
315 let (c, r, p) = self.config.fabric_port_index_to_col_row_port(i);
316 self.nodes[c][r].connect_port_egress_i(p, port_state)
317 }
318
319 fn port_ingress_i(&self, i: usize) -> PortStateResult<T> {
320 let (c, r, p) = self.config.fabric_port_index_to_col_row_port(i);
321 self.nodes[c][r].port_ingress_i(p)
322 }
323
324 fn col_row_port_to_fabric_port_index(&self, col: usize, row: usize, port: usize) -> usize {
325 self.config
326 .col_row_port_to_fabric_port_index(col, row, port)
327 }
328}