1use std::cell::{Cell, RefCell};
7use std::future::Future;
8use std::pin::Pin;
9use std::rc::Rc;
10use std::task::{Context, Poll, Waker};
11
12use rustdv_gpi as gpi;
13
14use crate::executor;
15use crate::time::SimDuration;
16
17pub(crate) struct TrigShared {
19 fired: Cell<bool>,
20 waker: RefCell<Option<Waker>>,
21}
22
23impl TrigShared {
24 pub(crate) fn new() -> Rc<TrigShared> {
25 Rc::new(TrigShared {
26 fired: Cell::new(false),
27 waker: RefCell::new(None),
28 })
29 }
30 pub(crate) fn fire(&self) {
31 self.fired.set(true);
32 if let Some(w) = self.waker.borrow_mut().take() {
33 w.wake();
34 }
35 }
36 pub(crate) fn fired(&self) -> bool {
37 self.fired.get()
38 }
39 pub(crate) fn set_waker(&self, w: Waker) {
40 *self.waker.borrow_mut() = Some(w);
41 }
42}
43
44#[must_use = "triggers do nothing unless you .await them"]
51pub struct Timer {
52 steps: u64,
53 shared: Option<Rc<TrigShared>>,
54 _cb: Option<gpi::CallbackHandle>,
55}
56
57impl Timer {
58 pub fn new(d: SimDuration) -> Timer {
59 assert!(d.steps > 0, "Timer duration must be positive (cocotb rule)");
60 Timer {
61 steps: d.steps,
62 shared: None,
63 _cb: None,
64 }
65 }
66 pub fn steps(steps: u64) -> Timer {
67 Self::new(SimDuration::steps(steps))
68 }
69 pub fn ns(n: u64) -> Timer {
70 Self::new(SimDuration::ns(n))
71 }
72 pub fn us(n: u64) -> Timer {
73 Self::new(SimDuration::us(n))
74 }
75 pub fn ms(n: u64) -> Timer {
76 Self::new(SimDuration::ms(n))
77 }
78}
79
80impl Future for Timer {
81 type Output = ();
82 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
83 match &self.shared {
84 None => {
85 let sh = TrigShared::new();
86 sh.set_waker(cx.waker().clone());
87 let sh2 = sh.clone();
88 let cb = gpi::register_timer(
89 self.steps,
90 Box::new(move || {
91 sh2.fire();
92 executor::current().run_until_idle();
93 }),
94 );
95 self.shared = Some(sh);
96 self._cb = Some(cb);
97 Poll::Pending
98 }
99 Some(sh) => {
100 if sh.fired() {
101 Poll::Ready(())
102 } else {
103 sh.set_waker(cx.waker().clone());
104 Poll::Pending
105 }
106 }
107 }
108 }
109}
110
111#[derive(Copy, Clone, PartialEq, Eq)]
116pub(crate) enum EdgeKind {
117 Rising,
118 Falling,
119 AnyChange,
120}
121
122#[must_use = "triggers do nothing unless you .await them"]
125pub struct Edge {
126 sig: gpi::LogicHandle,
127 kind: EdgeKind,
128 shared: Option<Rc<TrigShared>>,
129 _cb: Option<gpi::CallbackHandle>,
130}
131
132impl Edge {
133 pub(crate) fn new(sig: gpi::LogicHandle, kind: EdgeKind) -> Edge {
134 Edge {
135 sig,
136 kind,
137 shared: None,
138 _cb: None,
139 }
140 }
141}
142
143impl Future for Edge {
144 type Output = ();
145 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
146 match &self.shared {
147 None => {
148 let sh = TrigShared::new();
149 sh.set_waker(cx.waker().clone());
150 let sh2 = sh.clone();
151 let sig = self.sig;
152 let kind = self.kind;
153 let cb = gpi::register_value_change(
154 sig,
155 Box::new(move || {
156 if sh2.fired() {
157 return; }
159 let hit = match kind {
160 EdgeKind::AnyChange => true,
161 EdgeKind::Rising => sig.size() == 1 && sig.get_u64() == Ok(1),
162 EdgeKind::Falling => sig.size() == 1 && sig.get_u64() == Ok(0),
163 };
164 if hit {
165 sh2.fire();
166 executor::current().run_until_idle();
167 }
168 }),
169 );
170 self.shared = Some(sh);
171 self._cb = Some(cb);
172 Poll::Pending
173 }
174 Some(sh) => {
175 if sh.fired() {
176 Poll::Ready(())
177 } else {
178 sh.set_waker(cx.waker().clone());
179 Poll::Pending
180 }
181 }
182 }
183 }
184}
185
186#[must_use = "triggers do nothing unless you .await them"]
193pub struct NullTrigger {
194 yielded: bool,
195}
196
197impl NullTrigger {
198 #[allow(clippy::new_without_default)]
199 pub fn new() -> NullTrigger {
200 NullTrigger { yielded: false }
201 }
202}
203
204impl Future for NullTrigger {
205 type Output = ();
206 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
207 if self.yielded {
208 Poll::Ready(())
209 } else {
210 self.yielded = true;
211 cx.waker().wake_by_ref();
212 Poll::Pending
213 }
214 }
215}