1use std::sync::Arc;
4
5use celox_design::{DomainKind, StateAddr};
6use celox_runtime::backend::EventHandle;
7use celox_runtime::{DesignReflection, ReflectionSignal, SignalRef};
8use num_bigint::BigUint;
9
10use crate::{
11 NativeBackend, RuntimeEvent, RuntimeFormatContext, SharedNativeCode, SimBackend, SimulatorError,
12};
13
14use super::backend::NativeRuntimeSchema;
15use super::{NativeImageContainerError, NativeProgramImage};
16
17#[derive(Debug, thiserror::Error)]
19pub enum NativeProgramLoadError {
20 #[error(transparent)]
21 Container(#[from] NativeImageContainerError),
22 #[error("no native program image is attached to the runtime")]
23 MissingImage,
24 #[error("failed to attach native machine code: {0}")]
25 Attach(#[source] SimulatorError),
26 #[error("failed to initialize native program state: {0}")]
27 Initialize(#[source] celox_runtime::SimulatorErrorCode),
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32pub enum NativeSignalIdentity {
33 State(SignalRef),
35 Event(StateAddr),
37}
38
39pub struct NativeProgramInstance {
44 shared: Arc<SharedNativeCode>,
45 backend: NativeBackend,
46 runtime_event_read_seq: u64,
47 comb_observer_snapshots: Vec<Vec<(BigUint, BigUint)>>,
48 comb_observer_initial_eval: bool,
49 forced_values: crate::HashMap<NativeSignalIdentity, (BigUint, BigUint)>,
50}
51
52impl NativeProgramInstance {
53 pub unsafe fn from_image(image: NativeProgramImage) -> Result<Self, NativeProgramLoadError> {
60 let shared = Arc::new(
62 unsafe { SharedNativeCode::from_image(image) }
63 .map_err(NativeProgramLoadError::Attach)?,
64 );
65 let backend = NativeBackend::from_shared(Arc::clone(&shared));
66 let mut instance = Self {
67 shared,
68 backend,
69 runtime_event_read_seq: 0,
70 comb_observer_snapshots: Vec::new(),
71 comb_observer_initial_eval: true,
72 forced_values: crate::HashMap::default(),
73 };
74 instance.comb_observer_snapshots = instance.snapshot_all_comb_observers();
75 instance
76 .eval_comb_checked(RuntimeFormatContext::default())
77 .map_err(NativeProgramLoadError::Initialize)?;
78 Ok(instance)
79 }
80
81 pub unsafe fn from_attached_bytes(bytes: &[u8]) -> Result<Self, NativeProgramLoadError> {
88 let appended = NativeProgramImage::discover_appended(bytes)?
89 .ok_or(NativeProgramLoadError::MissingImage)?;
90 unsafe { Self::from_image(appended.image) }
92 }
93
94 pub unsafe fn from_current_executable() -> Result<Self, NativeProgramLoadError> {
100 let appended = NativeProgramImage::discover_in_current_executable()?
101 .ok_or(NativeProgramLoadError::MissingImage)?;
102 unsafe { Self::from_image(appended.image) }
104 }
105
106 pub fn reflection(&self) -> &DesignReflection {
108 self.shared.program_image().reflection()
109 }
110
111 pub fn signal(&self, full_name: &str) -> Option<&ReflectionSignal> {
113 self.reflection()
114 .signal_by_name(full_name)
115 .map(|(_, signal)| signal)
116 }
117
118 pub fn signal_ref(&self, full_name: &str) -> Option<SignalRef> {
120 self.signal(full_name).map(|signal| signal.signal)
121 }
122
123 pub fn signal_identity(
125 &self,
126 id: celox_runtime::ReflectionSignalId,
127 ) -> Option<NativeSignalIdentity> {
128 let signal = self.reflection().signal(id)?;
129 Some(if signal.domain_kind == DomainKind::Other {
130 NativeSignalIdentity::State(signal.signal)
131 } else {
132 NativeSignalIdentity::Event(
133 self.shared
134 .program_image()
135 .event_topology()
136 .canonical(signal.state_address),
137 )
138 })
139 }
140
141 pub fn eval_comb(&mut self) -> Result<(), celox_runtime::SimulatorErrorCode> {
143 self.eval_comb_checked(RuntimeFormatContext::default())
144 }
145
146 pub fn settle_active_edges(
152 &mut self,
153 active_edges: &[StateAddr],
154 ) -> Result<(), celox_runtime::SimulatorErrorCode> {
155 self.settle_active_edges_with_context(active_edges, RuntimeFormatContext::default())
156 }
157
158 pub fn settle_active_edges_with_context(
160 &mut self,
161 active_edges: &[StateAddr],
162 context: RuntimeFormatContext<'_>,
163 ) -> Result<(), celox_runtime::SimulatorErrorCode> {
164 let start_seq = (!self.runtime_schema().runtime_event_sites.is_empty())
165 .then(|| crate::simulator::runtime_event_write_seq_for_backend(&self.backend));
166 self.eval_comb_checked(context)?;
167 let mut seen = crate::HashSet::default();
168 let mut events = Vec::new();
169 for address in active_edges {
170 let canonical = self
171 .shared
172 .program_image()
173 .event_topology()
174 .canonical(*address);
175 let Some(event) = self.backend.resolve_event_opt(&canonical) else {
176 continue;
177 };
178 if seen.insert(event.id()) {
179 events.push(event);
180 }
181 }
182
183 if events.len() == 1 {
184 self.backend
185 .eval_apply_ff_at(events[0])
186 .map_err(|error| self.decorate_runtime_error_since(error, start_seq, context))?;
187 } else if !events.is_empty() {
188 let split = events
189 .iter()
190 .map(|event| {
191 Some((
192 self.backend.resolve_eval_only_event(&event.addr())?,
193 self.backend.resolve_apply_event(&event.addr())?,
194 ))
195 })
196 .collect::<Option<Vec<_>>>();
197 if let Some(split) = split {
198 for (evaluate, _) in &split {
199 self.backend.eval_only_ff_at(*evaluate).map_err(|error| {
200 self.decorate_runtime_error_since(error, start_seq, context)
201 })?;
202 }
203 for (_, apply) in &split {
204 self.backend.apply_ff_at(*apply).map_err(|error| {
205 self.decorate_runtime_error_since(error, start_seq, context)
206 })?;
207 }
208 } else {
209 for event in events {
210 self.backend.eval_apply_ff_at(event).map_err(|error| {
211 self.decorate_runtime_error_since(error, start_seq, context)
212 })?;
213 }
214 }
215 }
216 self.eval_comb_checked(context)?;
217 if let Some(start_seq) = start_seq {
218 self.check_fatal_events_since(start_seq, context)?;
219 }
220 Ok(())
221 }
222
223 pub fn drain_runtime_events(&mut self) -> Vec<RuntimeEvent> {
226 self.drain_runtime_events_with_context(RuntimeFormatContext::default())
227 }
228
229 pub fn drain_runtime_events_with_context(
232 &mut self,
233 context: RuntimeFormatContext<'_>,
234 ) -> Vec<RuntimeEvent> {
235 crate::simulator::collect_runtime_events_for_backend(
236 &self.backend,
237 &self
238 .shared
239 .program_image()
240 .runtime_schema()
241 .runtime_event_sites,
242 &mut self.runtime_event_read_seq,
243 context,
244 )
245 }
246
247 pub fn backend(&self) -> &NativeBackend {
250 &self.backend
251 }
252
253 pub fn backend_mut(&mut self) -> &mut NativeBackend {
254 &mut self.backend
255 }
256
257 pub fn force_signal(
260 &mut self,
261 id: celox_runtime::ReflectionSignalId,
262 value: BigUint,
263 mask: BigUint,
264 ) -> bool {
265 if !self.shared.supports_forces() {
266 return false;
267 }
268 let Some(identity) = self.signal_identity(id) else {
269 return false;
270 };
271 let aliases = self.signal_refs_for_identity(identity);
272 for signal in aliases {
273 self.backend
274 .set_four_state(signal, value.clone(), mask.clone());
275 }
276 self.forced_values.insert(identity, (value, mask));
277 true
278 }
279
280 pub fn release_signal(&mut self, id: celox_runtime::ReflectionSignalId) {
282 if let Some(identity) = self.signal_identity(id) {
283 self.forced_values.remove(&identity);
284 }
285 }
286
287 fn signal_refs_for_identity(&self, identity: NativeSignalIdentity) -> Vec<SignalRef> {
288 let mut signals = self
289 .reflection()
290 .signals()
291 .iter()
292 .enumerate()
293 .filter_map(|(index, signal)| {
294 (self.signal_identity(celox_runtime::ReflectionSignalId(index as u32))
295 == Some(identity))
296 .then_some(signal.signal)
297 })
298 .collect::<Vec<_>>();
299 signals.sort_unstable();
300 signals.dedup();
301 signals
302 }
303
304 fn runtime_schema(&self) -> &NativeRuntimeSchema {
305 self.shared.program_image().runtime_schema()
306 }
307
308 fn decorate_runtime_error(
309 &self,
310 error: celox_runtime::SimulatorErrorCode,
311 ) -> celox_runtime::SimulatorErrorCode {
312 let celox_runtime::SimulatorErrorCode::DetectedTrueLoopCode(code) = error else {
313 return error;
314 };
315 let Some(info) = self.runtime_schema().runtime_errors.get(&code) else {
316 return celox_runtime::SimulatorErrorCode::DetectedTrueLoop;
317 };
318 let signals = info
319 .signals
320 .iter()
321 .filter_map(|address| {
322 self.reflection()
323 .signals()
324 .iter()
325 .find(|signal| signal.state_address == *address)
326 .map(|signal| signal.full_name.clone())
327 })
328 .collect::<Vec<_>>();
329 if info.message == "Detected True Loop" {
330 celox_runtime::SimulatorErrorCode::DetectedTrueLoopAt { signals }
331 } else {
332 celox_runtime::SimulatorErrorCode::Runtime {
333 message: info.message.clone(),
334 signals,
335 }
336 }
337 }
338
339 fn decorate_runtime_error_since(
340 &self,
341 error: celox_runtime::SimulatorErrorCode,
342 start_seq: Option<u64>,
343 context: RuntimeFormatContext<'_>,
344 ) -> celox_runtime::SimulatorErrorCode {
345 if let Some(start_seq) = start_seq
346 && let Err(runtime_event_error) = self.check_fatal_events_since(start_seq, context)
347 {
348 return runtime_event_error;
349 }
350 self.decorate_runtime_error(error)
351 }
352
353 fn eval_comb_checked(
354 &mut self,
355 context: RuntimeFormatContext<'_>,
356 ) -> Result<(), celox_runtime::SimulatorErrorCode> {
357 if self.runtime_schema().runtime_event_sites.is_empty() {
358 return self.eval_comb_backend();
359 }
360
361 let start_seq = crate::simulator::runtime_event_write_seq_for_backend(&self.backend);
362 if self.runtime_schema().comb_observers.is_empty() {
363 let result = self.eval_comb_backend();
364 self.check_fatal_events_since(start_seq, context)?;
365 return result;
366 }
367
368 let before = self.snapshot_all_comb_observers();
369 let active_before = before
370 .iter()
371 .zip(&self.comb_observer_snapshots)
372 .map(|(current, previous)| current != previous)
373 .collect::<Vec<_>>();
374 let mut active_sites = vec![false; self.runtime_schema().runtime_event_sites.len()];
375 for (observer, active) in self
376 .runtime_schema()
377 .comb_observers
378 .iter()
379 .zip(active_before)
380 {
381 if active || self.comb_observer_initial_eval {
382 for group_observer in &self.runtime_schema().comb_observers {
383 if group_observer.activation_group == observer.activation_group {
384 active_sites[group_observer.site_id as usize] = true;
385 }
386 }
387 }
388 }
389 self.backend.set_comb_capture_event_enabled(&active_sites);
390 let result = self.eval_comb_backend();
391 let after = self.snapshot_all_comb_observers();
392 self.backend.set_comb_capture_event_enabled(&vec![
393 false;
394 self.runtime_schema()
395 .runtime_event_sites
396 .len()
397 ]);
398 self.comb_observer_snapshots = after;
399 self.comb_observer_initial_eval = false;
400 self.check_fatal_events_since(start_seq, context)?;
401 result
402 }
403
404 fn eval_comb_backend(&mut self) -> Result<(), celox_runtime::SimulatorErrorCode> {
405 if self.forced_values.is_empty() {
406 return self
407 .backend
408 .eval_comb()
409 .map_err(|error| self.decorate_runtime_error(error));
410 }
411 let overrides = self
412 .forced_values
413 .iter()
414 .flat_map(|(identity, (value, mask))| {
415 self.signal_refs_for_identity(*identity)
416 .into_iter()
417 .map(|signal| (signal, value.clone(), mask.clone()))
418 })
419 .collect::<Vec<_>>();
420 for (signal, value, mask) in &overrides {
421 self.backend
422 .set_four_state(*signal, value.clone(), mask.clone());
423 }
424 self.backend
425 .eval_comb_units_with(|backend| {
426 for (signal, value, mask) in &overrides {
427 backend.set_four_state(*signal, value.clone(), mask.clone());
428 }
429 })
430 .map_err(|error| self.decorate_runtime_error(error))
431 }
432
433 fn check_fatal_events_since(
434 &self,
435 start_seq: u64,
436 context: RuntimeFormatContext<'_>,
437 ) -> Result<(), celox_runtime::SimulatorErrorCode> {
438 let mut read_seq = start_seq;
439 let events = crate::simulator::collect_runtime_events_for_backend(
440 &self.backend,
441 &self.runtime_schema().runtime_event_sites,
442 &mut read_seq,
443 context,
444 );
445 if let Some(message) = events.into_iter().find_map(|event| match event {
446 RuntimeEvent::AssertFatal { message } => Some(message),
447 RuntimeEvent::Missed { count } => Some(format!(
448 "missed {count} runtime events; a fatal assertion may have been overwritten"
449 )),
450 RuntimeEvent::Display { .. }
451 | RuntimeEvent::Write { .. }
452 | RuntimeEvent::AssertContinue { .. } => None,
453 }) {
454 return Err(celox_runtime::SimulatorErrorCode::Runtime {
455 message,
456 signals: Vec::new(),
457 });
458 }
459 Ok(())
460 }
461
462 fn snapshot_all_comb_observers(&self) -> Vec<Vec<(BigUint, BigUint)>> {
463 self.runtime_schema()
464 .comb_observers
465 .iter()
466 .map(|observer| {
467 observer
468 .sensitivity
469 .iter()
470 .map(|atom| {
471 let signal = self.backend.resolve_signal(&atom.id);
472 let (value, mask) = if signal.is_4state {
473 self.backend.get_four_state(signal)
474 } else {
475 (self.backend.get(signal), BigUint::default())
476 };
477 (
478 slice_biguint(&value, atom.access.lsb, atom.access.msb),
479 slice_biguint(&mask, atom.access.lsb, atom.access.msb),
480 )
481 })
482 .collect()
483 })
484 .collect()
485 }
486}
487
488fn slice_biguint(value: &BigUint, least: usize, most: usize) -> BigUint {
489 if most < least {
490 return BigUint::default();
491 }
492 let width = most - least + 1;
493 (value >> least) & ((BigUint::from(1u8) << width) - BigUint::from(1u8))
494}