1use num_bigint::BigUint;
8use wasmtime::{Engine, Linker, Memory, Module, Store, TypedFunc};
9
10use crate::{
11 HashMap, SimulatorOptions,
12 backend::{
13 MemoryLayout, SimulatorErrorCode, get_byte_size,
14 memory_layout::{
15 STATE_HEADER_COMB_CAPTURE_ENABLED_ADDR_OFFSET, STATE_HEADER_RUNTIME_EVENT_ADDR_OFFSET,
16 },
17 },
18 ir::{AbsoluteAddr, SignalRef},
19};
20
21use super::wasm_codegen;
22
23fn wasm_codegen_error(stage: &'static str, source: wasmtime::Error) -> crate::SimulatorError {
24 crate::CodegenError::Wasm { stage, source }.into()
25}
26
27#[derive(Clone, Copy)]
29pub struct WasmEventRef {
30 instance_idx: usize,
32 pub addr: AbsoluteAddr,
33 pub id: usize,
34}
35
36impl std::fmt::Debug for WasmEventRef {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("WasmEventRef")
39 .field("instance_idx", &self.instance_idx)
40 .field("addr", &self.addr)
41 .field("id", &self.id)
42 .finish()
43 }
44}
45
46impl super::traits::EventHandle for WasmEventRef {
47 fn id(&self) -> usize {
48 self.id
49 }
50 fn addr(&self) -> AbsoluteAddr {
51 self.addr
52 }
53}
54
55impl super::traits::SimBackend for WasmBackend {
56 type Event = WasmEventRef;
57
58 fn eval_comb(&mut self) -> Result<(), super::SimulatorErrorCode> {
59 WasmBackend::eval_comb(self)
60 }
61 fn eval_apply_ff_at(&mut self, event: WasmEventRef) -> Result<(), super::SimulatorErrorCode> {
62 WasmBackend::eval_apply_ff_at(self, &event)
63 }
64 fn eval_only_ff_at(&mut self, event: WasmEventRef) -> Result<(), super::SimulatorErrorCode> {
65 WasmBackend::eval_only_ff_at(self, &event)
66 }
67 fn apply_ff_at(&mut self, event: WasmEventRef) -> Result<(), super::SimulatorErrorCode> {
68 WasmBackend::apply_ff_at(self, &event)
69 }
70 fn resolve_signal(&self, addr: &AbsoluteAddr) -> SignalRef {
71 WasmBackend::resolve_signal(self, addr)
72 }
73 fn resolve_event(&self, addr: &AbsoluteAddr) -> WasmEventRef {
74 WasmBackend::resolve_event(self, addr)
75 }
76 fn resolve_event_opt(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
77 WasmBackend::resolve_event_opt(self, addr)
78 }
79 fn resolve_eval_only_event(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
80 WasmBackend::resolve_eval_only_event(self, addr)
81 }
82 fn resolve_apply_event(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
83 WasmBackend::resolve_apply_event(self, addr)
84 }
85 fn set<T: Copy>(&mut self, signal: SignalRef, value: T) {
86 WasmBackend::set(self, signal, value)
87 }
88 fn set_wide(&mut self, signal: SignalRef, value: BigUint) {
89 WasmBackend::set_wide(self, signal, value)
90 }
91 fn set_four_state(&mut self, signal: SignalRef, value: BigUint, mask: BigUint) {
92 WasmBackend::set_four_state(self, signal, value, mask)
93 }
94 fn get(&self, signal: SignalRef) -> BigUint {
95 WasmBackend::get(self, signal)
96 }
97 fn get_as<T: Default + Copy>(&self, signal: SignalRef) -> T {
98 WasmBackend::get_as(self, signal)
99 }
100 fn get_four_state(&self, signal: SignalRef) -> (BigUint, BigUint) {
101 WasmBackend::get_four_state(self, signal)
102 }
103 fn memory_as_ptr(&self) -> (*const u8, usize) {
104 WasmBackend::memory_as_ptr(self)
105 }
106 fn memory_as_mut_ptr(&mut self) -> (*mut u8, usize) {
107 WasmBackend::memory_as_mut_ptr(self)
108 }
109 fn vcd_tracking_enabled(&self) -> bool {
110 !self.raw_view_exposed
111 }
112 fn runtime_event_buffer_as_ptr(&self) -> (*const u8, usize) {
113 WasmBackend::runtime_event_buffer_as_ptr(self)
114 }
115 fn set_comb_capture_event_enabled(&mut self, active_sites: &[bool]) {
116 WasmBackend::set_comb_capture_event_enabled(self, active_sites)
117 }
118 fn stable_region_size(&self) -> usize {
119 WasmBackend::stable_region_size(self)
120 }
121 fn layout(&self) -> &super::MemoryLayout {
122 WasmBackend::layout(self)
123 }
124 fn id_to_addr_slice(&self) -> &[AbsoluteAddr] {
125 WasmBackend::id_to_addr_slice(self)
126 }
127 fn id_to_event_slice(&self) -> &[WasmEventRef] {
128 WasmBackend::id_to_event_slice(self)
129 }
130 fn num_events(&self) -> usize {
131 WasmBackend::num_events(self)
132 }
133 fn clear_triggered_bits(&mut self) {
134 WasmBackend::clear_triggered_bits(self)
135 }
136 fn mark_triggered_bit(&mut self, id: usize) {
137 WasmBackend::mark_triggered_bit(self, id)
138 }
139 fn get_triggered_bits(&self) -> bit_set::BitSet {
140 WasmBackend::get_triggered_bits(self)
141 }
142}
143
144pub struct WasmBackend {
146 store: Store<()>,
147 memory: Memory,
148 raw_view_exposed: bool,
149 comb_func: TypedFunc<(), i64>,
150 event_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>>,
151 eval_only_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>>,
152 apply_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>>,
153 event_map: HashMap<AbsoluteAddr, WasmEventRef>,
154 eval_only_event_map: HashMap<AbsoluteAddr, WasmEventRef>,
155 apply_event_map: HashMap<AbsoluteAddr, WasmEventRef>,
156 id_to_addr: Vec<AbsoluteAddr>,
157 id_to_event: Vec<WasmEventRef>,
158 layout: MemoryLayout,
159 options: SimulatorOptions,
160}
161
162impl WasmBackend {
163 pub fn new(
164 laid_out: &crate::ir::LaidOutProgram,
165 options: &SimulatorOptions,
166 ) -> Result<Self, crate::SimulatorError> {
167 let engine = Engine::default();
168 let sir = laid_out;
169 let layout = laid_out.layout().clone();
170
171 let comb_wasm = wasm_codegen::compile_units(
173 &sir.sir.eval_comb,
174 &layout,
175 options.four_state,
176 options.emit_triggers,
177 );
178 let comb_module = Module::new(&engine, &comb_wasm.bytes)
179 .map_err(|source| wasm_codegen_error("eval_comb compilation", source))?;
180
181 let mut event_modules = Vec::new();
183 let mut eval_only_modules = Vec::new();
184 let mut apply_modules = Vec::new();
185 let mut event_map = HashMap::default();
186 let mut eval_only_event_map = HashMap::default();
187 let mut apply_event_map = HashMap::default();
188 let mut id_to_addr = Vec::new();
189 let mut addr_to_id = HashMap::default();
190 let mut next_id = 0usize;
191
192 let compile_ffs = |ff_map: &HashMap<
193 AbsoluteAddr,
194 Vec<crate::ir::ExecutionUnit<crate::ir::RegionedAbsoluteAddr>>,
195 >,
196 modules: &mut Vec<(AbsoluteAddr, Module)>,
197 emap: &mut HashMap<AbsoluteAddr, WasmEventRef>,
198 addr_to_id: &mut HashMap<AbsoluteAddr, usize>,
199 next_id: &mut usize,
200 id_to_addr: &mut Vec<AbsoluteAddr>|
201 -> Result<(), crate::SimulatorError> {
202 for (clock, units) in ff_map {
203 let canonical = sir.design.events.canonical(*clock);
204 let id = *addr_to_id.entry(canonical).or_insert_with(|| {
205 let id = *next_id;
206 *next_id += 1;
207 id_to_addr.push(canonical);
208 id
209 });
210
211 let wasm = wasm_codegen::compile_units(
212 units,
213 &layout,
214 options.four_state,
215 options.emit_triggers,
216 );
217 let module = Module::new(&engine, &wasm.bytes)
218 .map_err(|source| wasm_codegen_error("FF compilation", source))?;
219 let idx = modules.len();
220 modules.push((canonical, module));
221 emap.insert(
222 canonical,
223 WasmEventRef {
224 instance_idx: idx,
225 addr: canonical,
226 id,
227 },
228 );
229 }
230 Ok(())
231 };
232
233 compile_ffs(
234 &sir.sir.eval_apply_ffs,
235 &mut event_modules,
236 &mut event_map,
237 &mut addr_to_id,
238 &mut next_id,
239 &mut id_to_addr,
240 )?;
241 compile_ffs(
242 &sir.sir.eval_only_ffs,
243 &mut eval_only_modules,
244 &mut eval_only_event_map,
245 &mut addr_to_id,
246 &mut next_id,
247 &mut id_to_addr,
248 )?;
249 compile_ffs(
250 &sir.sir.apply_ffs,
251 &mut apply_modules,
252 &mut apply_event_map,
253 &mut addr_to_id,
254 &mut next_id,
255 &mut id_to_addr,
256 )?;
257
258 for (alias, canonical) in &sir.design.events.aliases {
260 if let Some(ev) = event_map.get(canonical).cloned() {
261 event_map.insert(*alias, ev);
262 }
263 if let Some(ev) = eval_only_event_map.get(canonical).cloned() {
264 eval_only_event_map.insert(*alias, ev);
265 }
266 if let Some(ev) = apply_event_map.get(canonical).cloned() {
267 apply_event_map.insert(*alias, ev);
268 }
269 }
270
271 let mut four_state_inits = Vec::new();
273 if options.four_state {
274 for (addr, &offset) in &layout.offsets {
275 let width = layout.widths[addr];
276 let is_4state = sir
277 .design
278 .state_objects
279 .get(addr)
280 .map(|metadata| metadata.is_4state)
281 .unwrap_or(false);
282 if is_4state {
283 four_state_inits.push((offset, get_byte_size(width)));
284 }
285 }
286 for (addr, &rel_offset) in &layout.working_offsets {
287 let offset = layout.working_base_offset + rel_offset;
288 let width = layout.widths[addr];
289 let is_4state = sir
290 .design
291 .state_objects
292 .get(addr)
293 .map(|metadata| metadata.is_4state)
294 .unwrap_or(false);
295 if is_4state {
296 four_state_inits.push((offset, get_byte_size(width)));
297 }
298 }
299 }
300
301 let mut store = Store::new(&engine, ());
303 let runtime_event_base = layout.merged_total_size;
304 let comb_capture_enabled_base = runtime_event_base + layout.runtime_event_buffer_size;
305 let comb_capture_enabled_size = layout.runtime_event_site_layouts.len().max(1);
306 let wasm_state_size = comb_capture_enabled_base + comb_capture_enabled_size;
307 let mem_pages = wasm_state_size.div_ceil(65536) as u64;
308 let mem_pages = mem_pages.max(1);
309 let memory = Memory::new(
310 &mut store,
311 wasmtime::MemoryType::new(mem_pages as u32, None),
312 )
313 .map_err(|source| wasm_codegen_error("memory creation", source))?;
314
315 {
317 let mem_data = memory.data_mut(&mut store);
318 mem_data[STATE_HEADER_RUNTIME_EVENT_ADDR_OFFSET..][..8]
319 .copy_from_slice(&(runtime_event_base as u64).to_le_bytes());
320 mem_data[STATE_HEADER_COMB_CAPTURE_ENABLED_ADDR_OFFSET..][..8]
321 .copy_from_slice(&(comb_capture_enabled_base as u64).to_le_bytes());
322 for &(offset, allocated_size) in &four_state_inits {
323 for i in 0..allocated_size {
325 if offset + i < mem_data.len() {
326 mem_data[offset + i] = 0xFF;
327 }
328 if offset + allocated_size + i < mem_data.len() {
329 mem_data[offset + allocated_size + i] = 0xFF;
330 }
331 }
332 }
333 }
334
335 fn instantiate_module(
337 engine: &Engine,
338 store: &mut Store<()>,
339 module: &Module,
340 memory: &Memory,
341 ) -> Result<TypedFunc<(), i64>, crate::SimulatorError> {
342 let mut linker = Linker::new(engine);
343 linker
344 .define(&mut *store, "env", "memory", *memory)
345 .map_err(|source| wasm_codegen_error("linking", source))?;
346 let instance = linker
347 .instantiate(&mut *store, module)
348 .map_err(|source| wasm_codegen_error("instantiation", source))?;
349 let func = instance
350 .get_typed_func::<(), i64>(&mut *store, "run")
351 .map_err(|source| wasm_codegen_error("entry-point lookup", source))?;
352 Ok(func)
353 }
354
355 let comb_func = instantiate_module(&engine, &mut store, &comb_module, &memory)?;
356
357 let mut event_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>> = HashMap::default();
358 for (addr, module) in &event_modules {
359 let func = instantiate_module(&engine, &mut store, module, &memory)?;
360 event_funcs.entry(*addr).or_default().push(func);
361 }
362 let mut eval_only_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>> =
363 HashMap::default();
364 for (addr, module) in &eval_only_modules {
365 let func = instantiate_module(&engine, &mut store, module, &memory)?;
366 eval_only_funcs.entry(*addr).or_default().push(func);
367 }
368 let mut apply_funcs: HashMap<AbsoluteAddr, Vec<TypedFunc<(), i64>>> = HashMap::default();
369 for (addr, module) in &apply_modules {
370 let func = instantiate_module(&engine, &mut store, module, &memory)?;
371 apply_funcs.entry(*addr).or_default().push(func);
372 }
373
374 let id_to_event: Vec<WasmEventRef> =
375 id_to_addr.iter().map(|addr| event_map[addr]).collect();
376
377 Ok(Self {
378 store,
379 memory,
380 raw_view_exposed: false,
381 comb_func,
382 event_funcs,
383 eval_only_funcs,
384 apply_funcs,
385 event_map,
386 eval_only_event_map,
387 apply_event_map,
388 id_to_addr,
389 id_to_event,
390 layout,
391 options: options.clone(),
392 })
393 }
394
395 fn run_func(&mut self, func: &TypedFunc<(), i64>) -> Result<(), SimulatorErrorCode> {
396 let res = func.call(&mut self.store, ()).unwrap_or(2);
397 match res {
398 0 => Ok(()),
399 1 => Err(SimulatorErrorCode::DetectedTrueLoopCode(1)),
400 code if code >= 2000 => Err(SimulatorErrorCode::DetectedTrueLoopCode(code)),
401 _ => Err(SimulatorErrorCode::InternalError),
402 }
403 }
404
405 pub fn eval_comb(&mut self) -> Result<(), SimulatorErrorCode> {
406 let func = self.comb_func.clone();
407 self.run_func(&func)
408 }
409
410 pub fn eval_apply_ff_at(&mut self, event: &WasmEventRef) -> Result<(), SimulatorErrorCode> {
411 if let Some(funcs) = self.event_funcs.get(&event.addr).cloned() {
412 for func in funcs {
413 self.run_func(&func)?;
414 }
415 }
416 Ok(())
417 }
418
419 pub fn eval_only_ff_at(&mut self, event: &WasmEventRef) -> Result<(), SimulatorErrorCode> {
420 if let Some(funcs) = self.eval_only_funcs.get(&event.addr).cloned() {
421 for func in funcs {
422 self.run_func(&func)?;
423 }
424 }
425 Ok(())
426 }
427
428 pub fn apply_ff_at(&mut self, event: &WasmEventRef) -> Result<(), SimulatorErrorCode> {
429 if let Some(funcs) = self.apply_funcs.get(&event.addr).cloned() {
430 for func in funcs {
431 self.run_func(&func)?;
432 }
433 }
434 Ok(())
435 }
436
437 pub fn resolve_signal(&self, addr: &AbsoluteAddr) -> SignalRef {
438 let offset = self.layout.offsets[addr];
439 let width = self.layout.widths[addr];
440 let is_4state = self.layout.is_4states[addr];
441 SignalRef {
442 offset,
443 width,
444 is_4state,
445 array_layout: None,
446 }
447 }
448
449 pub fn resolve_event(&self, addr: &AbsoluteAddr) -> WasmEventRef {
450 self.event_map[addr]
451 }
452
453 pub fn resolve_event_opt(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
454 self.event_map.get(addr).cloned()
455 }
456
457 pub fn resolve_eval_only_event(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
458 self.eval_only_event_map.get(addr).cloned()
459 }
460
461 pub fn resolve_apply_event(&self, addr: &AbsoluteAddr) -> Option<WasmEventRef> {
462 self.apply_event_map.get(addr).cloned()
463 }
464
465 pub fn set<T: Copy>(&mut self, signal: SignalRef, value: T) {
466 celox_runtime::backend::SimBackend::mark_vcd_signal(self, signal);
467 let allocated_size = get_byte_size(signal.width);
468 let provided_size = std::mem::size_of::<T>();
469 assert!(provided_size <= allocated_size);
470
471 let mem = self.memory.data_mut(&mut self.store);
472 for i in 0..allocated_size {
474 mem[signal.offset + i] = 0;
475 }
476 let bytes =
478 unsafe { std::slice::from_raw_parts(&value as *const T as *const u8, provided_size) };
479 mem[signal.offset..signal.offset + provided_size].copy_from_slice(bytes);
480
481 if self.options.four_state && signal.is_4state {
483 for i in 0..allocated_size {
484 mem[signal.offset + allocated_size + i] = 0;
485 }
486 }
487 }
488
489 pub fn set_wide(&mut self, signal: SignalRef, value: BigUint) {
490 celox_runtime::backend::SimBackend::mark_vcd_signal(self, signal);
491 let allocated_size = get_byte_size(signal.width);
492 let mut bytes = value.to_bytes_le();
493 bytes.resize(allocated_size, 0u8);
494
495 let mem = self.memory.data_mut(&mut self.store);
496 mem[signal.offset..signal.offset + allocated_size]
497 .copy_from_slice(&bytes[..allocated_size]);
498
499 if self.options.four_state && signal.is_4state {
500 for i in 0..allocated_size {
501 mem[signal.offset + allocated_size + i] = 0;
502 }
503 }
504 }
505
506 pub fn set_four_state(&mut self, signal: SignalRef, value: BigUint, mask: BigUint) {
507 celox_runtime::backend::SimBackend::mark_vcd_signal(self, signal);
508 let allocated_size = get_byte_size(signal.width);
509 let mut v_bytes = value.to_bytes_le();
510 v_bytes.resize(allocated_size, 0u8);
511
512 let mem = self.memory.data_mut(&mut self.store);
513 mem[signal.offset..signal.offset + allocated_size]
514 .copy_from_slice(&v_bytes[..allocated_size]);
515
516 if self.options.four_state && signal.is_4state {
517 let mut m_bytes = mask.to_bytes_le();
518 m_bytes.resize(allocated_size, 0u8);
519 mem[signal.offset + allocated_size..signal.offset + 2 * allocated_size]
520 .copy_from_slice(&m_bytes[..allocated_size]);
521 }
522 }
523
524 pub fn get(&self, signal: SignalRef) -> BigUint {
525 let byte_size = get_byte_size(signal.width);
526 let mem = self.memory.data(&self.store);
527 let slice = &mem[signal.offset..signal.offset + byte_size];
528 let mut val = BigUint::from_bytes_le(slice);
529 let extra_bits = byte_size * 8 - signal.width;
530 if extra_bits > 0 {
531 let mask = (BigUint::from(1u32) << signal.width) - 1u32;
532 val &= mask;
533 }
534 val
535 }
536
537 pub fn get_as<T: Default + Copy>(&self, signal: SignalRef) -> T {
538 let byte_size = get_byte_size(signal.width);
539 let provided_size = std::mem::size_of::<T>();
540 assert!(byte_size <= provided_size);
541
542 let mem = self.memory.data(&self.store);
543 let slice = &mem[signal.offset..signal.offset + byte_size];
544
545 let mut val = T::default();
546 unsafe {
547 let val_ptr = &mut val as *mut T as *mut u8;
548 std::ptr::copy_nonoverlapping(slice.as_ptr(), val_ptr, byte_size);
549 }
550 val
551 }
552
553 pub fn get_four_state(&self, signal: SignalRef) -> (BigUint, BigUint) {
554 let byte_size = get_byte_size(signal.width);
555 let mem = self.memory.data(&self.store);
556 let v_slice = &mem[signal.offset..signal.offset + byte_size];
557 let mut v_val = BigUint::from_bytes_le(v_slice);
558
559 let mut m_val = if self.options.four_state && signal.is_4state {
560 let m_slice = &mem[signal.offset + byte_size..signal.offset + 2 * byte_size];
561 BigUint::from_bytes_le(m_slice)
562 } else {
563 BigUint::from(0u32)
564 };
565
566 let extra_bits = byte_size * 8 - signal.width;
567 if extra_bits > 0 {
568 let bitmask = (BigUint::from(1u32) << signal.width) - 1u32;
569 v_val &= &bitmask;
570 m_val &= &bitmask;
571 }
572
573 (v_val, m_val)
574 }
575
576 pub fn memory_as_ptr(&self) -> (*const u8, usize) {
577 let data = self.memory.data(&self.store);
578 (data.as_ptr(), self.layout.merged_total_size)
579 }
580
581 pub fn memory_as_mut_ptr(&mut self) -> (*mut u8, usize) {
582 self.raw_view_exposed = true;
583 let data = self.memory.data_mut(&mut self.store);
584 (data.as_mut_ptr(), self.layout.merged_total_size)
585 }
586
587 pub fn runtime_event_buffer_as_ptr(&self) -> (*const u8, usize) {
588 let data = self.memory.data(&self.store);
589 let event_base = self.layout.merged_total_size;
590 (
591 unsafe { data.as_ptr().add(event_base) },
592 self.layout.runtime_event_buffer_size,
593 )
594 }
595
596 pub fn set_comb_capture_event_enabled(&mut self, active_sites: &[bool]) {
597 let enabled_base = self.layout.merged_total_size + self.layout.runtime_event_buffer_size;
598 let enabled_len = self.layout.runtime_event_site_layouts.len().max(1);
599 let mem = self.memory.data_mut(&mut self.store);
600 mem[enabled_base..enabled_base + enabled_len].fill(0);
601 for (idx, &active) in active_sites.iter().enumerate().take(enabled_len) {
602 if active {
603 mem[enabled_base + idx] = 1;
604 }
605 }
606 }
607
608 pub fn stable_region_size(&self) -> usize {
609 self.layout.total_size
610 }
611
612 pub fn layout(&self) -> &MemoryLayout {
613 &self.layout
614 }
615
616 pub fn id_to_addr_slice(&self) -> &[AbsoluteAddr] {
617 &self.id_to_addr
618 }
619
620 pub fn id_to_event_slice(&self) -> &[WasmEventRef] {
621 &self.id_to_event
622 }
623
624 pub fn num_events(&self) -> usize {
625 let mut max_id = 0;
626 for ev in self.event_map.values() {
627 max_id = max_id.max(ev.id);
628 }
629 for ev in self.eval_only_event_map.values() {
630 max_id = max_id.max(ev.id);
631 }
632 for ev in self.apply_event_map.values() {
633 max_id = max_id.max(ev.id);
634 }
635 if self.event_map.is_empty()
636 && self.eval_only_event_map.is_empty()
637 && self.apply_event_map.is_empty()
638 {
639 0
640 } else {
641 max_id + 1
642 }
643 }
644
645 pub fn clear_triggered_bits(&mut self) {
646 let offset = self.layout.triggered_bits_offset;
647 let size = self.layout.triggered_bits_total_size;
648 let mem = self.memory.data_mut(&mut self.store);
649 for i in 0..size {
650 mem[offset + i] = 0;
651 }
652 }
653
654 pub fn mark_triggered_bit(&mut self, id: usize) {
655 let byte_idx = id / 8;
656 let bit_idx = id % 8;
657 let offset = self.layout.triggered_bits_offset + byte_idx;
658 let mem = self.memory.data_mut(&mut self.store);
659 mem[offset] |= 1 << bit_idx;
660 }
661
662 pub fn get_triggered_bits(&self) -> bit_set::BitSet {
663 let mut bits = bit_set::BitSet::with_capacity(self.num_events());
664 let mem = self.memory.data(&self.store);
665 let offset = self.layout.triggered_bits_offset;
666 let size = self.layout.triggered_bits_total_size;
667
668 for i in 0..size {
669 let byte = mem[offset + i];
670 if byte != 0 {
671 for j in 0..8 {
672 if (byte & (1 << j)) != 0 {
673 bits.insert(i * 8 + j);
674 }
675 }
676 }
677 }
678 bits
679 }
680}
681
682