1use std::cell::{Cell, RefCell};
12use std::future::Future;
13use std::pin::Pin;
14use std::rc::Rc;
15use std::task::{Context, Poll, Waker};
16
17use rustdv_gpi as gpi;
18use rustdv_gpi::LogicArray;
19
20use crate::executor;
21
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23pub enum SimPhase {
24 Normal,
25 ReadWrite,
26 ReadOnly,
27}
28
29enum WriteVal {
30 U64(u64),
31 Arr(LogicArray),
32}
33
34struct Hub {
35 phase: Cell<SimPhase>,
36 rw_waiters: RefCell<Vec<Waker>>,
37 rw_cb: RefCell<Option<gpi::CallbackHandle>>,
38 ro_waiters: RefCell<Vec<Waker>>,
39 ro_cb: RefCell<Option<gpi::CallbackHandle>>,
40 nt_waiters: RefCell<Vec<Waker>>,
41 nt_cb: RefCell<Option<gpi::CallbackHandle>>,
42 writes: RefCell<Vec<(gpi::LogicHandle, WriteVal)>>,
43}
44
45thread_local! {
46 static HUB: RefCell<Option<Rc<Hub>>> = const { RefCell::new(None) };
47}
48
49pub(crate) fn init_hub() {
50 HUB.with(|h| {
51 *h.borrow_mut() = Some(Rc::new(Hub {
52 phase: Cell::new(SimPhase::Normal),
53 rw_waiters: RefCell::new(Vec::new()),
54 rw_cb: RefCell::new(None),
55 ro_waiters: RefCell::new(Vec::new()),
56 ro_cb: RefCell::new(None),
57 nt_waiters: RefCell::new(Vec::new()),
58 nt_cb: RefCell::new(None),
59 writes: RefCell::new(Vec::new()),
60 }));
61 });
62}
63
64fn hub() -> Rc<Hub> {
65 HUB.with(|h| h.borrow().clone().expect("rustdv sim context not initialized"))
66}
67
68pub fn current_phase() -> SimPhase {
69 hub().phase.get()
70}
71
72pub async fn leave_read_only() {
96 if hub().phase.get() != SimPhase::ReadOnly {
97 return;
98 }
99 crate::triggers::Timer::steps(1).await;
100}
101
102fn apply_write(h: gpi::LogicHandle, v: &WriteVal) {
107 match v {
108 WriteVal::U64(x) => h.set_u64_now(*x),
109 WriteVal::Arr(a) => h.set_now(a),
110 }
111}
112
113pub(crate) fn deny_write_in_read_only(h: gpi::LogicHandle) {
116 if hub().phase.get() == SimPhase::ReadOnly {
117 panic!(
118 "illegal write to '{}' during the ReadOnly phase (cocotb rule, design-doc §4.4)",
119 h.full_name()
120 );
121 }
122}
123
124fn schedule(h: gpi::LogicHandle, v: WriteVal) {
125 let hub = hub();
126 match hub.phase.get() {
127 SimPhase::ReadOnly => deny_write_in_read_only(h),
128 SimPhase::ReadWrite => apply_write(h, &v),
129 SimPhase::Normal => {
130 {
131 let mut writes = hub.writes.borrow_mut();
132 if let Some(slot) = writes.iter_mut().find(|(eh, _)| *eh == h) {
133 slot.1 = v; } else {
135 writes.push((h, v));
136 }
137 }
138 prime_rw(&hub);
139 }
140 }
141}
142
143pub(crate) fn schedule_write_u64(h: gpi::LogicHandle, v: u64) {
144 schedule(h, WriteVal::U64(v));
145}
146
147pub(crate) fn schedule_write_arr(h: gpi::LogicHandle, v: LogicArray) {
148 schedule(h, WriteVal::Arr(v));
149}
150
151fn prime_rw(hub: &Rc<Hub>) {
156 if hub.rw_cb.borrow().is_some() {
157 return;
158 }
159 let h = hub.clone();
160 let cb = gpi::register_read_write(Box::new(move || {
161 h.rw_cb.borrow_mut().take(); h.phase.set(SimPhase::ReadWrite);
163 let writes: Vec<_> = h.writes.borrow_mut().drain(..).collect();
165 for (sig, val) in &writes {
166 apply_write(*sig, val);
167 }
168 for w in h.rw_waiters.borrow_mut().drain(..) {
169 w.wake();
170 }
171 executor::current().run_until_idle();
172 h.phase.set(SimPhase::Normal);
173 }));
174 *hub.rw_cb.borrow_mut() = Some(cb);
175}
176
177fn prime_ro(hub: &Rc<Hub>) {
178 if hub.ro_cb.borrow().is_some() {
179 return;
180 }
181 let h = hub.clone();
182 let cb = gpi::register_read_only(Box::new(move || {
183 h.ro_cb.borrow_mut().take();
184 h.phase.set(SimPhase::ReadOnly);
185 for w in h.ro_waiters.borrow_mut().drain(..) {
186 w.wake();
187 }
188 executor::current().run_until_idle();
189 h.phase.set(SimPhase::Normal);
190 }));
191 *hub.ro_cb.borrow_mut() = Some(cb);
192}
193
194fn prime_nt(hub: &Rc<Hub>) {
195 if hub.nt_cb.borrow().is_some() {
196 return;
197 }
198 let h = hub.clone();
199 let cb = gpi::register_next_sim_time(Box::new(move || {
200 h.nt_cb.borrow_mut().take();
201 for w in h.nt_waiters.borrow_mut().drain(..) {
202 w.wake();
203 }
204 executor::current().run_until_idle();
205 }));
206 *hub.nt_cb.borrow_mut() = Some(cb);
207}
208
209#[derive(Copy, Clone, PartialEq, Eq)]
214enum PhaseKind {
215 ReadWrite,
216 ReadOnly,
217 NextTimeStep,
218}
219
220pub struct PhaseFut {
221 kind: PhaseKind,
222 registered: bool,
223}
224
225impl Future for PhaseFut {
226 type Output = ();
227 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
228 if self.registered {
229 return Poll::Ready(());
230 }
231 let hub = hub();
232 match self.kind {
233 PhaseKind::ReadWrite => {
234 if hub.phase.get() == SimPhase::ReadOnly {
235 panic!("awaiting ReadWrite from the ReadOnly phase is illegal (cocotb rule)");
236 }
237 hub.rw_waiters.borrow_mut().push(cx.waker().clone());
238 prime_rw(&hub);
239 }
240 PhaseKind::ReadOnly => {
241 if hub.phase.get() == SimPhase::ReadOnly {
242 panic!("awaiting ReadOnly from the ReadOnly phase is illegal (cocotb rule)");
243 }
244 hub.ro_waiters.borrow_mut().push(cx.waker().clone());
245 prime_ro(&hub);
246 }
247 PhaseKind::NextTimeStep => {
248 hub.nt_waiters.borrow_mut().push(cx.waker().clone());
249 prime_nt(&hub);
250 }
251 }
252 self.registered = true;
253 Poll::Pending
254 }
255}
256
257pub fn read_write() -> PhaseFut {
259 PhaseFut { kind: PhaseKind::ReadWrite, registered: false }
260}
261
262pub fn read_only() -> PhaseFut {
264 PhaseFut { kind: PhaseKind::ReadOnly, registered: false }
265}
266
267pub fn next_time_step() -> PhaseFut {
269 PhaseFut { kind: PhaseKind::NextTimeStep, registered: false }
270}