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 { fired: Cell::new(false), waker: RefCell::new(None) })
26 }
27 pub(crate) fn fire(&self) {
28 self.fired.set(true);
29 if let Some(w) = self.waker.borrow_mut().take() {
30 w.wake();
31 }
32 }
33 pub(crate) fn fired(&self) -> bool {
34 self.fired.get()
35 }
36 pub(crate) fn set_waker(&self, w: Waker) {
37 *self.waker.borrow_mut() = Some(w);
38 }
39}
40
41#[must_use = "triggers do nothing unless you .await them"]
48pub struct Timer {
49 steps: u64,
50 shared: Option<Rc<TrigShared>>,
51 _cb: Option<gpi::CallbackHandle>,
52}
53
54impl Timer {
55 pub fn new(d: SimDuration) -> Timer {
56 assert!(d.steps > 0, "Timer duration must be positive (cocotb rule)");
57 Timer { steps: d.steps, shared: None, _cb: None }
58 }
59 pub fn steps(steps: u64) -> Timer {
60 Self::new(SimDuration::steps(steps))
61 }
62 pub fn ns(n: u64) -> Timer {
63 Self::new(SimDuration::ns(n))
64 }
65 pub fn us(n: u64) -> Timer {
66 Self::new(SimDuration::us(n))
67 }
68 pub fn ms(n: u64) -> Timer {
69 Self::new(SimDuration::ms(n))
70 }
71}
72
73impl Future for Timer {
74 type Output = ();
75 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
76 match &self.shared {
77 None => {
78 let sh = TrigShared::new();
79 sh.set_waker(cx.waker().clone());
80 let sh2 = sh.clone();
81 let cb = gpi::register_timer(
82 self.steps,
83 Box::new(move || {
84 sh2.fire();
85 executor::current().run_until_idle();
86 }),
87 );
88 self.shared = Some(sh);
89 self._cb = Some(cb);
90 Poll::Pending
91 }
92 Some(sh) => {
93 if sh.fired() {
94 Poll::Ready(())
95 } else {
96 sh.set_waker(cx.waker().clone());
97 Poll::Pending
98 }
99 }
100 }
101 }
102}
103
104#[derive(Copy, Clone, PartialEq, Eq)]
109pub(crate) enum EdgeKind {
110 Rising,
111 Falling,
112 AnyChange,
113}
114
115#[must_use = "triggers do nothing unless you .await them"]
118pub struct Edge {
119 sig: gpi::LogicHandle,
120 kind: EdgeKind,
121 shared: Option<Rc<TrigShared>>,
122 _cb: Option<gpi::CallbackHandle>,
123}
124
125impl Edge {
126 pub(crate) fn new(sig: gpi::LogicHandle, kind: EdgeKind) -> Edge {
127 Edge { sig, kind, shared: None, _cb: None }
128 }
129}
130
131impl Future for Edge {
132 type Output = ();
133 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
134 match &self.shared {
135 None => {
136 let sh = TrigShared::new();
137 sh.set_waker(cx.waker().clone());
138 let sh2 = sh.clone();
139 let sig = self.sig;
140 let kind = self.kind;
141 let cb = gpi::register_value_change(
142 sig,
143 Box::new(move || {
144 if sh2.fired() {
145 return; }
147 let hit = match kind {
148 EdgeKind::AnyChange => true,
149 EdgeKind::Rising => sig.get_binstr() == "1",
150 EdgeKind::Falling => sig.get_binstr() == "0",
151 };
152 if hit {
153 sh2.fire();
154 executor::current().run_until_idle();
155 }
156 }),
157 );
158 self.shared = Some(sh);
159 self._cb = Some(cb);
160 Poll::Pending
161 }
162 Some(sh) => {
163 if sh.fired() {
164 Poll::Ready(())
165 } else {
166 sh.set_waker(cx.waker().clone());
167 Poll::Pending
168 }
169 }
170 }
171 }
172}
173
174#[must_use = "triggers do nothing unless you .await them"]
181pub struct NullTrigger {
182 yielded: bool,
183}
184
185impl NullTrigger {
186 #[allow(clippy::new_without_default)]
187 pub fn new() -> NullTrigger {
188 NullTrigger { yielded: false }
189 }
190}
191
192impl Future for NullTrigger {
193 type Output = ();
194 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
195 if self.yielded {
196 Poll::Ready(())
197 } else {
198 self.yielded = true;
199 cx.waker().wake_by_ref();
200 Poll::Pending
201 }
202 }
203}