1use std::cell::{Cell, RefCell};
42use std::collections::BTreeMap;
43
44use sv_sim_abi::{Bit4, SimHandle, SlotId};
45
46use super::accessor_impl::{resolve_foreign_slot, slot_location_for};
47use super::instance::InstanceHandle;
48use super::write::SlotLocation;
49use super::{Arena, ChangeDelta, FourStateValue, SlotClass, StateCellId};
50
51struct HugeMirror {
53 width: u32,
55 pk: Box<[u64]>,
58 valid: bool,
60 dirty_lo: u32,
63 dirty_hi: u32,
64}
65
66impl HugeMirror {
67 fn new(width: u32) -> Self {
68 let words = (2 * width as usize).div_ceil(64) + 2;
69 HugeMirror {
70 width,
71 pk: vec![0u64; words].into_boxed_slice(),
72 valid: false,
73 dirty_lo: 0,
74 dirty_hi: 0,
75 }
76 }
77
78 fn clear_dirty(&mut self) {
79 self.dirty_lo = 0;
80 self.dirty_hi = 0;
81 }
82
83 fn dirty(&self) -> bool {
84 self.dirty_lo < self.dirty_hi
85 }
86
87 fn plane_words(&self) -> usize {
88 (self.width as usize).div_ceil(64)
89 }
90}
91
92pub(crate) struct HugeMirrors {
95 map: RefCell<BTreeMap<u32, HugeMirror>>,
96 count: Cell<usize>,
99 verify: bool,
102}
103
104impl Default for HugeMirrors {
105 fn default() -> Self {
106 HugeMirrors {
107 map: RefCell::default(),
108 count: Cell::new(0),
109 verify: std::env::var_os("SV_SIM_HUGE_VERIFY").is_some_and(|v| v != "0"),
110 }
111 }
112}
113
114impl HugeMirrors {
115 pub(crate) fn invalidate_range(&self, start: usize, words: usize) {
119 if self.count.get() == 0 || words == 0 {
120 return;
121 }
122 let end = start + words;
123 let mut map = self.map.borrow_mut();
124 for (&key, m) in map.range_mut(..end as u32).rev() {
128 if key as usize + m.plane_words() <= start {
129 break;
130 }
131 m.valid = false;
132 }
133 }
134
135 pub(crate) fn invalidate_all(&self) {
138 if self.count.get() == 0 {
139 return;
140 }
141 for m in self.map.borrow_mut().values_mut() {
142 m.valid = false;
143 }
144 }
145}
146
147fn low_mask(n: usize) -> u64 {
148 if n >= 64 { u64::MAX } else { (1u64 << n) - 1 }
149}
150
151fn put_word(dst: &mut [u64], pos: usize, v: u64, n: usize) {
153 let wi = pos / 64;
154 let bo = pos % 64;
155 let m = low_mask(n);
156 let v = v & m;
157 dst[wi] = (dst[wi] & !(m << bo)) | (v << bo);
158 if bo + n > 64 {
159 dst[wi + 1] = (dst[wi + 1] & !(m >> (64 - bo))) | (v >> (64 - bo));
160 }
161}
162
163fn pk_word(img: &[u64], pos: usize, len: usize) -> u64 {
165 let (wi, bo) = (pos / 64, pos % 64);
166 let lo = img.get(wi).copied().unwrap_or(0) >> bo;
167 let hi = if bo == 0 {
168 0
169 } else {
170 img.get(wi + 1).copied().unwrap_or(0) << (64 - bo)
171 };
172 (lo | hi) & low_mask(len)
173}
174
175fn planes_word(img: &[u64], w: usize, i: usize) -> (u64, u64) {
177 let len = (w - i * 64).min(64);
178 let v = pk_word(img, i * 64, len);
179 let u = pk_word(img, w + i * 64, len);
180 (v ^ u, u)
181}
182
183fn window_mask(lo: usize, hi: usize, i: usize) -> u64 {
185 let base = i * 64;
186 let wl = lo.saturating_sub(base).min(64);
187 let wh = hi.saturating_sub(base).min(64);
188 if wh <= wl {
189 return 0;
190 }
191 low_mask(wh) & !low_mask(wl)
192}
193
194fn rebuild(arena: &Arena, start: usize, m: &mut HugeMirror) {
197 let w = m.width as usize;
198 let n = m.plane_words();
199 let (a, b) = (arena.wide_aval(), arena.wide_bval());
200 m.pk.fill(0);
201 for i in 0..n {
202 let len = (w - i * 64).min(64);
203 let (av, bv) = (a[start + i], b[start + i]);
204 put_word(&mut m.pk, i * 64, av ^ bv, len);
205 put_word(&mut m.pk, w + i * 64, bv, len);
206 }
207 m.valid = true;
208 m.clear_dirty();
209}
210
211fn verify_matches(arena: &Arena, start: usize, m: &HugeMirror, what: &str) {
213 let w = m.width as usize;
214 let (a, b) = (arena.wide_aval(), arena.wide_bval());
215 for i in 0..m.plane_words() {
216 let len = (w - i * 64).min(64);
217 let mask = low_mask(len);
218 let (na, nb) = planes_word(&m.pk, w, i);
219 let (oa, ob) = (a[start + i], b[start + i]);
220 assert!(
221 ((oa ^ na) | (ob ^ nb)) & mask == 0,
222 "SV_SIM_HUGE_VERIFY ({what}): huge signal at wide word {start} (width {w}) \
223 diverges from its mirror in plane word {i}: planes ({oa:#x},{ob:#x}) vs \
224 mirror ({na:#x},{nb:#x}); dirty window [{}, {})",
225 m.dirty_lo,
226 m.dirty_hi
227 );
228 }
229}
230
231fn locate(handle: SimHandle, slot: SlotId) -> (&'static InstanceHandle, SlotLocation) {
234 let (owner, oslot) = resolve_foreign_slot(handle, slot);
235 let ih: &'static InstanceHandle = unsafe { InstanceHandle::from_sim_handle(owner) };
239 (ih, slot_location_for(ih, oslot))
240}
241
242pub fn huge_prime(handle: SimHandle, slot: SlotId, width: u32, force_rebuild: bool) -> *mut u64 {
250 let (ih, loc) = locate(handle, slot);
251 debug_assert_eq!(loc.class, SlotClass::Wide);
252 debug_assert_eq!(loc.bit_width, width);
253 let arena = ih.arena();
254 let start = loc.word_offset as usize;
255 let mut map = arena.huge.map.borrow_mut();
256 let m = map.entry(loc.word_offset).or_insert_with(|| {
257 arena.huge.count.set(arena.huge.count.get() + 1);
258 HugeMirror::new(width)
259 });
260 debug_assert_eq!(m.width, width);
261 if !m.valid || force_rebuild {
262 rebuild(arena, start, m);
263 } else if arena.huge.verify {
264 verify_matches(arena, start, m, "prime");
265 }
266 m.pk.as_mut_ptr()
267}
268
269pub fn huge_mark(handle: SimHandle, slot: SlotId, start: i64, len: i64) {
273 if len <= 0 {
274 return;
275 }
276 let (ih, loc) = locate(handle, slot);
277 let arena = ih.arena();
278 let mut map = arena.huge.map.borrow_mut();
279 let Some(m) = map.get_mut(&loc.word_offset) else {
280 return;
281 };
282 let lo = i128::from(start).max(0);
283 let hi = (i128::from(start) + i128::from(len)).min(i128::from(m.width));
284 if lo >= hi {
285 return;
286 }
287 let (lo, hi) = (lo as u32, hi as u32);
288 if m.dirty() {
289 m.dirty_lo = m.dirty_lo.min(lo);
290 m.dirty_hi = m.dirty_hi.max(hi);
291 } else {
292 m.dirty_lo = lo;
293 m.dirty_hi = hi;
294 }
295}
296
297pub enum HugeCommit {
300 Nothing,
302 Unchanged,
304 Changed {
307 cell: StateCellId,
308 old: Bit4,
309 new: Bit4,
310 delta: Option<ChangeDelta>,
313 },
314 Full {
319 cell: StateCellId,
320 value: FourStateValue,
321 revalidate: bool,
323 },
324}
325
326pub fn huge_commit(
328 handle: SimHandle,
329 slot: SlotId,
330 width: u32,
331 wants_delta: &dyn Fn(StateCellId) -> bool,
332) -> HugeCommit {
333 let (ih, loc) = locate(handle, slot);
334 let arena = ih.arena();
335 let start = loc.word_offset as usize;
336 let mut map = arena.huge.map.borrow_mut();
337 let Some(m) = map.get_mut(&loc.word_offset) else {
338 return HugeCommit::Nothing;
339 };
340 if !m.dirty() {
341 return HugeCommit::Nothing;
342 }
343 debug_assert_eq!(m.width, width);
344 let w = m.width as usize;
345 let (lo, hi) = (m.dirty_lo as usize, m.dirty_hi as usize);
346 let (wlo, whi) = (lo / 64, (hi - 1) / 64);
347
348 let this = unsafe { InstanceHandle::from_sim_handle(handle) };
350 let cell = this.cell_for_slot(slot.0 as usize);
351 let forced = ih.vc().force.borrow().contains(cell);
352 if forced || ih.vc().records_changes() {
353 let n = m.plane_words();
358 let (pa, pb) = (arena.wide_aval(), arena.wide_bval());
359 let mut aval = pa[start..start + n].to_vec();
360 let mut bval = pb[start..start + n].to_vec();
361 if m.valid {
362 for i in 0..n {
363 (aval[i], bval[i]) = planes_word(&m.pk, w, i);
364 }
365 } else {
366 for i in wlo..=whi {
368 let (a, b) = planes_word(&m.pk, w, i);
369 let k = window_mask(lo, hi, i);
370 aval[i] = (aval[i] & !k) | (a & k);
371 bval[i] = (bval[i] & !k) | (b & k);
372 }
373 }
374 m.clear_dirty();
375 return HugeCommit::Full {
376 cell,
377 value: FourStateValue::Wide { aval, bval },
378 revalidate: m.valid && !forced,
379 };
380 }
381
382 let (ap, bp) = unsafe {
386 (
387 arena.wide_aval_ptr().add(start),
388 arena.wide_bval_ptr().add(start),
389 )
390 };
391 let mut changed = false;
392 let (mut old0, mut new0) = (Bit4::Zero, Bit4::Zero);
393 let mut delta = wants_delta(cell).then(|| ChangeDelta {
394 lo_word: wlo,
395 ..ChangeDelta::default()
396 });
397 for i in wlo..=whi {
398 let len = (w - i * 64).min(64);
399 let mask = low_mask(len);
400 let (na, nb) = planes_word(&m.pk, w, i);
401 let (oa, ob) = unsafe { (ap.add(i).read(), bp.add(i).read()) };
403 let (na, nb) = if m.valid {
405 (na, nb)
406 } else {
407 let k = window_mask(lo, hi, i);
408 ((oa & !k) | (na & k), (ob & !k) | (nb & k))
409 };
410 if i == 0 {
411 old0 = Bit4::from_aval_bval(oa & 1 != 0, ob & 1 != 0);
412 new0 = Bit4::from_aval_bval(na & 1 != 0, nb & 1 != 0);
413 }
414 if ((oa ^ na) | (ob ^ nb)) & mask != 0 {
415 changed = true;
416 }
417 if let Some(d) = delta.as_mut() {
418 d.old_a.push(oa);
419 d.old_b.push(ob);
420 d.new_a.push(na);
421 d.new_b.push(nb);
422 }
423 if oa != na || ob != nb {
424 unsafe {
426 ap.add(i).write(na);
427 bp.add(i).write(nb);
428 }
429 }
430 }
431 m.clear_dirty();
432 if arena.huge.verify && m.valid {
433 verify_matches(arena, start, m, "commit");
434 }
435 if changed {
436 HugeCommit::Changed {
437 cell,
438 old: old0,
439 new: new0,
440 delta,
441 }
442 } else {
443 HugeCommit::Unchanged
444 }
445}
446
447pub struct NbaWindow {
450 pub cell: StateCellId,
451 pub window: u32,
452 pub aval: Vec<u64>,
453 pub bval: Vec<u64>,
454 pub mask: Vec<u64>,
455}
456
457pub fn huge_nba_window(
463 handle: SimHandle,
464 slot: SlotId,
465 width: u32,
466 start: i64,
467 len: i64,
468 img: &[u64],
469) -> Option<NbaWindow> {
470 let lo = i128::from(start).max(0);
471 let hi = (i128::from(start) + i128::from(len)).min(i128::from(width));
472 if len <= 0 || lo >= hi {
473 return None;
474 }
475 let (lo, hi) = (lo as usize, hi as usize);
476 let (start, len) = (start as i128, len as usize);
477 let (wlo, whi) = (lo / 64, (hi - 1) / 64);
478 let words = whi - wlo + 1;
479 let (mut aval, mut bval, mut mask) = (
480 Vec::with_capacity(words),
481 Vec::with_capacity(words),
482 Vec::with_capacity(words),
483 );
484 for i in wlo..=whi {
485 let base = i * 64;
486 let (s, e) = (lo.max(base), hi.min(base + 64));
487 let (n, sh) = (e - s, s - base);
488 let src = (s as i128 - start) as usize;
489 let v = pk_word(img, src, n);
490 let u = pk_word(img, len + src, n);
491 aval.push((v ^ u) << sh);
492 bval.push(u << sh);
493 mask.push(low_mask(n) << sh);
494 }
495 let this = unsafe { InstanceHandle::from_sim_handle(handle) };
497 Some(NbaWindow {
498 cell: this.cell_for_slot(slot.0 as usize),
499 window: wlo as u32,
500 aval,
501 bval,
502 mask,
503 })
504}
505
506pub(crate) fn read_window(
509 arena: &Arena,
510 start: usize,
511 lo_word: usize,
512 words: usize,
513) -> (Vec<u64>, Vec<u64>) {
514 let at = start + lo_word;
515 (
516 arena.wide_aval()[at..at + words].to_vec(),
517 arena.wide_bval()[at..at + words].to_vec(),
518 )
519}
520
521pub(crate) fn store_window(
529 arena: &Arena,
530 start: usize,
531 width: u32,
532 lo_word: usize,
533 aval: &[u64],
534 bval: &[u64],
535) -> bool {
536 let w = width as usize;
537 let plane_words = w.div_ceil(64);
538 debug_assert_eq!(aval.len(), bval.len());
539 debug_assert!(lo_word + aval.len() <= plane_words);
540 let (ap, bp) = unsafe {
544 (
545 arena.wide_aval_ptr().add(start),
546 arena.wide_bval_ptr().add(start),
547 )
548 };
549 let mut changed = false;
550 for (j, (&na, &nb)) in aval.iter().zip(bval).enumerate() {
551 let i = lo_word + j;
552 let mask = low_mask((w - i * 64).min(64));
553 let (oa, ob) = unsafe { (ap.add(i).read(), bp.add(i).read()) };
555 if ((oa ^ na) | (ob ^ nb)) & mask != 0 {
556 changed = true;
557 }
558 if oa != na || ob != nb {
559 unsafe {
561 ap.add(i).write(na);
562 bp.add(i).write(nb);
563 }
564 }
565 }
566 if changed {
567 let mut map = arena.huge.map.borrow_mut();
568 if let Some(m) = map.get_mut(&(start as u32)).filter(|m| m.valid) {
569 let (dlo, dhi) = if m.dirty() {
570 (m.dirty_lo as usize, m.dirty_hi as usize)
571 } else {
572 (0, 0)
573 };
574 for (j, (&na, &nb)) in aval.iter().zip(bval).enumerate() {
575 let i = lo_word + j;
576 let len = (w - i * 64).min(64);
577 let keep = window_mask(dlo, dhi, i);
580 let (ca, cb) = planes_word(&m.pk, w, i);
581 let (ma, mb) = ((ca & keep) | (na & !keep), (cb & keep) | (nb & !keep));
582 put_word(&mut m.pk, i * 64, ma ^ mb, len);
583 put_word(&mut m.pk, w + i * 64, mb, len);
584 }
585 if arena.huge.verify && !m.dirty() {
586 verify_matches(arena, start, m, "nba");
587 }
588 }
589 }
590 changed
591}
592
593pub fn huge_commit_full_done(handle: SimHandle, slot: SlotId, revalidate: bool) {
597 if !revalidate {
598 return;
599 }
600 let (ih, loc) = locate(handle, slot);
601 let arena = ih.arena();
602 if let Some(m) = arena.huge.map.borrow_mut().get_mut(&loc.word_offset) {
603 m.valid = true;
604 m.clear_dirty();
605 }
606}