use bit_set::BitSet;
use celox_design::DomainKind;
use fxhash::FxHashMap;
use crate::{
SignalRef, SimulatorErrorCode,
backend::{EventHandle, SimBackend},
scheduler::{ClockDef, Scheduler, SimEvent},
};
pub trait SimulationExecutor {
type Backend: SimBackend;
fn backend(&self) -> &Self::Backend;
fn backend_mut(&mut self) -> &mut Self::Backend;
fn eval_comb(&mut self) -> Result<(), SimulatorErrorCode>;
fn eval_apply_ff_at(
&mut self,
event: <Self::Backend as SimBackend>::Event,
) -> Result<(), SimulatorErrorCode>;
fn eval_only_ff_at(
&mut self,
event: <Self::Backend as SimBackend>::Event,
) -> Result<(), SimulatorErrorCode>;
fn apply_ff_at(
&mut self,
event: <Self::Backend as SimBackend>::Event,
) -> Result<(), SimulatorErrorCode>;
fn stage_external_event(
&mut self,
_event: <Self::Backend as SimBackend>::Event,
_timestamp: u64,
) -> Result<(), SimulatorErrorCode> {
Ok(())
}
fn fire_external_event(
&mut self,
_event: <Self::Backend as SimBackend>::Event,
_timestamp: u64,
) -> Result<(), SimulatorErrorCode> {
Ok(())
}
fn finish_timed_step(&mut self, _timestamp: u64) {}
}
pub struct EventInfo<B: SimBackend> {
pub canonical_id: usize,
pub is_cascaded: bool,
pub eval_ff_event: Option<B::Event>,
pub eval_only_event: Option<B::Event>,
pub apply_event: Option<B::Event>,
}
impl<B: SimBackend> Clone for EventInfo<B> {
fn clone(&self) -> Self {
*self
}
}
impl<B: SimBackend> Copy for EventInfo<B> {}
impl<B: SimBackend> std::fmt::Debug for EventInfo<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventInfo")
.field("canonical_id", &self.canonical_id)
.field("is_cascaded", &self.is_cascaded)
.field("eval_ff_event", &self.eval_ff_event)
.field("eval_only_event", &self.eval_only_event)
.field("apply_event", &self.apply_event)
.finish()
}
}
pub struct SimulationState<B: SimBackend> {
scheduler: Scheduler<B>,
last_clock_values: BitSet,
topo_signals: Vec<(SignalRef, usize, usize)>,
domain_kinds: Vec<Option<DomainKind>>,
event_info: Vec<EventInfo<B>>,
signal_to_id: FxHashMap<SignalRef, usize>,
}
impl<B: SimBackend> SimulationState<B> {
pub fn synchronize_event_values(&mut self, backend: &B) {
self.last_clock_values.make_empty();
for (signal, id, _) in &self.topo_signals {
if *id == usize::MAX {
continue;
}
let value: u8 = backend.get_as(*signal);
if value != 0 {
self.last_clock_values.insert(*id);
}
}
}
fn replace_triggers_with_stable_edges(&self, backend: &mut B) {
backend.clear_triggered_bits();
for (signal, id, _) in &self.topo_signals {
if *id == usize::MAX {
continue;
}
let was_nonzero = self.last_clock_values.contains(*id);
let value: u8 = backend.get_as(*signal);
let is_nonzero = value != 0;
let triggered = match self.domain_kinds[*id] {
Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
!was_nonzero && is_nonzero
}
Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
was_nonzero && !is_nonzero
}
_ => was_nonzero != is_nonzero,
};
if triggered {
backend.mark_triggered_bit(*id);
}
}
}
pub fn new(
backend: &B,
topo_signals: Vec<(SignalRef, usize, usize)>,
domain_kinds: Vec<Option<DomainKind>>,
event_info: Vec<EventInfo<B>>,
) -> Self {
let mut last_clock_values = BitSet::with_capacity(backend.num_events());
let mut signal_to_id = FxHashMap::default();
for (signal, id, _) in topo_signals.iter().copied() {
if id == usize::MAX {
continue;
}
signal_to_id.insert(signal, id);
let value: u8 = backend.get_as(signal);
if value != 0 {
last_clock_values.insert(id);
}
}
Self {
scheduler: Scheduler::new(),
last_clock_values,
topo_signals,
domain_kinds,
event_info,
signal_to_id,
}
}
pub fn add_clock(
&mut self,
event: B::Event,
signal: SignalRef,
period: u64,
initial_delay: u64,
) {
let event_id = event.id();
if event_id >= self.scheduler.clocks.len() {
self.scheduler.clocks.resize(event_id + 1, None);
}
self.scheduler.clocks[event_id] = Some(ClockDef { period });
self.scheduler.push(SimEvent {
time: initial_delay,
event_ref: event,
signal,
next_val: 1,
});
}
pub fn schedule(&mut self, event: B::Event, signal: SignalRef, time: u64, value: u8) {
self.scheduler.push(SimEvent {
time,
event_ref: event,
signal,
next_val: value,
});
}
pub fn step<E>(&mut self, executor: &mut E) -> Result<Option<u64>, SimulatorErrorCode>
where
E: SimulationExecutor<Backend = B>,
{
let (current_time, events_to_process) = match self.scheduler.pop_all_at_next_time() {
Some(events) => events,
None => return Ok(None),
};
self.scheduler.time = current_time;
let num_events = executor.backend().num_events();
for event in &events_to_process {
executor.backend_mut().set(event.signal, event.next_val);
}
let mut triggered_domains = BitSet::with_capacity(num_events);
let mut discovered_in_this_step = BitSet::with_capacity(num_events);
let mut scheduled_trigger_ids = BitSet::with_capacity(num_events);
let mut has_scheduled_event_signal = false;
executor.backend_mut().clear_triggered_bits();
for event in &events_to_process {
if let Some(&id) = self.signal_to_id.get(&event.signal) {
has_scheduled_event_signal = true;
let was_nonzero = self.last_clock_values.contains(id);
let is_nonzero = event.next_val != 0;
let triggered = match self.domain_kinds[id] {
Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
!was_nonzero && is_nonzero
}
Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
was_nonzero && !is_nonzero
}
_ => !was_nonzero && is_nonzero,
};
if triggered {
scheduled_trigger_ids.insert(id);
executor.backend_mut().mark_triggered_bit(id);
}
}
}
executor.eval_comb()?;
if has_scheduled_event_signal {
if scheduled_trigger_ids.is_empty() {
self.replace_triggers_with_stable_edges(executor.backend_mut());
} else {
executor.backend_mut().clear_triggered_bits();
for id in scheduled_trigger_ids.iter() {
executor.backend_mut().mark_triggered_bit(id);
}
}
}
let mut comb_already_done = false;
loop {
let mut any_new_outer_loop_trigger = false;
let mut newly_triggered = Vec::new();
loop {
let mut any_new_sequential_trigger = false;
let marked_bits = executor.backend().get_triggered_bits();
executor.backend_mut().clear_triggered_bits();
let mut can_use_eval_apply =
triggered_domains.is_empty() && marked_bits.count() == 1;
if can_use_eval_apply {
let single_id = marked_bits.iter().next().expect("one marked trigger");
let info = self.event_info[single_id];
can_use_eval_apply = !info.is_cascaded;
if can_use_eval_apply {
if let Some(event) = info.eval_ff_event {
discovered_in_this_step.insert(single_id);
triggered_domains.insert(info.canonical_id);
any_new_outer_loop_trigger = true;
executor.stage_external_event(event, current_time)?;
executor.eval_apply_ff_at(event)?;
executor.fire_external_event(event, current_time)?;
executor.eval_comb()?;
if has_scheduled_event_signal {
self.replace_triggers_with_stable_edges(executor.backend_mut());
}
comb_already_done = true;
break;
}
}
}
for id in marked_bits.iter() {
if discovered_in_this_step.contains(id) {
continue;
}
discovered_in_this_step.insert(id);
let info = self.event_info[id];
if triggered_domains.contains(info.canonical_id) {
continue;
}
triggered_domains.insert(info.canonical_id);
any_new_sequential_trigger = true;
newly_triggered.push(info.canonical_id);
if let Some(event) = info.eval_only_event {
executor.stage_external_event(
info.eval_ff_event.unwrap_or(event),
current_time,
)?;
executor.eval_only_ff_at(event)?;
} else if let Some(event) = info.eval_ff_event {
executor.stage_external_event(event, current_time)?;
executor.eval_apply_ff_at(event)?;
} else {
unreachable!(
"FF trigger discovered without a corresponding execution unit"
);
}
}
if !any_new_sequential_trigger {
break;
}
}
if newly_triggered.is_empty() && !any_new_outer_loop_trigger {
break;
}
for id in &newly_triggered {
if let Some(event) = self.event_info[*id].apply_event {
executor.apply_ff_at(event)?;
}
}
for id in &newly_triggered {
if let Some(event) = self.event_info[*id].eval_ff_event {
executor.fire_external_event(event, current_time)?;
}
}
if comb_already_done {
comb_already_done = false;
} else {
executor.eval_comb()?;
if has_scheduled_event_signal {
self.replace_triggers_with_stable_edges(executor.backend_mut());
}
}
}
for (signal, id, _) in &self.topo_signals {
if *id == usize::MAX {
continue;
}
let value: u8 = executor.backend().get_as(*signal);
if value != 0 {
self.last_clock_values.insert(*id);
} else {
self.last_clock_values.remove(*id);
}
}
for event in &events_to_process {
let event_id = event.event_ref.id();
if let Some(Some(clock)) = self.scheduler.clocks.get(event_id) {
self.scheduler.push(SimEvent {
time: current_time + clock.period / 2,
event_ref: event.event_ref,
signal: event.signal,
next_val: 1 - event.next_val,
});
}
}
executor.finish_timed_step(current_time);
Ok(Some(current_time))
}
pub fn time(&self) -> u64 {
self.scheduler.time
}
pub fn set_time(&mut self, time: u64) {
self.scheduler.time = time;
}
pub fn next_event_time(&self) -> Option<u64> {
self.scheduler.next_event_time()
}
}