Skip to main content

gwr_components/
connect.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! Helper connection macros.
4//!
5//! Use these macros for component topology wiring instead of manually passing
6//! [`PortStateResult`](gwr_engine::port::PortStateResult) values around. They
7//! encode the repository's port naming convention, support indexed port arrays,
8//! and fail early when the expected `port_*` or `connect_port_*` method does
9//! not exist.
10
11#[doc(hidden)]
12pub use paste::paste;
13
14/// Connect an [OutPort](gwr_engine::port::OutPort) port to an
15/// [InPort](gwr_engine::port::InPort)
16#[macro_export]
17macro_rules! connect_port {
18    ($from:expr, $from_port_name:ident => $to:expr, $to_port_name:ident) => {
19        {
20            use gwr_track::entity::GetEntity;
21            gwr_track::debug!($from.entity() ; "Connect {}.{} => {}.{}", $from, stringify!($from_port_name), $to, stringify!($to_port_name));
22            $crate::connect::paste! {
23                $from.[< connect_port_ $from_port_name >]($to.[< port_ $to_port_name >]())
24            }
25        }
26    };
27    ($from:expr, $from_port_name:ident, $from_index:expr => $to:expr, $to_port_name:ident) => {
28        {
29            use gwr_track::entity::GetEntity;
30            let from_index: usize = $from_index;
31            gwr_track::debug!($from.entity() ; "Connect {}.{}[{}] => {}.{}", $from, stringify!($from_port_name), from_index, $to, stringify!($to_port_name));
32            $crate::connect::paste! {
33                $from.[< connect_port_ $from_port_name _i >](from_index, $to.[< port_ $to_port_name >]())
34            }
35        }
36    };
37    ($from:expr, $from_port_name:ident => $to:expr, $to_port_name:ident, $to_index:expr) => {
38        {
39            use gwr_track::entity::GetEntity;
40            let to_index: usize = $to_index;
41            gwr_track::debug!($from.entity() ; "Connect {}.{} => {}.{}[{}]", $from, stringify!($from_port_name), $to, stringify!($to_port_name), to_index);
42            $crate::connect::paste! {
43                $from.[< connect_port_ $from_port_name >]($to.[< port_ $to_port_name _i >](to_index))
44            }
45        }
46    };
47    ($from:expr, $from_port_name:ident, $from_index:expr => $to:expr, $to_port_name:ident, $to_index:expr) => {
48        {
49            use gwr_track::entity::GetEntity;
50            let from_index: usize = $from_index;
51            let to_index: usize = $to_index;
52            gwr_track::debug!($from.entity() ; "Connect {}.{}[{}] => {}.{}[{}]", $from, stringify!($from_port_name), from_index, $to, stringify!($to_port_name), to_index);
53            $crate::connect::paste! {
54                $from.[< connect_port_ $from_port_name _i >](from_index, $to.[< port_ $to_port_name _i >](to_index))
55            }
56        }
57    };
58}
59
60/// Create and connect a dummy RX port.
61///
62/// Use this when an output port is deliberately left unconnected in a
63/// particular topology. The dummy endpoint records that choice and marks the
64/// output as connected, preventing a runtime "not connected" error. It does not
65/// consume data: the output must never call `put` or `try_put`, because no
66/// receiver will call `get` and the simulation may stall or terminate with
67/// unfinished work.
68#[macro_export]
69macro_rules! connect_dummy_rx {
70    ($from:expr, $from_port_name:ident => $engine:expr, $clock:expr, $entity:expr) => {
71        {
72            use gwr_track::entity::GetEntity;
73            let rx_port = gwr_engine::port::InPort::new($engine, $clock, $entity, "dummy");
74
75            gwr_track::debug!($from.entity() ; "Connect {}.{} => {}", $from, stringify!($from_port_name), rx_port);
76            $crate::connect::paste! {
77                $from.[< connect_port_ $from_port_name >](rx_port.state())
78            }
79        }
80    };
81    ($from:expr, $from_port_name:ident, $from_index:expr => $engine:expr, $clock:expr, $entity:expr) => {
82        {
83            use gwr_track::entity::GetEntity;
84            let rx_port = gwr_engine::port::InPort::new($engine, $clock, $entity, "dummy");
85
86            let from_index: usize = $from_index;
87            gwr_track::debug!($from.entity() ; "Connect {}.{}[{}] => {}", $from, stringify!($from_port_name), from_index, rx_port);
88            $crate::connect::paste! {
89                $from.[< connect_port_ $from_port_name _i >](from_index, rx_port.state())
90            }
91        }
92    };
93}
94
95/// Create and connect a dummy TX port.
96///
97/// Use this when an input port is deliberately left unconnected in a particular
98/// topology. The dummy endpoint records that choice and marks the input as
99/// connected, preventing a runtime "not connected" error. It does not produce
100/// data: the input must never call `get`, because no transmitter will provide a
101/// value and the simulation may stall or terminate with unfinished work.
102#[macro_export]
103macro_rules! connect_dummy_tx {
104    ($entity:expr => $to:expr, $to_port_name:ident) => {
105        {
106            let mut tx_port = gwr_engine::port::OutPort::new($entity, "dummy");
107
108            gwr_track::debug!($entity ; "Connect {} => {}.{}", tx_port, $to, stringify!($to_port_name));
109            $crate::connect::paste! {
110                tx_port.connect($to.[< port_ $to_port_name >]())
111            }
112        }
113    };
114    ($entity:expr => $to:expr, $to_port_name:ident, $to_index:expr) => {
115        {
116            let mut tx_port = gwr_engine::port::OutPort::new($entity, "dummy");
117
118            let to_index: usize = $to_index;
119            gwr_track::debug!($entity ; "Connect {} => {}.{}[{}]", tx_port, $to, stringify!($to_port_name), to_index);
120            $crate::connect::paste! {
121                tx_port.connect($to.[< port_ $to_port_name _i >](to_index))
122            }
123        }
124    };
125}
126
127/// Connect a tx port for a subcomponent.
128///
129/// The subcomponent is expected to be stored in a `RefCell<Option<>>`
130#[macro_export]
131macro_rules! connect_tx {
132    ($component:expr, $fn:ident ; $port_state:ident) => {
133        $crate::connect::paste! {
134            $component
135                .borrow_mut()
136                .as_mut()
137                .unwrap()
138                .$fn($port_state)
139        }
140    };
141}
142
143/// Connect a tx port for a subcomponent where the port is one of an array.
144///
145/// The subcomponent is expected to be stored in a `RefCell<Option<>>`
146#[macro_export]
147macro_rules! connect_tx_i {
148    ($component:expr, $fn:ident, $index:expr ; $port_state:ident) => {
149        $component
150            .borrow_mut()
151            .as_mut()
152            .unwrap()
153            .$fn($index, $port_state)
154    };
155}
156
157/// Access rx port for a subcomponent.
158///
159/// The subcomponent is expected to be stored in a `RefCell<Option<>>`
160#[macro_export]
161macro_rules! port_rx {
162    ($component:expr, $fn:ident) => {
163        $component.borrow().as_ref().unwrap().$fn()
164    };
165}
166
167/// Access an individual index of an rx port array for a subcomponent.
168///
169/// The subcomponent is expected to be stored in a `RefCell<Option<>>`
170#[macro_export]
171macro_rules! port_rx_i {
172    ($component:expr, $fn:ident, $index:expr) => {
173        $component.borrow().as_ref().unwrap().$fn($index)
174    };
175}
176
177/// Get a reference to a variable stored in a `RefCell<Option<>>`.
178#[macro_export]
179macro_rules! borrow_option {
180    ($var:expr) => {
181        $var.borrow().as_ref().unwrap()
182    };
183}
184
185/// Get a mutable reference to a variable stored in a `RefCell<Option<>>`.
186#[macro_export]
187macro_rules! borrow_option_mut {
188    ($var:expr) => {
189        $var.borrow_mut().as_mut().unwrap()
190    };
191}
192
193/// Take a variable out of a `RefCell<Option<>>`.
194#[macro_export]
195macro_rules! take_option {
196    ($var:expr) => {
197        $var.borrow_mut().take().unwrap()
198    };
199}