1use std::fmt;
4use std::rc::Rc;
5
6use clap::ValueEnum;
7use gwr_engine::types::AccessType;
8use gwr_model_builder::EntityGet;
9use gwr_models::fabric::FabricConfig;
10use gwr_models::memory::memory_access::MemoryAccess;
11use gwr_models::memory::memory_map::DeviceId;
12use gwr_track::entity::Entity;
13use rand::SeedableRng;
14use rand::seq::{IteratorRandom, SliceRandom};
15use rand_xoshiro::Xoshiro256PlusPlus;
16use serde::Serialize;
17
18#[derive(ValueEnum, Clone, Copy, Default, Debug, Serialize, PartialEq)]
19#[serde(rename_all = "kebab-case")]
20pub enum TrafficPattern {
21 #[default]
23 AllToOne,
24
25 Random,
27
28 AllToAllFixed,
30
31 AllToAllSeq,
33
34 AllToAllRandom,
37}
38
39impl fmt::Display for TrafficPattern {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 write!(f, "{self:?}")
42 }
43}
44
45#[derive(EntityGet)]
51pub struct AccessGen {
52 entity: Rc<Entity>,
53 config: Rc<FabricConfig>,
54 source_index: usize,
55 dest_index: usize,
56 traffic_pattern: TrafficPattern,
57 overhead_size_bytes: usize,
58 payload_size_bytes: usize,
59 num_send_frames: usize,
60 num_sent_frames: usize,
61 rng: Xoshiro256PlusPlus,
62 next_dests: Vec<usize>,
63}
64
65impl AccessGen {
66 #[expect(clippy::too_many_arguments)]
67 #[must_use]
68 pub fn new(
69 parent: &Rc<Entity>,
70 config: Rc<FabricConfig>,
71 source_index: usize,
72 initial_dest_index: usize,
73 traffic_pattern: TrafficPattern,
74 overhead_size_bytes: usize,
75 payload_size_bytes: usize,
76 num_send_frames: usize,
77 seed: u64,
78 ) -> Self {
79 let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed ^ (source_index as u64));
81 let num_ports = config.num_ports();
82
83 let dest_index = match traffic_pattern {
84 TrafficPattern::Random => (0..num_ports).choose(&mut rng).unwrap(),
85 TrafficPattern::AllToOne
86 | TrafficPattern::AllToAllFixed
87 | TrafficPattern::AllToAllSeq
88 | TrafficPattern::AllToAllRandom => initial_dest_index,
89 };
90
91 let mut next_dests: Vec<usize> = (0..num_ports).collect();
92 next_dests.shuffle(&mut rng);
93
94 Self {
95 entity: Rc::new(Entity::new(parent, &format!("gen{source_index}"))),
96 config,
97 source_index,
98 dest_index,
99 traffic_pattern,
100 overhead_size_bytes,
101 payload_size_bytes,
102 num_send_frames,
103 num_sent_frames: 0,
104 rng,
105 next_dests,
106 }
107 }
108
109 #[must_use]
110 fn next_dest(&mut self) -> usize {
111 let num_ports = self.config.max_num_ports();
112 match self.traffic_pattern {
113 TrafficPattern::Random => (0..num_ports).choose(&mut self.rng).unwrap(),
114 TrafficPattern::AllToOne => self.dest_index,
115 TrafficPattern::AllToAllFixed => self.dest_index,
116 TrafficPattern::AllToAllSeq => (self.dest_index + 1) % num_ports,
117 TrafficPattern::AllToAllRandom => {
118 let dest = self.next_dests.pop().unwrap();
119 if self.next_dests.is_empty() {
120 self.next_dests = (0..num_ports).collect();
121 self.next_dests.shuffle(&mut self.rng);
122 }
123 dest
124 }
125 }
126 }
127}
128
129impl Iterator for AccessGen {
130 type Item = MemoryAccess;
131 fn next(&mut self) -> Option<Self::Item> {
132 let no_valid_accesses = (self.dest_index == self.source_index)
134 && (self.traffic_pattern == TrafficPattern::AllToOne
135 || self.traffic_pattern == TrafficPattern::AllToAllFixed);
136
137 if no_valid_accesses {
138 return None;
139 }
140
141 if self.num_sent_frames < self.num_send_frames {
142 while self.dest_index == self.source_index {
143 self.dest_index = self.next_dest();
144 }
145
146 let label = (self.num_sent_frames | (self.source_index << 32)) as u64;
147 self.num_sent_frames += 1;
148
149 let dest = self.config.port_indices()[self.dest_index];
152 let access = Some(MemoryAccess::new(
153 &self.entity,
154 AccessType::WriteRequest,
155 self.payload_size_bytes,
156 label,
157 self.source_index as u64,
158 DeviceId(dest as u64),
159 DeviceId(self.source_index as u64),
160 self.overhead_size_bytes,
161 ));
162
163 self.dest_index = self.next_dest();
164 access
165 } else {
166 None
167 }
168 }
169}