1use std::cell::{Cell, RefCell};
12use std::future::Future;
13use std::pin::Pin;
14use std::rc::{Rc, Weak};
15use std::task::{Context, Poll};
16
17use rustdv_gpi as gpi;
18use rustdv_gpi::{BigUint, LogicArray};
19
20use crate::{executor, triggers::TrigShared};
21
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23pub enum SimPhase {
24 Normal,
25 ReadWrite,
26 ReadOnly,
27}
28
29pub(crate) enum WriteVal {
30 Bool(bool),
31 U8(u8),
32 U16(u16),
33 U32(u32),
34 U64(u64),
35 U128(u128),
36 BigInt(BigUint),
37 Logic(LogicArray),
38 Real(f64),
39 String(String),
40}
41
42#[derive(Copy, Clone, PartialEq, Eq)]
43pub(crate) enum WriteTarget {
44 Logic(gpi::LogicHandle),
45 Real(gpi::RealHandle),
46 String(gpi::StringHandle),
47}
48impl WriteTarget {
49 fn full_name(self) -> String {
50 match self {
51 Self::Logic(h) => h.full_name(),
52 Self::Real(h) => h.full_name(),
53 Self::String(h) => h.full_name(),
54 }
55 }
56}
57impl From<gpi::LogicHandle> for WriteTarget {
58 fn from(h: gpi::LogicHandle) -> Self {
59 Self::Logic(h)
60 }
61}
62impl From<gpi::RealHandle> for WriteTarget {
63 fn from(h: gpi::RealHandle) -> Self {
64 Self::Real(h)
65 }
66}
67impl From<gpi::StringHandle> for WriteTarget {
68 fn from(h: gpi::StringHandle) -> Self {
69 Self::String(h)
70 }
71}
72
73struct Hub {
74 phase: Cell<SimPhase>,
75 rw_waiters: RefCell<Vec<Weak<TrigShared>>>,
76 rw_cb: RefCell<Option<gpi::CallbackHandle>>,
77 ro_waiters: RefCell<Vec<Weak<TrigShared>>>,
78 ro_cb: RefCell<Option<gpi::CallbackHandle>>,
79 nt_waiters: RefCell<Vec<Weak<TrigShared>>>,
80 nt_cb: RefCell<Option<gpi::CallbackHandle>>,
81 writes: RefCell<Vec<(WriteTarget, WriteVal)>>,
82}
83
84thread_local! {
85 static HUB: RefCell<Option<Rc<Hub>>> = const { RefCell::new(None) };
86}
87
88pub(crate) fn init_hub() {
89 HUB.with(|h| {
90 *h.borrow_mut() = Some(Rc::new(Hub {
91 phase: Cell::new(SimPhase::Normal),
92 rw_waiters: RefCell::new(Vec::new()),
93 rw_cb: RefCell::new(None),
94 ro_waiters: RefCell::new(Vec::new()),
95 ro_cb: RefCell::new(None),
96 nt_waiters: RefCell::new(Vec::new()),
97 nt_cb: RefCell::new(None),
98 writes: RefCell::new(Vec::new()),
99 }));
100 });
101}
102
103fn hub() -> Rc<Hub> {
104 HUB.with(|h| {
105 h.borrow()
106 .clone()
107 .expect("rustdv sim context not initialized")
108 })
109}
110
111pub fn current_phase() -> SimPhase {
112 hub().phase.get()
113}
114
115pub(crate) fn current_phase_if_initialized() -> Option<SimPhase> {
116 HUB.with(|hub| hub.borrow().as_ref().map(|hub| hub.phase.get()))
117}
118
119pub async fn leave_read_only() {
143 if hub().phase.get() != SimPhase::ReadOnly {
144 return;
145 }
146 crate::triggers::Timer::steps(1).await;
147}
148
149fn apply_write(target: WriteTarget, v: &WriteVal) {
154 match (target, v) {
155 (WriteTarget::Real(h), WriteVal::Real(x)) => {
156 h.set_now(*x);
157 return;
158 }
159 (WriteTarget::String(h), WriteVal::String(x)) => {
160 h.set_now(x).expect("scheduled string was validated");
161 return;
162 }
163 _ => {}
164 }
165 let WriteTarget::Logic(h) = target else {
166 unreachable!("write target/value mismatch")
167 };
168 match v {
169 WriteVal::Bool(x) => h.set_bool_now(*x),
170 WriteVal::U8(x) => h.set_u8_now(*x),
171 WriteVal::U16(x) => h.set_u16_now(*x),
172 WriteVal::U32(x) => h.set_u32_now(*x),
173 WriteVal::U64(x) => h.set_u64_now(*x),
174 WriteVal::U128(x) => h.set_u128_now(*x),
175 WriteVal::BigInt(x) => h.set_bigint_now(x),
176 WriteVal::Logic(x) => h.set_logic_now(x),
177 _ => unreachable!("write target/value mismatch"),
178 }
179}
180
181pub(crate) fn deny_write_in_read_only(h: impl Into<WriteTarget>) {
184 let h = h.into();
185 if hub().phase.get() == SimPhase::ReadOnly {
186 panic!(
187 "illegal write to '{}' during the ReadOnly phase (cocotb rule, design-doc §4.4)",
188 h.full_name()
189 );
190 }
191}
192
193pub(crate) fn schedule(h: impl Into<WriteTarget>, v: WriteVal) {
194 let h = h.into();
195 let hub = hub();
196 match hub.phase.get() {
197 SimPhase::ReadOnly => deny_write_in_read_only(h),
198 SimPhase::ReadWrite => apply_write(h, &v),
199 SimPhase::Normal => {
200 {
201 let mut writes = hub.writes.borrow_mut();
202 if let Some(slot) = writes.iter_mut().find(|(eh, _)| *eh == h) {
203 slot.1 = v; } else {
205 writes.push((h, v));
206 }
207 }
208 prime_rw(&hub);
209 }
210 }
211}
212
213fn fire_waiters(waiters: &RefCell<Vec<Weak<TrigShared>>>) {
221 for waiter in std::mem::take(&mut *waiters.borrow_mut()) {
222 if let Some(shared) = waiter.upgrade() {
223 shared.fire();
224 }
225 }
226}
227
228fn prime_rw(hub: &Rc<Hub>) {
229 if hub.rw_cb.borrow().is_some() {
230 return;
231 }
232 let h = hub.clone();
233 let cb = gpi::register_read_write(Box::new(move || {
234 h.rw_cb.borrow_mut().take(); h.phase.set(SimPhase::ReadWrite);
236 let writes: Vec<_> = h.writes.borrow_mut().drain(..).collect();
238 for (sig, val) in &writes {
239 apply_write(*sig, val);
240 }
241 fire_waiters(&h.rw_waiters);
242 executor::current().run_until_idle();
243 h.phase.set(SimPhase::Normal);
244 }));
245 *hub.rw_cb.borrow_mut() = Some(cb);
246}
247
248fn prime_ro(hub: &Rc<Hub>) {
249 if hub.ro_cb.borrow().is_some() {
250 return;
251 }
252 let h = hub.clone();
253 let cb = gpi::register_read_only(Box::new(move || {
254 h.ro_cb.borrow_mut().take();
255 h.phase.set(SimPhase::ReadOnly);
256 fire_waiters(&h.ro_waiters);
257 executor::current().run_until_idle();
258 h.phase.set(SimPhase::Normal);
259 }));
260 *hub.ro_cb.borrow_mut() = Some(cb);
261}
262
263fn prime_nt(hub: &Rc<Hub>) {
264 if hub.nt_cb.borrow().is_some() {
265 return;
266 }
267 let h = hub.clone();
268 let cb = gpi::register_next_sim_time(Box::new(move || {
269 h.nt_cb.borrow_mut().take();
270 fire_waiters(&h.nt_waiters);
271 executor::current().run_until_idle();
272 }));
273 *hub.nt_cb.borrow_mut() = Some(cb);
274}
275
276#[derive(Copy, Clone, PartialEq, Eq)]
281enum PhaseKind {
282 ReadWrite,
283 ReadOnly,
284 NextTimeStep,
285}
286
287pub struct PhaseFut {
288 kind: PhaseKind,
289 shared: Option<Rc<TrigShared>>,
290}
291
292impl Future for PhaseFut {
293 type Output = ();
294 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
295 if let Some(shared) = &self.shared {
296 if shared.fired() {
297 return Poll::Ready(());
298 }
299 shared.set_waker(cx.waker().clone());
300 return Poll::Pending;
301 }
302 let hub = hub();
303 let shared = TrigShared::new();
304 shared.set_waker(cx.waker().clone());
305 match self.kind {
306 PhaseKind::ReadWrite => {
307 if hub.phase.get() == SimPhase::ReadOnly {
308 panic!("awaiting ReadWrite from the ReadOnly phase is illegal (cocotb rule)");
309 }
310 hub.rw_waiters.borrow_mut().push(Rc::downgrade(&shared));
311 prime_rw(&hub);
312 }
313 PhaseKind::ReadOnly => {
314 if hub.phase.get() == SimPhase::ReadOnly {
315 panic!("awaiting ReadOnly from the ReadOnly phase is illegal (cocotb rule)");
316 }
317 hub.ro_waiters.borrow_mut().push(Rc::downgrade(&shared));
318 prime_ro(&hub);
319 }
320 PhaseKind::NextTimeStep => {
321 hub.nt_waiters.borrow_mut().push(Rc::downgrade(&shared));
322 prime_nt(&hub);
323 }
324 }
325 self.shared = Some(shared);
326 Poll::Pending
327 }
328}
329
330pub fn read_write() -> PhaseFut {
332 PhaseFut {
333 kind: PhaseKind::ReadWrite,
334 shared: None,
335 }
336}
337
338pub fn read_only() -> PhaseFut {
340 PhaseFut {
341 kind: PhaseKind::ReadOnly,
342 shared: None,
343 }
344}
345
346pub async fn service_read_only<F, R>(service: F) -> R
367where
368 F: FnOnce() -> R,
369{
370 read_only().await;
371 service()
372}
373
374pub fn next_time_step() -> PhaseFut {
376 PhaseFut {
377 kind: PhaseKind::NextTimeStep,
378 shared: None,
379 }
380}