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(Source::new_and_register(
87            engine,
88            top,
89            &format!("source_{i}"),
90            Some(Box::new(FrameGen::new(
91                top,
92                u64_to_mac(neighbour_left as u64),
93                config.frame_payload_bytes,
94                config.num_send_frames,
95            ))),
96        ));
97    }
98
99    let sinks: Sinks = (0..config.ring_size)
100        .map(|i| Sink::new_and_register(engine, clock, top, &format!("sink_{i}")))
101        .collect();
102
103    (sources, sinks)
104}
105
106pub fn build_pipes(engine: &mut Engine, clock: &Clock, config: &Config) -> (Pipes, Pipes) {
107    let mut ingress_pipes = Vec::with_capacity(config.ring_size);
108    let mut ring_pipes = Vec::with_capacity(config.ring_size);
109
110    let top = engine.top();
111
112    let pipe_config = FcPipelineConfig::new(500, 500, 500);
113    for i in 0..config.ring_size {
114        ingress_pipes.push(
115            FcPipeline::new_and_register(
116                engine,
117                clock,
118                top,
119                &format!("ingress_pipe_{i}"),
120                &pipe_config,
121            )
122            .unwrap(),
123        );
124        ring_pipes.push(
125            FcPipeline::new_and_register(
126                engine,
127                clock,
128                top,
129                &format!("ring_pipe_{i}"),
130                &pipe_config,
131            )
132            .unwrap(),
133        );
134    }
135    (ingress_pipes, ring_pipes)
136}
137
138pub fn build_limiters(
139    engine: &mut Engine,
140    clock: &Clock,
141    config: &Config,
142    gbps: usize,
143) -> (Limiters, Limiters, Limiters) {
144    let limiter_gbps = rc_limiter!(clock, gbps);
145    let top = engine.top();
146    let source_limiters: Limiters = (0..config.ring_size)
147        .map(|i| {
148            Limiter::new_and_register(
149                engine,
150                clock,
151                top,
152                &format!("src_limit_{i}"),
153                limiter_gbps.clone(),
154            )
155        })
156        .collect();
157
158    let ring_limiters: Limiters = (0..config.ring_size)
159        .map(|i| {
160            Limiter::new_and_register(
161                engine,
162                clock,
163                top,
164                &format!("ring_limit_{i}"),
165                limiter_gbps.clone(),
166            )
167        })
168        .collect();
169
170    let sink_limiters: Limiters = (0..config.ring_size)
171        .map(|i| {
172            Limiter::new_and_register(
173                engine,
174                clock,
175                top,
176                &format!("sink_limit_{i}"),
177                limiter_gbps.clone(),
178            )
179        })
180        .collect();
181    (source_limiters, ring_limiters, sink_limiters)
182}