1use crossbeam::channel::{bounded, Receiver, Sender};
2use crossbeam::channel::{RecvError, SendError};
3
4use crate::block::Block;
5use crate::check_error::{check_all, CheckError};
6use crate::vcd_probe::{write_vcd_change, write_vcd_dump, write_vcd_header};
7use std::io::Write;
8use std::thread::JoinHandle;
9
10pub fn simulate<B: Block>(uut: &mut B, max_iters: usize) -> bool {
20 for _ in 0..max_iters {
21 uut.update_all();
22 if !uut.has_changed() {
23 return true;
24 }
25 }
26 false
27}
28
29#[derive(Clone, Debug, PartialEq)]
30pub enum SimError {
32 SimTerminated,
34 MaxTimeReached,
36 SimHalted,
38 FailedToConverge,
40 Check(CheckError),
42 SimPanic,
44}
45
46impl From<CheckError> for SimError {
47 fn from(x: CheckError) -> Self {
48 SimError::Check(x)
49 }
50}
51
52impl From<RecvError> for SimError {
53 fn from(_x: RecvError) -> Self {
54 SimError::SimTerminated
55 }
56}
57
58impl<T> From<SendError<T>> for SimError {
59 fn from(_x: SendError<T>) -> Self {
60 SimError::SimTerminated
61 }
62}
63
64pub type Result<T> = std::result::Result<T, SimError>;
66
67enum TriggerType<T> {
68 Never,
69 Time(u64),
70 Function(Box<dyn Fn(&T) -> bool + Send>),
71 Clock(u64),
72 Halt,
73}
74
75struct Message<T> {
76 kind: TriggerType<T>,
77 circuit: Box<T>,
78}
79
80enum MessageOrPanic<T> {
81 Message(Message<T>),
82 Panic,
83}
84
85struct Worker<T> {
86 id: usize,
87 channel_to_worker: Sender<Message<T>>,
88 kind: TriggerType<T>,
89}
90
91pub type CustomLogicFn<T> = Box<dyn Fn(&mut T) -> ()>;
95
96pub struct Simulation<T> {
99 workers: Vec<Worker<T>>,
100 recv: Receiver<MessageOrPanic<T>>,
101 channel_to_sim: Sender<MessageOrPanic<T>>,
102 time: u64,
103 testbenches: Vec<JoinHandle<Result<()>>>,
104 custom_logic: Vec<CustomLogicFn<T>>,
105}
106
107pub struct Sim<T> {
111 time: u64,
112 to_sim: Sender<MessageOrPanic<T>>,
113 from_sim: Receiver<Message<T>>,
114}
115
116struct NextTime {
117 time: u64,
118 idx: usize,
119 clocks_only: bool,
120 halted: bool,
121}
122
123impl<T: Send + 'static + Block> Default for Simulation<T> {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl<T: Send + 'static + Block> Simulation<T> {
130 pub fn new() -> Simulation<T> {
132 let (send, recv) = bounded(0);
133 Self {
134 workers: vec![],
135 recv,
136 channel_to_sim: send,
137 time: 0,
138 testbenches: vec![],
139 custom_logic: vec![],
140 }
141 }
142 pub fn add_clock<F>(&mut self, interval: u64, clock_fn: F)
170 where
171 F: Fn(&mut Box<T>) -> () + Send + 'static + std::panic::RefUnwindSafe,
172 {
173 self.add_testbench(move |mut ep: Sim<T>| {
174 let mut x = ep.init()?;
175 loop {
176 x = ep.clock(interval, x)?;
177 clock_fn(&mut x);
178 }
179 });
180 }
181 pub fn add_phased_clock<F>(&mut self, interval: u64, phase_delay: u64, clock_fn: F)
215 where
216 F: Fn(&mut Box<T>) -> () + Send + 'static + std::panic::RefUnwindSafe,
217 {
218 self.add_testbench(move |mut ep: Sim<T>| {
219 let mut x = ep.init()?;
220 x = ep.wait(phase_delay, x)?;
221 loop {
222 x = ep.clock(interval, x)?;
223 clock_fn(&mut x);
224 }
225 });
226 }
227 pub fn add_testbench<F>(&mut self, testbench: F)
238 where
239 F: Fn(Sim<T>) -> Result<()> + Send + 'static + std::panic::RefUnwindSafe,
240 {
241 let ep = self.endpoint();
242 self.testbenches.push(std::thread::spawn(move || {
243 let ep_panic = ep.to_sim.clone();
244 let result = std::panic::catch_unwind(|| testbench(ep));
245 match result {
246 Ok(x) => x,
247 Err(_e) => {
248 ep_panic.send(MessageOrPanic::Panic).unwrap();
249 Err(SimError::SimPanic)
250 }
251 }
252 }));
253 }
254 pub fn add_custom_logic<F>(&mut self, logic: F)
255 where
256 F: Fn(&mut T) -> () + 'static,
257 {
258 self.custom_logic.push(Box::new(logic));
259 }
260 pub fn endpoint(&mut self) -> Sim<T> {
261 let (send_to_worker, recv_from_sim_to_worker) = bounded(0);
262 let id = self.workers.len();
263 let worker = Worker {
264 id,
265 channel_to_worker: send_to_worker,
266 kind: TriggerType::Never,
267 };
268 self.workers.push(worker);
269 Sim {
270 to_sim: self.channel_to_sim.clone(),
271 from_sim: recv_from_sim_to_worker,
272 time: 0,
273 }
274 }
275 fn dispatch(&mut self, idx: usize, x: Box<T>) -> Result<Box<T>> {
276 let worker = &mut self.workers[idx];
277 worker.channel_to_worker.send(Message {
278 kind: TriggerType::Time(self.time),
279 circuit: x,
280 })?;
281 let x = self.recv.recv()?;
282 let mut x = match x {
283 MessageOrPanic::Message(x) => x,
284 MessageOrPanic::Panic => {
285 return Err(SimError::SimPanic);
286 }
287 };
288 worker.kind = x.kind;
289 let mut converged = false;
291 for _ in 0..100 {
292 for l in &self.custom_logic {
293 l(&mut x.circuit);
294 }
295 x.circuit.update_all();
296 if !x.circuit.has_changed() {
297 converged = true;
298 break;
299 }
300 }
301 if !converged {
302 Err(SimError::FailedToConverge)
303 } else {
304 Ok(x.circuit)
305 }
306 }
307 fn scan_workers(&self, x: &T) -> NextTime {
308 let mut min_time = !0_u64;
309 let mut min_idx = 0;
310 let mut only_clock_waiters = true;
311 for worker in self.workers.iter() {
312 match &worker.kind {
313 TriggerType::Halt => {
314 return NextTime {
315 halted: true,
316 time: !0,
317 idx: !0,
318 clocks_only: false,
319 }
320 }
321 TriggerType::Never => {}
322 TriggerType::Time(t) => {
323 only_clock_waiters = false;
324 if *t < min_time {
325 min_time = *t;
326 min_idx = worker.id;
327 }
328 }
329 TriggerType::Function(watch) => {
330 only_clock_waiters = false;
331 if watch(&x) {
332 min_idx = worker.id;
333 min_time = self.time;
334 break;
335 }
336 }
337 TriggerType::Clock(t) => {
338 if *t < min_time {
339 min_time = *t;
340 min_idx = worker.id;
341 }
342 }
343 }
344 }
345 NextTime {
346 time: min_time,
347 idx: min_idx,
348 clocks_only: only_clock_waiters,
349 halted: false,
350 }
351 }
352 fn terminate(&mut self) {
353 self.workers.clear();
354 for handle in std::mem::take(&mut self.testbenches) {
355 let _ = handle.join().unwrap();
356 }
357 }
358 pub fn run(&mut self, mut x: Box<T>, max_time: u64) -> Result<()> {
359 x.as_mut().connect_all();
360 check_all(x.as_mut())?;
361 for id in 0..self.workers.len() {
363 x = self.dispatch(id, x)?;
364 }
365 let mut halted = false;
367 while self.time < max_time {
368 let next = self.scan_workers(&x);
369 if next.time == !0 || next.clocks_only || next.halted {
370 halted = next.halted;
371 break;
372 }
373 self.time = next.time;
374 x = self.dispatch(next.idx, x)?;
375 }
376 self.terminate();
377 if self.time >= max_time {
378 return Err(SimError::MaxTimeReached);
379 }
380 if halted {
381 return Err(SimError::SimHalted);
382 }
383 Ok(())
384 }
385 pub fn run_to_file(&mut self, x: Box<T>, max_time: u64, name: &str) -> Result<()> {
386 let mut vcd = vec![];
387 let result = self.run_traced(x, max_time, &mut vcd);
388 std::fs::write(name, vcd).unwrap();
389 result
390 }
391 pub fn run_traced<W: Write>(&mut self, mut x: Box<T>, max_time: u64, trace: W) -> Result<()> {
392 x.as_mut().connect_all();
393 check_all(x.as_mut())?;
394 let mut vcd = write_vcd_header(trace, x.as_ref());
395 for id in 0..self.workers.len() {
397 x = self.dispatch(id, x)?;
398 }
399 vcd = write_vcd_dump(vcd, x.as_ref());
400 let mut halted = false;
401 while self.time < max_time {
403 let next = self.scan_workers(x.as_ref());
404 if next.time == !0 || next.clocks_only || next.halted {
405 halted = next.halted;
406 break;
407 }
408 self.time = next.time;
409 x = self.dispatch(next.idx, x)?;
410 vcd.timestamp(next.time).unwrap();
411 vcd = write_vcd_change(vcd, x.as_ref());
412 }
413 self.terminate();
414 if self.time >= max_time {
415 return Err(SimError::MaxTimeReached);
416 }
417 if halted {
418 return Err(SimError::SimHalted);
419 }
420 Ok(())
421 }
422}
423
424pub mod sim_time {
425 pub const ONE_PICOSECOND: u64 = 1;
426 pub const ONE_NANOSECOND: u64 = 1000 * ONE_PICOSECOND;
427 pub const ONE_MICROSECOND: u64 = 1000 * ONE_NANOSECOND;
428 pub const ONE_MILLISECOND: u64 = 1000 * ONE_MICROSECOND;
429 pub const ONE_SEC: u64 = 1000 * ONE_MILLISECOND;
430}
431
432impl<T> Sim<T> {
433 pub fn init(&self) -> Result<Box<T>> {
434 Ok(self.from_sim.recv()?.circuit)
435 }
436 pub fn watch<S>(&mut self, check: S, x: Box<T>) -> Result<Box<T>>
437 where
438 S: Fn(&T) -> bool + Send + 'static,
439 {
440 self.to_sim.send(MessageOrPanic::Message(Message {
441 kind: TriggerType::Function(Box::new(check)),
442 circuit: x,
443 }))?;
444 let t = self.from_sim.recv()?;
445 if let TriggerType::Time(t0) = t.kind {
446 self.time = t0;
447 }
448 Ok(t.circuit)
449 }
450 pub fn clock(&mut self, delta: u64, x: Box<T>) -> Result<Box<T>> {
451 self.to_sim.send(MessageOrPanic::Message(Message {
452 kind: TriggerType::Clock(delta + self.time),
453 circuit: x,
454 }))?;
455 let t = self.from_sim.recv()?;
456 if let TriggerType::Time(t0) = t.kind {
457 self.time = t0;
458 }
459 Ok(t.circuit)
460 }
461 pub fn wait(&mut self, delta: u64, x: Box<T>) -> Result<Box<T>> {
462 self.to_sim.send(MessageOrPanic::Message(Message {
463 kind: TriggerType::Time(delta + self.time),
464 circuit: x,
465 }))?;
466 let t = self.from_sim.recv()?;
467 if let TriggerType::Time(t0) = t.kind {
468 self.time = t0;
469 }
470 Ok(t.circuit)
471 }
472 pub fn done(&self, x: Box<T>) -> Result<()> {
473 self.to_sim.send(MessageOrPanic::Message(Message {
474 kind: TriggerType::Never,
475 circuit: x,
476 }))?;
477 Ok(())
478 }
479 pub fn halt(&self, x: Box<T>) -> Result<()> {
480 self.to_sim.send(MessageOrPanic::Message(Message {
481 kind: TriggerType::Halt,
482 circuit: x,
483 }))?;
484 Err(SimError::SimHalted)
485 }
486 pub fn time(&self) -> u64 {
487 self.time
488 }
489}
490
491#[macro_export]
492macro_rules! wait_clock_true {
493 ($sim: ident, $($clock: ident).+, $me: expr) => {
494 $me = $sim.watch(|x| x.$($clock).+.val().clk, $me)?
495 };
496}
497
498#[macro_export]
499macro_rules! wait_clock_false {
500 ($sim: ident, $($clock: ident).+, $me: expr) => {
501 $me = $sim.watch(|x| !x.$($clock).+.val().clk, $me)?
502 };
503}
504
505#[macro_export]
506macro_rules! wait_clock_cycle {
507 ($sim: ident, $($clock: ident).+, $me: expr) => {
508 if $me.$($clock).+.val().clk {
509 wait_clock_false!($sim, $($clock).+, $me);
510 wait_clock_true!($sim, $($clock).+, $me);
511 } else {
512 wait_clock_true!($sim, $($clock).+, $me);
513 wait_clock_false!($sim, $($clock).+, $me);
514 }
515 };
516 ($sim: ident, $clock: ident, $me: expr, $count: expr) => {
517 for _i in 0..$count {
518 wait_clock_cycle!($sim, $clock, $me);
519 }
520 };
521}
522
523#[macro_export]
524macro_rules! wait_clock_cycles {
525 ($sim: ident, $($clock: ident).+, $me: expr, $count: expr) => {
526 for _i in 0..$count {
527 wait_clock_cycle!($sim, $($clock).+, $me);
528 }
529 };
530}
531
532#[macro_export]
533macro_rules! sim_assert {
534 ($sim: ident, $test: expr, $circuit: ident) => {
535 if !($test) {
536 println!("HALT {}", stringify!($test));
537 return $sim.halt($circuit);
538 }
539 };
540}
541
542#[macro_export]
543macro_rules! sim_assert_eq {
544 ($sim: ident, $lhs: expr, $rhs: expr, $circuit: ident) => {
545 if !($lhs == $rhs) {
546 println!(
547 "HALT {} != {}, {:?} != {:?}",
548 stringify!($lhs),
549 stringify!($rhs),
550 $lhs,
551 $rhs
552 );
553 return $sim.halt($circuit);
554 }
555 };
556}
557
558#[macro_export]
559macro_rules! simple_sim {
560 ($kind: ty, $($clock: ident).+, $clock_speed_hz: expr, $fixture: ident, $testbench: expr) => {
561 {
562 let mut sim = Simulation::new();
563 let half_period = 1_000_000_000_000 / (2 * $clock_speed_hz);
564 sim.add_clock(half_period, |x: &mut Box<$kind>| x.$($clock).+.next = !x.$($clock).+.val());
565 sim.add_testbench(move |mut $fixture: Sim<$kind>| {
566 $testbench
567 });
568 sim
569 }
570 }
571}
572
573pub const SIMULATION_TIME_ONE_SECOND: u64 = 1_000_000_000_000;