avr_tester/
components.rs

1mod component_controller;
2mod component_handle;
3mod component_runtime;
4mod component_state;
5mod futures;
6
7use self::component_controller::*;
8use crate::*;
9use std::future::Future;
10
11pub use self::component_handle::*;
12pub(crate) use self::component_runtime::*;
13pub use self::component_state::*;
14pub(crate) use self::futures::*;
15
16/// Manages components.
17///
18/// # Abstract
19///
20/// Components are _peripherals_ attached to the AVR - they allow to easily
21/// simulate external devices, such as shift registers or screens, without
22/// forcing you to think about those devices' timings with respect to other
23/// attached peripherals.
24///
25/// For instance, let's say that we've got a firmware that provides some UART
26/// functionality, but at the same time it requires for `PB1` and `PB2` to be
27/// toggled in regular intervals (say, because they are attached to watchdog).
28///
29/// Assuming `PB1` has to be toggled each 5 ms and `PB2` each 15 ms, we could
30/// write a test such as this:
31///
32/// ```no_run
33/// # use avr_tester::*;
34/// #
35/// let mut avr = AvrTester::test();
36///
37/// avr.uart0().write([0x01, 0x02, 0x03]);
38///
39/// for cycle in 0.. {
40///     // Keep the watchdog happy:
41///     if cycle % 5 == 0 {
42///         avr.pins().pb1().toggle();
43///     }
44///
45///     if cycle % 15 == 0 {
46///         avr.pins().pb2().toggle();
47///     }
48///
49///     // Check if the response has arrived:
50///     if let Some(response) = avr.uart0().try_read_byte() {
51///         assert_eq!(0x06, response);
52///         break;
53///     }
54///
55///     avr.run_for_ms(1);
56/// }
57/// ```
58///
59/// ... but that approach not only scales poorly (imagine having to handle
60/// multiple devices, each with its own clock!), but also obfuscates the test -
61/// if we're mostly interested in the UART part, then there shouldn't be any
62/// reason to intertwine it with the pin-toggling.
63///
64/// Here come components - they are like background tasks that are polled after
65/// each AVR's instruction:
66///
67/// ```no_run
68/// # use avr_tester::*;
69/// #
70/// let mut avr = AvrTester::test();
71///
72/// // Start the `PB1` toggler:
73/// avr.components().add(async {
74///     loop {
75///         avr_rt().pins().pb1().toggle();
76///         avr_rt().run_for_ms(5).await;
77///     }
78/// });
79///
80/// // Start the `PB2` toggler:
81/// avr.components().add(async {
82///     loop {
83///         avr_rt().pins().pb2().toggle();
84///         avr_rt().run_for_ms(15).await;
85///     }
86/// });
87///
88/// // Perform the test:
89/// avr.uart0().write([0x01, 0x02, 0x03]);
90/// avr.run_for_ms(100);
91/// assert_eq!(Some(0x06), avr.uart0().try_read_byte());
92/// ```
93///
94/// Components are handy, because AvrTester automatically takes care of
95/// their scheduling - we don't have to worry about `PB1` and `PB2`'s timings
96/// anymore: we just say "PB1 must be toggled every 5 ms", "PB2 must be toggled
97/// every 15 ms" and that's it.
98///
99/// From AvrTester's perspective, what happens here is basically:
100///
101/// ```text
102/// fn run_for_ms(ms):
103///     /* run() in a loop */
104///
105/// fn run():
106///     simavr.run_one_instruction()
107///
108///     for component in components:
109///         component.poll(simavr)
110/// ```
111///
112/// # Writing components
113///
114/// Writing components doesn't differ that much from writing regular tests - the
115/// most important caveat is that components must be asynchronous, so that
116/// AvrTester knows when a component has finished its "clock cycle".
117///
118/// This means that inside components you can't access regular [`AvrTester`] -
119/// you have to call [`avr_rt()`], which returns [`AvrTesterAsync`] with its
120/// own set of functions that operate on pins.
121///
122/// Similarly, instead of calling `thread::sleep()` you should invoke
123/// `avr_rt().run_for_ms(...)`.
124///
125/// # Examples
126///
127/// ## `PB2 = !PB1`
128///
129/// This component implements a simple `PB2 = !PB1` gate:
130///
131/// ```no_run
132/// # use avr_tester::*;
133/// #
134/// let mut avr = AvrTester::test();
135///
136/// avr.components().add(async {
137///     loop {
138///         let is_high = avr_rt().pins().pb1().is_high();
139///
140///         avr_rt().pins().pb2().set(!is_high);
141///         avr_rt().run().await;
142///     }
143/// });
144/// ```
145#[derive(Debug)]
146pub struct Components {
147    components: Vec<ComponentController>,
148}
149
150impl Components {
151    pub(crate) fn new() -> Self {
152        Self {
153            components: Default::default(),
154        }
155    }
156
157    /// Creates a new component and attaches it into the AVR.
158    ///
159    /// See [`Components`] for more details.
160    pub fn add(
161        &mut self,
162        component: impl Future<Output = ()> + 'static,
163    ) -> ComponentHandle {
164        let (controller, handle) = ComponentController::new(component);
165
166        self.components.push(controller);
167
168        handle
169    }
170
171    pub(crate) fn run(
172        &mut self,
173        sim: &mut Option<AvrSimulator>,
174        clock_frequency: u32,
175        tt: AvrDuration,
176    ) {
177        if self.components.is_empty() {
178            return;
179        }
180
181        ComponentRuntime::setup(sim.take().unwrap(), clock_frequency, tt);
182
183        // ---
184
185        let mut components_to_remove = Vec::new();
186
187        for (component_idx, component) in self.components.iter_mut().enumerate()
188        {
189            match component.run() {
190                ComponentControllerResult::KeepComponent => {
191                    //
192                }
193                ComponentControllerResult::RemoveComponent => {
194                    components_to_remove.push(component_idx);
195                }
196            }
197        }
198
199        for (removed_components, component_idx) in
200            components_to_remove.into_iter().enumerate()
201        {
202            self.components.remove(component_idx - removed_components);
203        }
204
205        // ---
206
207        *sim = Some(ComponentRuntime::destroy());
208    }
209}