sim_ring/
ring_builder.rs

1// Copyright (c) 2025 Graphcore Ltd. All rights reserved.
2
3//! Library functions to build parts of the sim-ring application.
4
5use std::rc::Rc;
6
7use gwr_components::arbiter::policy::WeightedRoundRobin;
8use gwr_components::flow_controls::limiter::Limiter;
9use gwr_components::rc_limiter;
10use gwr_components::router::Route;
11use gwr_components::sink::Sink;
12use gwr_components::source::Source;
13use gwr_engine::engine::Engine;
14use gwr_engine::time::clock::Clock;
15use gwr_engine::traits::Routable;
16use gwr_engine::types::SimError;
17use gwr_models::ethernet_frame::{EthernetFrame, u64_to_mac};
18use gwr_models::fc_pipeline::{FcPipeline, FcPipelineConfig};
19use gwr_models::ring_node::{IO_INDEX, RING_INDEX, RingConfig, RingNode};
20
21use crate::frame_gen::FrameGen;
22
23// Define some types to aid readability
24pub type Limiters = Vec<Rc<Limiter<EthernetFrame>>>;
25pub type Nodes = Vec<Rc<RingNode<EthernetFrame>>>;
26pub type Pipes = Vec<Rc<FcPipeline<EthernetFrame>>>;
27pub type Sources = Vec<Rc<Source<EthernetFrame>>>;
28pub type Sinks = Vec<Rc<Sink<EthernetFrame>>>;
29
30pub struct Config {
31    pub ring_size: usize,
32    pub ring_priority: usize,
33    pub rx_buffer_frames: usize,
34    pub tx_buffer_frames: usize,
35    pub frame_payload_bytes: usize,
36    pub num_send_frames: usize,
37}
38
39struct RoutingAlgorithm(usize);
40
41impl<T> Route<T> for RoutingAlgorithm
42where
43    T: Routable,
44{
45    fn route(&self, obj: &T) -> Result<usize, SimError> {
46        // If the dest matches then exit via port 1, otherwise use port 0 as that is the
47        // ring
48        let dest = obj.destination() as usize;
49        Ok(if self.0 == dest { IO_INDEX } else { RING_INDEX })
50    }
51}
52
53pub fn build_ring_nodes(engine: &mut Engine, clock: &Clock, config: &Config) -> Nodes {
54    let limiter_128_gbps = rc_limiter!(clock, 128);
55    let ring_config = RingConfig::new(
56        config.rx_buffer_frames,
57        config.tx_buffer_frames,
58        limiter_128_gbps.clone(),
59    );
60    let top = engine.top();
61    let ring_nodes: Nodes = (0..config.ring_size)
62        .map(|i| {
63            let weights = vec![config.ring_priority, 1];
64            RingNode::new_and_register(
65                engine,
66                clock,
67                top,
68                &format!("node_{i}"),
69                &ring_config,
70                Box::new(RoutingAlgorithm(i)),
71                Box::new(WeightedRoundRobin::new(weights, 2).unwrap()),
72            )
73            .unwrap()
74        })
75        .collect();
76    ring_nodes
77}
78
79pub fn build_source_sinks(engine: &mut Engine, clock: &Clock, config: &Config) -> (Sources, Sinks) {
80    let mut sources = Vec::with_capacity(config.ring_size);
81    let top = engine.top();
82
83    for i in 0..config.ring_size {
84        let neighbour_left = if i > 0 { i - 1 } else { config.ring_size - 1 };
85
86        sources.push(
87            Source::new_and_register(
88                engine,
89                top,
90                &format!("source_{i}"),
91                Some(Box::new(FrameGen::new(
92                    top,
93                    u64_to_mac(neighbour_left as u64),
94                    config.frame_payload_bytes,
95                    config.num_send_frames,
96                ))),
97            )
98            .unwrap(),
99        );
100    }
101
102    let sinks: Sinks = (0..config.ring_size)
103        .map(|i| Sink::new_and_register(engine, clock, top, &format!("sink_{i}")).unwrap())
104        .collect();
105
106    (sources, sinks)
107}
108
109pub fn build_pipes(engine: &mut Engine, clock: &Clock, config: &Config) -> (Pipes, Pipes) {
110    let mut ingress_pipes = Vec::with_capacity(config.ring_size);
111    let mut ring_pipes = Vec::with_capacity(config.ring_size);
112
113    let top = engine.top();
114
115    let pipe_config = FcPipelineConfig::new(500, 500, 500);
116    for i in 0..config.ring_size {
117        ingress_pipes.push(
118            FcPipeline::new_and_register(
119                engine,
120                clock,
121                top,
122                &format!("ingress_pipe_{i}"),
123                &pipe_config,
124            )
125            .unwrap(),
126        );
127        ring_pipes.push(
128            FcPipeline::new_and_register(
129                engine,
130                clock,
131                top,
132                &format!("ring_pipe_{i}"),
133                &pipe_config,
134            )
135            .unwrap(),
136        );
137    }
138    (ingress_pipes, ring_pipes)
139}
140
141pub fn build_limiters(
142    engine: &mut Engine,
143    clock: &Clock,
144    config: &Config,
145    gbps: usize,
146) -> (Limiters, Limiters, Limiters) {
147    let limiter_gbps = rc_limiter!(clock, gbps);
148    let top = engine.top();
149    let source_limiters: Limiters = (0..config.ring_size)
150        .map(|i| {
151            Limiter::new_and_register(
152                engine,
153                clock,
154                top,
155                &format!("src_limit_{i}"),
156                limiter_gbps.clone(),
157            )
158            .unwrap()
159        })
160        .collect();
161
162    let ring_limiters: Limiters = (0..config.ring_size)
163        .map(|i| {
164            Limiter::new_and_register(
165                engine,
166                clock,
167                top,
168                &format!("ring_limit_{i}"),
169                limiter_gbps.clone(),
170            )
171            .unwrap()
172        })
173        .collect();
174
175    let sink_limiters: Limiters = (0..config.ring_size)
176        .map(|i| {
177            Limiter::new_and_register(
178                engine,
179                clock,
180                top,
181                &format!("sink_limit_{i}"),
182                limiter_gbps.clone(),
183            )
184            .unwrap()
185        })
186        .collect();
187    (source_limiters, ring_limiters, sink_limiters)
188}