1use std::fmt;
86use std::rc::Rc;
87
88use async_trait::async_trait;
89use clap::ValueEnum;
90use gwr_components::arbiter::Arbiter;
91use gwr_components::arbiter::policy::RoundRobin;
92use gwr_components::flow_controls::limiter::Limiter;
93use gwr_components::router::{Route, Router};
94use gwr_components::store::{ByteStore, Store};
95use gwr_components::{connect_port, rc_limiter};
96use gwr_engine::engine::Engine;
97use gwr_engine::port::PortStateResult;
98use gwr_engine::time::clock::Clock;
99use gwr_engine::traits::{Routable, SimObject};
100use gwr_engine::types::{SimError, SimResult};
101use gwr_model_builder::{EntityDisplay, EntityGet, Runnable};
102use gwr_track::build_aka;
103use gwr_track::entity::Entity;
104use gwr_track::tracker::aka::Aka;
105use serde::{Deserialize, Serialize};
106
107use crate::fabric::FabricConfig;
108
109#[derive(ValueEnum, Clone, Copy, Default, Debug, Serialize, PartialEq, Deserialize)]
110#[serde(rename_all = "kebab-case")]
111pub enum FabricRoutingAlgorithm {
112 #[default]
114 ColumnFirst,
115
116 RowFirst,
118}
119
120struct NodeRouter {
121 index: usize,
122 node_col: usize,
123 node_row: usize,
124 fabric_algorithm: FabricRoutingAlgorithm,
125 config: Rc<FabricConfig>,
126}
127
128impl<T> Route<T> for NodeRouter
129where
130 T: SimObject + Routable,
131{
132 fn route(&self, object: &T) -> Result<usize, SimError> {
140 let dest_fabric_port = object.destination() as usize;
141 let (dest_col, dest_row, dest_port) = self
142 .config
143 .fabric_port_index_to_col_row_port(dest_fabric_port);
144 let dest_port = if (self.node_col == dest_col) && (self.node_row == dest_row) {
145 dest_port + (Port::Ingress as usize)
147 } else if self.node_col == dest_col {
148 if self.node_row < dest_row {
150 Port::RowPlus as usize
151 } else {
152 Port::RowMinus as usize
153 }
154 } else if self.node_row == dest_row {
155 if self.node_col < dest_col {
157 Port::ColPlus as usize
158 } else {
159 Port::ColMinus as usize
160 }
161 } else {
162 match self.fabric_algorithm {
164 FabricRoutingAlgorithm::ColumnFirst => {
165 if self.node_col < dest_col {
166 Port::ColPlus as usize
167 } else {
168 Port::ColMinus as usize
169 }
170 }
171 FabricRoutingAlgorithm::RowFirst => {
172 if self.node_row < dest_row {
173 Port::RowPlus as usize
174 } else {
175 Port::RowMinus as usize
176 }
177 }
178 }
179 };
180
181 assert_ne!(
182 dest_port, self.index,
183 "cannot route frame to egress from same port as ingress"
184 );
185
186 if dest_port > self.index {
200 Ok(dest_port - 1)
201 } else {
202 Ok(dest_port)
203 }
204 }
205}
206
207#[repr(usize)]
208#[derive(Copy, Clone, Debug)]
209pub enum Port {
210 ColMinus = 0,
211 ColPlus,
212 RowMinus,
213 RowPlus,
214 Ingress,
215}
216
217impl fmt::Display for Port {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 let name = match self {
221 Port::ColMinus => "col_minus",
222 Port::ColPlus => "col_plus",
223 Port::RowMinus => "row_minus",
224 Port::RowPlus => "row_plus",
225 Port::Ingress => "???",
226 };
227 write!(f, "{name}")
228 }
229}
230
231type RouterArbiterResult<T> = (Rc<Arbiter<T>>, Rc<Router<T>>);
232
233#[expect(clippy::too_many_arguments)]
234fn router_arbiter<T>(
235 engine: &Engine,
236 clock: &Clock,
237 node: &Rc<Entity>,
238 config: Rc<FabricConfig>,
239 fabric_algorithm: FabricRoutingAlgorithm,
240 num_arbiter_router_ports: usize,
241 router_arbiter_index: usize,
242 node_col: usize,
243 node_row: usize,
244 name: &str,
245) -> RouterArbiterResult<T>
246where
247 T: SimObject + Routable,
248{
249 let policy = Box::new(RoundRobin::new());
250 let algorithm = Box::new(NodeRouter {
251 index: router_arbiter_index,
252 node_col,
253 node_row,
254 fabric_algorithm,
255 config,
256 });
257 (
258 Arbiter::new_and_register(
259 engine,
260 clock,
261 node,
262 &format!("arb_{name}"),
263 num_arbiter_router_ports,
264 policy,
265 ),
266 Router::new_and_register(
267 engine,
268 clock,
269 node,
270 &format!("router_{name}"),
271 num_arbiter_router_ports,
272 algorithm,
273 ),
274 )
275}
276
277type Arbiters<T> = Vec<Rc<Arbiter<T>>>;
278type Routers<T> = Vec<Rc<Router<T>>>;
279type RoutersArbitersResult<T> = (Arbiters<T>, Routers<T>);
280
281#[expect(clippy::too_many_arguments)]
282fn create_arbiters_routers<T>(
283 engine: &Engine,
284 clock: &Clock,
285 node: &Rc<Entity>,
286 config: &Rc<FabricConfig>,
287 fabric_algorithm: FabricRoutingAlgorithm,
288 num_ingress_egress_ports: usize,
289 node_col: usize,
290 node_row: usize,
291) -> RoutersArbitersResult<T>
292where
293 T: SimObject + Routable,
294{
295 let num_arbiters_routers = Port::Ingress as usize + num_ingress_egress_ports;
296
297 let num_arbiter_router_ports = num_arbiters_routers - 1;
299
300 let mut arbiters = Vec::with_capacity(num_arbiters_routers);
301 let mut routers = Vec::with_capacity(num_arbiters_routers);
302
303 for (i, port) in vec![Port::ColMinus, Port::ColPlus, Port::RowMinus, Port::RowPlus]
304 .drain(..)
305 .enumerate()
306 {
307 let name = port.to_string();
308 let (arbiter, router) = router_arbiter(
309 engine,
310 clock,
311 node,
312 config.clone(),
313 fabric_algorithm,
314 num_arbiter_router_ports,
315 i,
316 node_col,
317 node_row,
318 name.as_str(),
319 );
320 arbiters.push(arbiter);
321 routers.push(router);
322 }
323
324 for i in 0..num_ingress_egress_ports {
325 let ingress_egress_index = i + Port::Ingress as usize;
326 let policy = Box::new(RoundRobin::new());
327 arbiters.push(Arbiter::new_and_register(
328 engine,
329 clock,
330 node,
331 &format!("arb_{ingress_egress_index}"),
332 num_arbiter_router_ports,
333 policy,
334 ));
335 let algorithm = Box::new(NodeRouter {
336 index: ingress_egress_index,
337 node_col,
338 node_row,
339 fabric_algorithm,
340 config: config.clone(),
341 });
342 routers.push(Router::new_and_register(
343 engine,
344 clock,
345 node,
346 &format!("router_{ingress_egress_index}"),
347 num_arbiter_router_ports,
348 algorithm,
349 ));
350 }
351
352 (arbiters, routers)
353}
354
355type IngressEgressBuffersResult<T> = Result<(Vec<Rc<Limiter<T>>>, Vec<Rc<Store<T>>>), SimError>;
356
357#[expect(clippy::too_many_arguments)]
358fn create_ingress_egress_buffers<T>(
359 engine: &Engine,
360 clock: &Clock,
361 node: &Rc<Entity>,
362 aka: Option<&Aka>,
363 config: &Rc<FabricConfig>,
364 num_ingress_egress_ports: usize,
365 arbiters: &Arbiters<T>,
366 routers: &Routers<T>,
367) -> IngressEgressBuffersResult<T>
368where
369 T: SimObject + Routable,
370{
371 let mut ingress_buffer_limiters = Vec::with_capacity(num_ingress_egress_ports);
372 let mut egress_buffers = Vec::with_capacity(num_ingress_egress_ports);
373
374 let port_limiter = rc_limiter!(clock, config.port_bits_per_tick);
375 for i in 0..num_ingress_egress_ports {
376 let ingress_egress_index = Port::Ingress as usize + i;
377 let ingress_buffer_limiter_aka = build_aka!(aka, node, &[(&format!("ingress_{i}"), "rx")]);
379 let ingress_buffer_limiter = Limiter::new_and_register_with_renames(
380 engine,
381 clock,
382 node,
383 &format!("limit_ingress_{i}"),
384 Some(&ingress_buffer_limiter_aka),
385 port_limiter.clone(),
386 );
387 let ingress_buffer = ByteStore::new_and_register(
388 engine,
389 clock,
390 node,
391 &format!("ingress_buf_{i}"),
392 config.rx_buffer_bytes,
393 )?;
394 connect_port!(ingress_buffer_limiter, tx => ingress_buffer, rx)
395 .expect("Internal ports should connect without error");
396 connect_port!(ingress_buffer, tx => routers[ingress_egress_index], rx)
397 .expect("Internal ports should connect without error");
398 ingress_buffer_limiters.push(ingress_buffer_limiter);
399
400 let egress_buffer_limiter = Limiter::new_and_register(
402 engine,
403 clock,
404 node,
405 &format!("limit_egress_{i}"),
406 port_limiter.clone(),
407 );
408 let egress_buffer_aka = build_aka!(aka, node, &[(&format!("egress_{i}"), "tx")]);
409 let egress_buffer = ByteStore::new_and_register_with_renames(
410 engine,
411 clock,
412 node,
413 &format!("egress_buf_{i}"),
414 Some(&egress_buffer_aka),
415 config.tx_buffer_bytes,
416 )?;
417 connect_port!(egress_buffer_limiter, tx => egress_buffer, rx)
418 .expect("Internal ports should connect without error");
419 connect_port!(arbiters[ingress_egress_index], tx => egress_buffer_limiter, rx)
420 .expect("Internal ports should connect without error");
421 egress_buffers.push(egress_buffer);
422 }
423
424 Ok((ingress_buffer_limiters, egress_buffers))
425}
426
427#[derive(EntityGet, EntityDisplay, Runnable)]
428pub struct FabricNode<T>
429where
430 T: SimObject + Routable,
431{
432 entity: Rc<Entity>,
433
434 arbiters: Vec<Rc<Arbiter<T>>>,
435 routers: Vec<Rc<Router<T>>>,
436
437 ingress_buffer_limiters: Vec<Rc<Limiter<T>>>,
438 egress_buffers: Vec<Rc<Store<T>>>,
439}
440
441impl<T> FabricNode<T>
442where
443 T: SimObject + Routable,
444{
445 #[expect(clippy::too_many_arguments)]
446 pub fn new_and_register_with_renames(
447 engine: &Engine,
448 clock: &Clock,
449 parent: &Rc<Entity>,
450 name: &str,
451 aka: Option<&Aka>,
452 node_col: usize,
453 node_row: usize,
454 config: &Rc<FabricConfig>,
455 fabric_algorithm: FabricRoutingAlgorithm,
456 ) -> Result<Rc<Self>, SimError> {
457 let entity = Rc::new(Entity::new(parent, name));
458
459 let num_ingress_egress_ports = config.node_num_ingress_egress_ports(node_col, node_row);
460
461 let (arbiters, routers) = create_arbiters_routers(
462 engine,
463 clock,
464 &entity,
465 config,
466 fabric_algorithm,
467 num_ingress_egress_ports,
468 node_col,
469 node_row,
470 );
471
472 let (ingress_buffer_limiters, egress_buffers) = create_ingress_egress_buffers(
473 engine,
474 clock,
475 &entity,
476 aka,
477 config,
478 num_ingress_egress_ports,
479 &arbiters,
480 &routers,
481 )?;
482
483 for (from, router) in routers.iter().enumerate() {
485 for (to, arbiter) in arbiters.iter().enumerate() {
486 if from == to {
487 continue;
488 }
489
490 let to_index = if to > from { to - 1 } else { to };
491 let from_index = if from > to { from - 1 } else { from };
492 connect_port!(router, tx, to_index => arbiter, rx, from_index)
493 .expect("Internal ports should connect without error");
494 }
495 }
496
497 let rc_self = Rc::new(Self {
498 entity,
499 ingress_buffer_limiters,
500 egress_buffers,
501 arbiters,
502 routers,
503 });
504 engine.register(rc_self.clone());
505 Ok(rc_self)
506 }
507
508 #[expect(clippy::too_many_arguments)]
509 pub fn new_and_register(
510 engine: &Engine,
511 clock: &Clock,
512 parent: &Rc<Entity>,
513 name: &str,
514 node_col: usize,
515 node_row: usize,
516 config: &Rc<FabricConfig>,
517 fabric_algorithm: FabricRoutingAlgorithm,
518 ) -> Result<Rc<Self>, SimError> {
519 Self::new_and_register_with_renames(
520 engine,
521 clock,
522 parent,
523 name,
524 None,
525 node_col,
526 node_row,
527 config,
528 fabric_algorithm,
529 )
530 }
531
532 pub fn connect_port_egress_i(&self, i: usize, port_state: PortStateResult<T>) -> SimResult {
533 self.egress_buffers[i].connect_port_tx(port_state)
534 }
535 pub fn port_ingress_i(&self, i: usize) -> PortStateResult<T> {
536 self.ingress_buffer_limiters[i].port_rx()
537 }
538
539 pub fn connect_port_row_minus(&self, port_state: PortStateResult<T>) -> SimResult {
540 self.arbiters[Port::RowMinus as usize].connect_port_tx(port_state)
541 }
542 pub fn connect_port_row_plus(&self, port_state: PortStateResult<T>) -> SimResult {
543 self.arbiters[Port::RowPlus as usize].connect_port_tx(port_state)
544 }
545 pub fn connect_port_col_minus(&self, port_state: PortStateResult<T>) -> SimResult {
546 self.arbiters[Port::ColMinus as usize].connect_port_tx(port_state)
547 }
548 pub fn connect_port_col_plus(&self, port_state: PortStateResult<T>) -> SimResult {
549 self.arbiters[Port::ColPlus as usize].connect_port_tx(port_state)
550 }
551
552 pub fn port_row_minus(&self) -> PortStateResult<T> {
553 self.routers[Port::RowMinus as usize].port_rx()
554 }
555 pub fn port_row_plus(&self) -> PortStateResult<T> {
556 self.routers[Port::RowPlus as usize].port_rx()
557 }
558 pub fn port_col_minus(&self) -> PortStateResult<T> {
559 self.routers[Port::ColMinus as usize].port_rx()
560 }
561 pub fn port_col_plus(&self) -> PortStateResult<T> {
562 self.routers[Port::ColPlus as usize].port_rx()
563 }
564}