use super::covdecl::{get_covgroup, put_covgroup};
use super::verif::{
AssertionImage, CasePairImage, ClockingImage, CovClassClockImage, CovExprTermImage,
CovGroupImage, CovSamplerImage, FreeGroupImage, FreeVarImage, SampledImage, SampledSiteImage,
SpawnCallImage, VerifImage,
};
use super::*;
use crate::collections::Key;
use crate::netlist::{ClassTemplateId, SymbolKey};
use crate::objects::{FieldSlot, NestedHandle, RandKind, RandProp, TemplateSnapshot};
use crate::sched::ProcessKind;
use crate::sched::assert_control::SiteDesc;
use crate::state::{
DriveStrength, DriveStrengthPair, NetKind, NetLayout, NetShape, SlotClass, SlotDescriptor,
SlotOffset, StateLayout,
};
use crate::sva::engine::AssertKind;
use crate::sva::ir::{LeafId, LocalVarId};
#[derive(Debug, PartialEq, Eq)]
pub enum ImageError {
Truncated,
BadMagic,
BadVersion {
found: u32,
expected: u32,
},
BadHash,
BadTag {
what: &'static str,
tag: u8,
},
BadUtf8,
TrailingBytes,
BadReference(&'static str),
}
impl std::fmt::Display for ImageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImageError::Truncated => write!(f, "design image is truncated"),
ImageError::BadMagic => write!(f, "not a design image (bad magic)"),
ImageError::BadVersion { found, expected } => write!(
f,
"design image version {found} (this runner reads version {expected})"
),
ImageError::BadHash => write!(f, "design image is corrupted (content hash mismatch)"),
ImageError::BadTag { what, tag } => {
write!(f, "design image has an unknown {what} tag {tag}")
}
ImageError::BadUtf8 => write!(f, "design image has a non-UTF-8 name"),
ImageError::TrailingBytes => write!(f, "design image has trailing bytes"),
ImageError::BadReference(what) => {
write!(f, "design image has a dangling {what} reference")
}
}
}
}
impl std::error::Error for ImageError {}
fn fnv1a(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in bytes {
h ^= u64::from(b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
#[derive(Default)]
pub struct Writer {
pub buf: Vec<u8>,
}
impl Writer {
pub fn u8(&mut self, v: u8) {
self.buf.push(v);
}
pub fn bool(&mut self, v: bool) {
self.buf.push(u8::from(v));
}
pub fn u32(&mut self, v: u32) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn i32(&mut self, v: i32) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn u64(&mut self, v: u64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn u128(&mut self, v: u128) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn len(&mut self, n: usize) {
self.u64(n as u64);
}
pub fn str(&mut self, s: &str) {
self.bytes(s.as_bytes());
}
pub fn bytes(&mut self, s: &[u8]) {
self.len(s.len());
self.buf.extend_from_slice(s);
}
pub fn words(&mut self, v: &[u64]) {
self.len(v.len());
for &w in v {
self.u64(w);
}
}
}
pub struct Reader<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Reader { bytes, pos: 0 }
}
fn take(&mut self, n: usize) -> Result<&'a [u8], ImageError> {
let end = self.pos.checked_add(n).ok_or(ImageError::Truncated)?;
let out = self.bytes.get(self.pos..end).ok_or(ImageError::Truncated)?;
self.pos = end;
Ok(out)
}
pub fn u8(&mut self) -> Result<u8, ImageError> {
Ok(self.take(1)?[0])
}
pub fn bool(&mut self) -> Result<bool, ImageError> {
match self.u8()? {
0 => Ok(false),
1 => Ok(true),
tag => Err(ImageError::BadTag { what: "bool", tag }),
}
}
pub fn u32(&mut self) -> Result<u32, ImageError> {
Ok(u32::from_le_bytes(
self.take(4)?.try_into().expect("4 bytes"),
))
}
pub fn i32(&mut self) -> Result<i32, ImageError> {
Ok(i32::from_le_bytes(
self.take(4)?.try_into().expect("4 bytes"),
))
}
pub fn u64(&mut self) -> Result<u64, ImageError> {
Ok(u64::from_le_bytes(
self.take(8)?.try_into().expect("8 bytes"),
))
}
pub fn u128(&mut self) -> Result<u128, ImageError> {
Ok(u128::from_le_bytes(
self.take(16)?.try_into().expect("16 bytes"),
))
}
#[allow(clippy::len_without_is_empty)] pub fn len(&mut self) -> Result<usize, ImageError> {
let n = self.u64()?;
if n > (self.bytes.len() - self.pos) as u64 {
return Err(ImageError::Truncated);
}
Ok(n as usize)
}
pub fn str(&mut self) -> Result<String, ImageError> {
String::from_utf8(self.bytes()?).map_err(|_| ImageError::BadUtf8)
}
pub fn bytes(&mut self) -> Result<Vec<u8>, ImageError> {
let n = self.len()?;
Ok(self.take(n)?.to_vec())
}
pub fn words(&mut self) -> Result<Vec<u64>, ImageError> {
let n = self.len()?;
if n.checked_mul(8)
.is_none_or(|b| b > self.bytes.len() - self.pos)
{
return Err(ImageError::Truncated);
}
(0..n).map(|_| self.u64()).collect()
}
pub fn done(&self) -> bool {
self.pos == self.bytes.len()
}
}
fn put_nest(w: &mut Writer, n: &crate::collections::Nest) {
w.len(n.slots.len());
for (off, inner) in &n.slots {
w.u32(*off);
put_nest(w, inner);
}
}
fn get_nest(r: &mut Reader<'_>) -> Result<crate::collections::Nest, ImageError> {
let n = r.len()?;
let mut slots = Vec::with_capacity(n);
for _ in 0..n {
let off = r.u32()?;
slots.push((off, get_nest(r)?));
}
Ok(crate::collections::Nest { slots })
}
fn put_slot_class(w: &mut Writer, c: SlotClass) {
w.u8(match c {
SlotClass::Inline => 0,
SlotClass::Wide => 1,
SlotClass::Memory => 2,
SlotClass::ForceWide => 3,
});
}
fn get_slot_class(r: &mut Reader<'_>) -> Result<SlotClass, ImageError> {
Ok(match r.u8()? {
0 => SlotClass::Inline,
1 => SlotClass::Wide,
2 => SlotClass::Memory,
3 => SlotClass::ForceWide,
tag => {
return Err(ImageError::BadTag {
what: "slot class",
tag,
});
}
})
}
fn put_edge(w: &mut Writer, e: EdgeKind) {
w.u8(match e {
EdgeKind::None => 0,
EdgeKind::Posedge => 1,
EdgeKind::Negedge => 2,
EdgeKind::BothEdges => 3,
});
}
fn get_edge(r: &mut Reader<'_>) -> Result<EdgeKind, ImageError> {
Ok(match r.u8()? {
0 => EdgeKind::None,
1 => EdgeKind::Posedge,
2 => EdgeKind::Negedge,
3 => EdgeKind::BothEdges,
tag => {
return Err(ImageError::BadTag {
what: "edge kind",
tag,
});
}
})
}
fn put_opt_edge(w: &mut Writer, e: Option<EdgeKind>) {
match e {
None => w.u8(0),
Some(e) => {
w.u8(1);
put_edge(w, e);
}
}
}
fn get_opt_edge(r: &mut Reader<'_>) -> Result<Option<EdgeKind>, ImageError> {
Ok(match r.u8()? {
0 => None,
1 => Some(get_edge(r)?),
tag => {
return Err(ImageError::BadTag {
what: "option",
tag,
});
}
})
}
fn put_net_kind(w: &mut Writer, k: NetKind) {
w.u8(match k {
NetKind::Wire => 0,
NetKind::WAnd => 1,
NetKind::WOr => 2,
NetKind::Tri => 3,
NetKind::TriAnd => 4,
NetKind::TriOr => 5,
NetKind::Tri0 => 6,
NetKind::Tri1 => 7,
NetKind::TriReg => 8,
NetKind::Supply0 => 9,
NetKind::Supply1 => 10,
NetKind::UWire => 11,
NetKind::UserDefined => 12,
});
}
fn get_net_kind(r: &mut Reader<'_>) -> Result<NetKind, ImageError> {
Ok(match r.u8()? {
0 => NetKind::Wire,
1 => NetKind::WAnd,
2 => NetKind::WOr,
3 => NetKind::Tri,
4 => NetKind::TriAnd,
5 => NetKind::TriOr,
6 => NetKind::Tri0,
7 => NetKind::Tri1,
8 => NetKind::TriReg,
9 => NetKind::Supply0,
10 => NetKind::Supply1,
11 => NetKind::UWire,
12 => NetKind::UserDefined,
tag => {
return Err(ImageError::BadTag {
what: "net kind",
tag,
});
}
})
}
fn put_net_shape(w: &mut Writer, s: NetShape) {
w.u8(match s {
NetShape::AliasedSingleDriver => 0,
NetShape::MultiDriverUniformStrength => 1,
NetShape::FullStrengthLattice => 2,
});
}
fn get_net_shape(r: &mut Reader<'_>) -> Result<NetShape, ImageError> {
Ok(match r.u8()? {
0 => NetShape::AliasedSingleDriver,
1 => NetShape::MultiDriverUniformStrength,
2 => NetShape::FullStrengthLattice,
tag => {
return Err(ImageError::BadTag {
what: "net shape",
tag,
});
}
})
}
fn put_process_kind(w: &mut Writer, k: ProcessKind) {
w.u8(match k {
ProcessKind::Initial => 0,
ProcessKind::Always => 1,
ProcessKind::AlwaysComb => 2,
ProcessKind::AlwaysLatch => 3,
ProcessKind::AlwaysFF => 4,
ProcessKind::Final => 5,
});
}
fn get_process_kind(r: &mut Reader<'_>) -> Result<ProcessKind, ImageError> {
Ok(match r.u8()? {
0 => ProcessKind::Initial,
1 => ProcessKind::Always,
2 => ProcessKind::AlwaysComb,
3 => ProcessKind::AlwaysLatch,
4 => ProcessKind::AlwaysFF,
5 => ProcessKind::Final,
tag => {
return Err(ImageError::BadTag {
what: "process kind",
tag,
});
}
})
}
pub fn put_four_state(w: &mut Writer, v: &FourStateValue) {
match v {
FourStateValue::Narrow(word) => {
w.u8(0);
w.u128(*word);
}
FourStateValue::Wide { aval, bval } => {
w.u8(1);
w.len(aval.len());
for a in aval {
w.u64(*a);
}
w.len(bval.len());
for b in bval {
w.u64(*b);
}
}
}
}
pub fn get_four_state(r: &mut Reader<'_>) -> Result<FourStateValue, ImageError> {
Ok(match r.u8()? {
0 => FourStateValue::Narrow(r.u128()?),
1 => {
let n = r.len()?;
let aval = (0..n).map(|_| r.u64()).collect::<Result<_, _>>()?;
let n = r.len()?;
let bval = (0..n).map(|_| r.u64()).collect::<Result<_, _>>()?;
FourStateValue::Wide { aval, bval }
}
tag => {
return Err(ImageError::BadTag {
what: "four-state value",
tag,
});
}
})
}
pub fn put_layout(w: &mut Writer, l: &StateLayout) {
w.u32(l.inline_words);
w.u32(l.wide_words);
w.len(l.slots.len());
for s in &l.slots {
w.u32(s.key.0);
put_slot_class(w, s.offset.class);
w.u32(s.offset.word_offset);
w.u32(s.offset.bit_width);
w.u32(s.offset.elem_word_count);
w.u32(s.offset.row_stride_words);
w.u32(s.offset.num_rows);
match &s.initial {
None => w.u8(0),
Some(v) => {
w.u8(1);
put_four_state(w, v);
}
}
match &s.net_info {
None => w.u8(0),
Some(n) => {
w.u8(1);
put_net_kind(w, n.kind);
put_net_shape(w, n.shape);
}
}
}
}
pub fn get_layout(r: &mut Reader<'_>) -> Result<StateLayout, ImageError> {
let inline_words = r.u32()?;
let wide_words = r.u32()?;
let n = r.len()?;
let mut slots = Vec::with_capacity(n);
for _ in 0..n {
let key = SymbolKey(r.u32()?);
let offset = SlotOffset {
class: get_slot_class(r)?,
word_offset: r.u32()?,
bit_width: r.u32()?,
elem_word_count: r.u32()?,
row_stride_words: r.u32()?,
num_rows: r.u32()?,
};
let initial = match r.u8()? {
0 => None,
1 => Some(get_four_state(r)?),
tag => {
return Err(ImageError::BadTag {
what: "option",
tag,
});
}
};
let net_info = match r.u8()? {
0 => None,
1 => Some(NetLayout {
kind: get_net_kind(r)?,
shape: get_net_shape(r)?,
}),
tag => {
return Err(ImageError::BadTag {
what: "option",
tag,
});
}
};
slots.push(SlotDescriptor {
key,
offset,
initial,
net_info,
});
}
Ok(StateLayout {
slots,
inline_words,
wide_words,
})
}
fn put_key(w: &mut Writer, k: &TemplateKey) {
w.u32(k.definition);
w.u64(k.params_hash);
}
fn get_key(r: &mut Reader<'_>) -> Result<TemplateKey, ImageError> {
Ok(TemplateKey {
definition: r.u32()?,
params_hash: r.u64()?,
})
}
pub fn put_templates(w: &mut Writer, t: &[TemplateImage]) {
w.len(t.len());
for t in t {
put_key(w, &t.key);
put_layout(w, &t.layout);
}
}
pub fn get_templates(r: &mut Reader<'_>) -> Result<Vec<TemplateImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
Ok(TemplateImage {
key: get_key(r)?,
layout: get_layout(r)?,
})
})
.collect()
}
pub fn put_instances(w: &mut Writer, v: &[InstanceImage]) {
w.len(v.len());
for i in v {
w.u32(i.id.0);
put_key(w, &i.template);
w.u8(match i.kind {
InstanceKind::Elaborated => 0,
InstanceKind::Package => 1,
InstanceKind::Bank => 2,
});
w.str(&i.path);
}
}
pub fn get_instances(r: &mut Reader<'_>) -> Result<Vec<InstanceImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
Ok(InstanceImage {
id: InstanceId(r.u32()?),
template: get_key(r)?,
kind: match r.u8()? {
0 => InstanceKind::Elaborated,
1 => InstanceKind::Package,
2 => InstanceKind::Bank,
tag => {
return Err(ImageError::BadTag {
what: "instance kind",
tag,
});
}
},
path: r.str()?,
})
})
.collect()
}
fn put_alias_window(w: &mut Writer, window: Option<crate::state::AliasWindow>) {
match window {
None => w.u8(0),
Some(crate::state::AliasWindow { row, lo, width }) => {
w.u8(1);
put_opt_u64(w, row);
w.u32(lo);
w.u32(width);
}
}
}
fn get_alias_window(r: &mut Reader<'_>) -> Result<Option<crate::state::AliasWindow>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => {
let row = get_opt_u64(r, "ForeignImage target window row")?;
let lo = r.u32()?;
let width = r.u32()?;
Ok(Some(crate::state::AliasWindow { row, lo, width }))
}
tag => Err(ImageError::BadTag {
what: "ForeignImage target window",
tag,
}),
}
}
pub fn put_foreign(w: &mut Writer, v: &[ForeignImage]) {
w.len(v.len());
for f in v {
w.u32(f.instance.0);
w.u32(f.own_slots);
w.len(f.targets.len());
for (owner, slot, window) in &f.targets {
w.u32(owner.0);
w.u32(*slot);
put_alias_window(w, *window);
}
}
}
pub fn get_foreign(r: &mut Reader<'_>) -> Result<Vec<ForeignImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let instance = InstanceId(r.u32()?);
let own_slots = r.u32()?;
let m = r.len()?;
let targets = (0..m)
.map(|_| Ok((InstanceId(r.u32()?), r.u32()?, get_alias_window(r)?)))
.collect::<Result<_, ImageError>>()?;
Ok(ForeignImage {
instance,
own_slots,
targets,
})
})
.collect()
}
pub fn put_x_windows(w: &mut Writer, v: &[XWindow]) {
w.len(v.len());
for x in v {
w.u32(x.instance.0);
w.u32(x.slot);
put_four_state(w, &x.value);
}
}
pub fn get_x_windows(r: &mut Reader<'_>) -> Result<Vec<XWindow>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
Ok(XWindow {
instance: InstanceId(r.u32()?),
slot: r.u32()?,
value: get_four_state(r)?,
})
})
.collect()
}
fn put_place(w: &mut Writer, p: &PlaceImage) {
w.u32(p.instance.0);
w.u32(p.slot);
w.u32(p.width);
match p.class {
PlaceClass::Inline => w.u8(0),
PlaceClass::Wide => w.u8(1),
PlaceClass::Memory { rows } => {
w.u8(2);
w.u32(rows);
}
}
}
fn get_place(r: &mut Reader<'_>) -> Result<PlaceImage, ImageError> {
let instance = InstanceId(r.u32()?);
let slot = r.u32()?;
let width = r.u32()?;
let class = match r.u8()? {
0 => PlaceClass::Inline,
1 => PlaceClass::Wide,
2 => PlaceClass::Memory { rows: r.u32()? },
tag => {
return Err(ImageError::BadTag {
what: "place class",
tag,
});
}
};
Ok(PlaceImage {
instance,
slot,
width,
class,
})
}
fn put_range(w: &mut Writer, r: Option<(u32, u32)>) {
match r {
None => w.u8(0),
Some((lo, width)) => {
w.u8(1);
w.u32(lo);
w.u32(width);
}
}
}
fn get_range(r: &mut Reader<'_>) -> Result<Option<(u32, u32)>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => Ok(Some((r.u32()?, r.u32()?))),
tag => Err(ImageError::BadTag { what: "range", tag }),
}
}
fn put_opt_u32(w: &mut Writer, v: Option<u32>) {
match v {
None => w.u8(0),
Some(x) => {
w.u8(1);
w.u32(x);
}
}
}
fn get_opt_u32(r: &mut Reader<'_>) -> Result<Option<u32>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => Ok(Some(r.u32()?)),
tag => Err(ImageError::BadTag {
what: "optional u32",
tag,
}),
}
}
fn put_strength(w: &mut Writer, s: DriveStrength) {
w.u8(match s {
DriveStrength::Supply => 0,
DriveStrength::Strong => 1,
DriveStrength::Pull => 2,
DriveStrength::Weak => 3,
DriveStrength::HighZ => 4,
});
}
fn get_strength(r: &mut Reader<'_>) -> Result<DriveStrength, ImageError> {
match r.u8()? {
0 => Ok(DriveStrength::Supply),
1 => Ok(DriveStrength::Strong),
2 => Ok(DriveStrength::Pull),
3 => Ok(DriveStrength::Weak),
4 => Ok(DriveStrength::HighZ),
tag => Err(ImageError::BadTag {
what: "drive strength",
tag,
}),
}
}
pub fn put_wires(w: &mut Writer, v: &[WireImage]) {
w.len(v.len());
for x in v {
put_place(w, &x.src);
put_range(w, x.src_range);
put_place(w, &x.dst);
put_range(w, x.dst_range);
w.bool(x.ranged);
}
}
pub fn get_wires(r: &mut Reader<'_>) -> Result<Vec<WireImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
Ok(WireImage {
src: get_place(r)?,
src_range: get_range(r)?,
dst: get_place(r)?,
dst_range: get_range(r)?,
ranged: r.bool()?,
})
})
.collect()
}
pub fn put_groups(w: &mut Writer, v: &[GroupImage]) {
w.len(v.len());
for g in v {
put_net_kind(w, g.kind);
w.u32(g.width);
w.len(g.nets.len());
for (inst, slot) in &g.nets {
w.u32(inst.0);
w.u32(*slot);
}
w.len(g.drivers.len());
for d in &g.drivers {
w.u32(d.slot);
put_strength(w, d.strength.s0);
put_strength(w, d.strength.s1);
put_opt_u32(w, d.ambiguous);
match &d.src {
None => w.u8(0),
Some(x) => {
w.u8(1);
w.u64(x.key);
w.u32(x.bit);
w.u32(x.out_bit);
w.bool(x.resistive);
w.u32(x.tick);
}
}
}
w.bool(g.switched);
match g.decay {
None => w.u8(0),
Some(t) => {
w.u8(1);
w.u64(t);
}
}
w.len(g.strength_keys.len());
for k in &g.strength_keys {
w.u64(*k);
}
w.u8(g.charge);
put_opt_u32(w, g.strength_tick);
w.len(g.sides.len());
for sd in &g.sides {
w.u64(sd.key);
w.len(sd.inside.len());
for &b in &sd.inside {
w.bool(b);
}
}
}
}
pub fn get_groups(r: &mut Reader<'_>) -> Result<Vec<GroupImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let kind = get_net_kind(r)?;
let width = r.u32()?;
let m = r.len()?;
let nets = (0..m)
.map(|_| Ok((InstanceId(r.u32()?), r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let drivers = (0..m)
.map(|_| {
Ok(DriverImage {
slot: r.u32()?,
strength: DriveStrengthPair {
s0: get_strength(r)?,
s1: get_strength(r)?,
},
ambiguous: get_opt_u32(r)?,
src: match r.u8()? {
0 => None,
1 => Some(StrengthSrc {
key: r.u64()?,
bit: r.u32()?,
out_bit: r.u32()?,
resistive: r.bool()?,
tick: r.u32()?,
}),
tag => {
return Err(ImageError::BadTag {
what: "strength source",
tag,
});
}
},
})
})
.collect::<Result<_, ImageError>>()?;
let switched = r.bool()?;
let decay = match r.u8()? {
0 => None,
1 => Some(r.u64()?),
tag => {
return Err(ImageError::BadTag {
what: "trireg decay",
tag,
});
}
};
let m = r.len()?;
let strength_keys = (0..m).map(|_| r.u64()).collect::<Result<_, ImageError>>()?;
Ok(GroupImage {
kind,
width,
nets,
drivers,
switched,
decay,
strength_keys,
charge: r.u8()?,
strength_tick: get_opt_u32(r)?,
sides: {
let m = r.len()?;
(0..m)
.map(|_| {
let key = r.u64()?;
let k = r.len()?;
let inside = (0..k).map(|_| r.bool()).collect::<Result<_, _>>()?;
Ok(SideSpec { key, inside })
})
.collect::<Result<_, ImageError>>()?
},
})
})
.collect()
}
fn put_conducts(w: &mut Writer, c: &ConductsImage) {
match c {
ConductsImage::Always => w.u8(0),
ConductsImage::When(ctrl, want) => {
w.u8(1);
match ctrl {
ControlImage::Bit {
instance,
slot,
width,
bit,
} => {
w.u8(0);
w.u32(instance.0);
w.u32(*slot);
w.u32(*width);
w.u32(*bit);
}
ControlImage::Const(v) => {
w.u8(1);
w.u8(*v);
}
}
w.bool(*want);
}
}
}
fn get_conducts(r: &mut Reader<'_>) -> Result<ConductsImage, ImageError> {
match r.u8()? {
0 => Ok(ConductsImage::Always),
1 => {
let ctrl = match r.u8()? {
0 => ControlImage::Bit {
instance: InstanceId(r.u32()?),
slot: r.u32()?,
width: r.u32()?,
bit: r.u32()?,
},
1 => ControlImage::Const(r.u8()?),
tag => {
return Err(ImageError::BadTag {
what: "switch control",
tag,
});
}
};
Ok(ConductsImage::When(ctrl, r.bool()?))
}
tag => Err(ImageError::BadTag {
what: "switch conduction",
tag,
}),
}
}
pub fn put_networks(w: &mut Writer, v: &[NetworkImage]) {
w.len(v.len());
for n in v {
w.len(n.groups.len());
for g in &n.groups {
w.u32(*g);
}
w.len(n.switches.len());
for s in &n.switches {
w.u32(s.a.0);
w.u32(s.a.1);
w.u32(s.b.0);
w.u32(s.b.1);
put_conducts(w, &s.conducts);
w.bool(s.resistive);
}
}
}
pub fn get_networks(r: &mut Reader<'_>) -> Result<Vec<NetworkImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let m = r.len()?;
let groups = (0..m).map(|_| r.u32()).collect::<Result<_, _>>()?;
let m = r.len()?;
let switches = (0..m)
.map(|_| {
Ok(SwitchImage {
a: (r.u32()?, r.u32()?),
b: (r.u32()?, r.u32()?),
conducts: get_conducts(r)?,
resistive: r.bool()?,
})
})
.collect::<Result<_, ImageError>>()?;
Ok(NetworkImage { groups, switches })
})
.collect()
}
pub fn put_symbols(w: &mut Writer, v: &[String]) {
w.len(v.len());
for s in v {
w.str(s);
}
}
pub fn get_symbols(r: &mut Reader<'_>) -> Result<Vec<String>, ImageError> {
let n = r.len()?;
(0..n).map(|_| r.str()).collect()
}
pub fn put_kernels(w: &mut Writer, v: &[KernelImage]) {
w.len(v.len());
for k in v {
w.u32(k.symbol);
w.u32(k.frame_words);
put_process_kind(w, k.kind);
w.u32(k.instance.0);
w.u8(match k.role {
KernelRole::Process => 0,
KernelRole::ContAssign => 1,
KernelRole::VarInit => 2,
});
w.bool(k.level_head);
w.bool(k.reactive);
w.bool(k.program_initial);
w.bool(k.registered);
w.len(k.read_set.len());
for s in &k.read_set {
w.u32(*s);
}
w.len(k.read_windows.len());
for (slot, lsb, width) in &k.read_windows {
w.u32(*slot);
w.u32(*lsb);
w.u32(*width);
}
match k.reset {
None => w.u8(0),
Some((slot, edge)) => {
w.u8(1);
w.u32(slot);
put_edge(w, edge);
}
}
w.len(k.disable_scopes.len());
for (resume, label, exit) in &k.disable_scopes {
w.u32(*resume);
w.str(label);
w.u32(*exit);
}
}
}
pub fn get_kernels(r: &mut Reader<'_>) -> Result<Vec<KernelImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let symbol = r.u32()?;
let frame_words = r.u32()?;
let kind = get_process_kind(r)?;
let instance = InstanceId(r.u32()?);
let role = match r.u8()? {
0 => KernelRole::Process,
1 => KernelRole::ContAssign,
2 => KernelRole::VarInit,
tag => {
return Err(ImageError::BadTag {
what: "kernel role",
tag,
});
}
};
let level_head = r.bool()?;
let reactive = r.bool()?;
let program_initial = r.bool()?;
let registered = r.bool()?;
let m = r.len()?;
let read_set = (0..m).map(|_| r.u32()).collect::<Result<_, _>>()?;
let m = r.len()?;
let read_windows = (0..m)
.map(|_| Ok((r.u32()?, r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let reset = match r.u8()? {
0 => None,
1 => Some((r.u32()?, get_edge(r)?)),
tag => {
return Err(ImageError::BadTag {
what: "option",
tag,
});
}
};
let m = r.len()?;
let disable_scopes = (0..m)
.map(|_| Ok((r.u32()?, r.str()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
Ok(KernelImage {
symbol,
frame_words,
kind,
instance,
role,
level_head,
reactive,
program_initial,
registered,
read_set,
read_windows,
reset,
disable_scopes,
})
})
.collect()
}
pub fn put_fork_table(w: &mut Writer, v: &[SpawnSpec]) {
w.len(v.len());
for s in v {
w.u32(s.kernel);
w.u32(s.entry_pc);
w.u32(s.parent);
w.len(s.disable_name.len());
for n in &s.disable_name {
w.str(n);
}
}
}
pub fn get_fork_table(r: &mut Reader<'_>) -> Result<Vec<SpawnSpec>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let kernel = r.u32()?;
let entry_pc = r.u32()?;
let parent = r.u32()?;
let m = r.len()?;
let disable_name = (0..m).map(|_| r.str()).collect::<Result<_, _>>()?;
Ok(SpawnSpec {
kernel,
entry_pc,
parent,
disable_name,
})
})
.collect()
}
pub fn put_fork_branches(w: &mut Writer, v: &[ForkBranchImage]) {
w.len(v.len());
for b in v {
w.u32(b.symbol);
w.u32(b.frame_words);
w.len(b.captures.len());
for (p, c) in &b.captures {
w.u32(*p);
w.u32(*c);
}
w.len(b.disable_scopes.len());
for (resume, label, exit) in &b.disable_scopes {
w.u32(*resume);
w.str(label);
w.u32(*exit);
}
}
}
pub fn get_fork_branches(r: &mut Reader<'_>) -> Result<Vec<ForkBranchImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let symbol = r.u32()?;
let frame_words = r.u32()?;
let m = r.len()?;
let captures = (0..m)
.map(|_| Ok((r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let disable_scopes = (0..m)
.map(|_| Ok((r.u32()?, r.str()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
Ok(ForkBranchImage {
symbol,
frame_words,
captures,
disable_scopes,
})
})
.collect()
}
pub fn put_wait_table(w: &mut Writer, v: &[WaitTableEntry]) {
w.len(v.len());
for e in v {
w.u32(e.slot);
put_opt_edge(w, e.edge);
match e.select {
None => w.u8(0),
Some((lsb, width)) => {
w.u8(1);
w.u32(lsb);
w.u32(width);
}
}
}
}
pub fn get_wait_table(r: &mut Reader<'_>) -> Result<Vec<WaitTableEntry>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let slot = r.u32()?;
let edge = get_opt_edge(r)?;
let select = match r.u8()? {
0 => None,
1 => Some((r.u32()?, r.u32()?)),
tag => {
return Err(ImageError::BadTag {
what: "WaitTableEntry::select",
tag,
});
}
};
Ok(WaitTableEntry { slot, edge, select })
})
.collect()
}
pub fn put_strings(w: &mut Writer, v: &[Vec<u8>]) {
w.len(v.len());
for s in v {
w.bytes(s);
}
}
pub fn get_strings(r: &mut Reader<'_>) -> Result<Vec<Vec<u8>>, ImageError> {
let n = r.len()?;
(0..n).map(|_| r.bytes()).collect()
}
fn put_collection_key(w: &mut Writer, k: &Key) {
match k {
Key::Int(v) => {
w.u8(0);
w.u128(*v as u128);
}
Key::Big(words) => {
w.u8(1);
w.words(words);
}
Key::Str(bytes) => {
w.u8(2);
w.bytes(bytes);
}
}
}
fn get_collection_key(r: &mut Reader<'_>) -> Result<Key, ImageError> {
match r.u8()? {
0 => Ok(Key::Int(r.u128()? as i128)),
1 => Ok(Key::Big(r.words()?)),
2 => Ok(Key::Str(r.bytes()?)),
tag => Err(ImageError::BadTag {
what: "collection key",
tag,
}),
}
}
pub fn put_collections(w: &mut Writer, v: &[FrozenData]) {
w.len(v.len());
for c in v {
match c {
FrozenData::Seq { stride, words } => {
w.u8(0);
w.len(*stride);
w.words(words);
}
FrozenData::Map { entries, default } => {
w.u8(1);
w.len(entries.len());
for (k, value) in entries {
put_collection_key(w, k);
w.words(value);
}
match default {
None => w.u8(0),
Some(d) => {
w.u8(1);
w.words(d);
}
}
}
}
}
}
pub fn get_collections(r: &mut Reader<'_>) -> Result<Vec<FrozenData>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| match r.u8()? {
0 => Ok(FrozenData::Seq {
stride: r.len()?,
words: r.words()?,
}),
1 => {
let n = r.len()?;
let entries = (0..n)
.map(|_| Ok((get_collection_key(r)?, r.words()?)))
.collect::<Result<_, ImageError>>()?;
let default = match r.u8()? {
0 => None,
1 => Some(r.words()?),
tag => {
return Err(ImageError::BadTag {
what: "collection default",
tag,
});
}
};
Ok(FrozenData::Map { entries, default })
}
tag => Err(ImageError::BadTag {
what: "collection",
tag,
}),
})
.collect()
}
fn put_field_slot(w: &mut Writer, s: &FieldSlot) {
w.u64(s.offset as u64);
w.u64(s.words as u64);
}
fn get_field_slot(r: &mut Reader<'_>) -> Result<FieldSlot, ImageError> {
Ok(FieldSlot {
offset: r.u64()? as usize,
words: r.u64()? as usize,
})
}
pub fn put_classes(w: &mut Writer, v: &[TemplateSnapshot]) {
w.len(v.len());
for t in v {
w.u32(t.id.0);
w.len(t.handle_fields.len());
t.handle_fields.iter().for_each(|s| put_field_slot(w, s));
w.len(t.collection_fields.len());
t.collection_fields
.iter()
.for_each(|s| put_field_slot(w, s));
w.len(t.nested_handles.len());
for n in &t.nested_handles {
put_field_slot(w, &n.slot);
w.u32(n.bit);
w.bool(n.coll);
}
w.len(t.ancestors.len());
for a in &t.ancestors {
w.u32(a.0);
}
w.len(t.rand_props.len());
for p in &t.rand_props {
put_field_slot(w, &p.slot);
w.u8(match p.kind {
RandKind::Rand => 0,
RandKind::RandC => 1,
});
}
}
}
pub fn get_classes(r: &mut Reader<'_>) -> Result<Vec<TemplateSnapshot>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let id = ClassTemplateId(r.u32()?);
let n = r.len()?;
let handle_fields = (0..n)
.map(|_| get_field_slot(r))
.collect::<Result<_, _>>()?;
let n = r.len()?;
let collection_fields = (0..n)
.map(|_| get_field_slot(r))
.collect::<Result<_, _>>()?;
let n = r.len()?;
let nested_handles = (0..n)
.map(|_| {
Ok(NestedHandle {
slot: get_field_slot(r)?,
bit: r.u32()?,
coll: r.bool()?,
})
})
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let ancestors = (0..n)
.map(|_| Ok(ClassTemplateId(r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let rand_props = (0..n)
.map(|_| {
let slot = get_field_slot(r)?;
let kind = match r.u8()? {
0 => RandKind::Rand,
1 => RandKind::RandC,
tag => {
return Err(ImageError::BadTag {
what: "rand kind",
tag,
});
}
};
Ok(RandProp { slot, kind })
})
.collect::<Result<_, ImageError>>()?;
Ok(TemplateSnapshot {
id,
handle_fields,
collection_fields,
nested_handles,
ancestors,
rand_props,
})
})
.collect()
}
pub fn put_stmt_sites(w: &mut Writer, v: &[StmtSiteImage]) {
w.len(v.len());
for s in v {
w.str(&s.def);
w.u32(s.buffer);
w.u64(s.start);
w.u64(s.end);
w.u32(u32::from(s.kind));
w.u32(s.nest);
}
}
pub fn get_stmt_sites(r: &mut Reader<'_>) -> Result<Vec<StmtSiteImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
Ok(StmtSiteImage {
def: r.str()?,
buffer: r.u32()?,
start: r.u64()?,
end: r.u64()?,
kind: r.u32()? as u16,
nest: r.u32()?,
})
})
.collect()
}
pub fn put_fsms(w: &mut Writer, v: &[FsmImage]) {
w.len(v.len());
for f in v {
w.u32(f.instance.0);
w.str(&f.name);
w.len(f.parts.len());
for p in &f.parts {
w.u32(p.slot);
w.u32(p.msb);
w.u32(p.lsb);
}
w.len(f.states.len());
for (n, val) in &f.states {
w.str(n);
w.u64(*val);
}
}
}
pub fn get_fsms(r: &mut Reader<'_>) -> Result<Vec<FsmImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let instance = InstanceId(r.u32()?);
let name = r.str()?;
let n = r.len()?;
let parts = (0..n)
.map(|_| {
Ok(FsmPartImage {
slot: r.u32()?,
msb: r.u32()?,
lsb: r.u32()?,
})
})
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let states = (0..n)
.map(|_| Ok((r.str()?, r.u64()?)))
.collect::<Result<_, ImageError>>()?;
Ok(FsmImage {
instance,
name,
parts,
states,
})
})
.collect()
}
pub fn put_dump_signals(w: &mut Writer, v: &[DumpSignalImage]) {
w.len(v.len());
for s in v {
w.u32(s.instance.0);
w.str(&s.def_name);
w.len(s.scope.len());
for name in &s.scope {
w.str(name);
}
w.len(s.scope_kinds.len());
for kind in &s.scope_kinds {
w.u8(match kind {
ScopeKind::Module => 0,
ScopeKind::Begin => 1,
ScopeKind::Fork => 2,
ScopeKind::Task => 3,
ScopeKind::Function => 4,
});
}
w.str(&s.name);
w.u32(s.width);
w.u8(VcdVarType::ALL
.iter()
.position(|t| *t == s.var_type)
.unwrap() as u8);
w.u32(s.slot);
w.bool(s.wide);
w.bool(s.real);
w.bool(s.class_handle);
w.u8(match s.port {
None => 0,
Some(PortDir::Input) => 1,
Some(PortDir::Output) => 2,
Some(PortDir::Inout) => 3,
});
put_opt_u64(w, s.strength_key);
put_opt_u64(w, s.side_key);
}
}
pub fn get_dump_signals(r: &mut Reader<'_>) -> Result<Vec<DumpSignalImage>, ImageError> {
let n = r.len()?;
(0..n)
.map(|_| {
let instance = InstanceId(r.u32()?);
let def_name = r.str()?;
let n = r.len()?;
let scope = (0..n).map(|_| r.str()).collect::<Result<_, _>>()?;
let n = r.len()?;
let scope_kinds = (0..n)
.map(|_| {
Ok(match r.u8()? {
0 => ScopeKind::Module,
1 => ScopeKind::Begin,
2 => ScopeKind::Fork,
3 => ScopeKind::Task,
4 => ScopeKind::Function,
tag => {
return Err(ImageError::BadTag {
what: "scope kind",
tag,
});
}
})
})
.collect::<Result<_, ImageError>>()?;
Ok(DumpSignalImage {
instance,
def_name,
scope,
scope_kinds,
name: r.str()?,
width: r.u32()?,
var_type: {
let tag = r.u8()?;
*VcdVarType::ALL
.get(tag as usize)
.ok_or(ImageError::BadTag {
what: "vcd var type",
tag,
})?
},
slot: r.u32()?,
wide: r.bool()?,
real: r.bool()?,
class_handle: r.bool()?,
port: match r.u8()? {
0 => None,
1 => Some(PortDir::Input),
2 => Some(PortDir::Output),
3 => Some(PortDir::Inout),
tag => {
return Err(ImageError::BadTag {
what: "port direction",
tag,
});
}
},
strength_key: get_opt_u64(r, "port strength key")?,
side_key: get_opt_u64(r, "port side key")?,
})
})
.collect()
}
pub fn put_dpi(w: &mut Writer, v: &DpiImage) {
w.len(v.imports.len());
for name in &v.imports {
w.str(name);
}
w.len(v.exports.len());
for (name, symbol) in &v.exports {
w.str(name);
w.u32(*symbol);
}
w.len(v.variants.len());
for (name, scope, variant) in &v.variants {
w.str(name);
w.str(scope);
w.u32(*variant);
}
w.bool(v.task_fibers);
}
pub fn get_dpi(r: &mut Reader<'_>) -> Result<DpiImage, ImageError> {
let n = r.len()?;
let imports = (0..n).map(|_| r.str()).collect::<Result<_, _>>()?;
let n = r.len()?;
let exports = (0..n)
.map(|_| Ok((r.str()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let variants = (0..n)
.map(|_| Ok((r.str()?, r.str()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
Ok(DpiImage {
imports,
exports,
variants,
task_fibers: r.bool()?,
})
}
fn put_opt_u64(w: &mut Writer, v: Option<u64>) {
match v {
None => w.u8(0),
Some(x) => {
w.u8(1);
w.u64(x);
}
}
}
fn get_opt_u64(r: &mut Reader<'_>, what: &'static str) -> Result<Option<u64>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => Ok(Some(r.u64()?)),
tag => Err(ImageError::BadTag { what, tag }),
}
}
fn put_opt_u8(w: &mut Writer, v: Option<u8>) {
match v {
None => w.u8(0),
Some(x) => {
w.u8(1);
w.u8(x);
}
}
}
fn get_opt_enum(r: &mut Reader<'_>, what: &'static str, max: u8) -> Result<Option<u8>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => {
let v = r.u8()?;
if v > max {
return Err(ImageError::BadTag { what, tag: v });
}
Ok(Some(v))
}
tag => Err(ImageError::BadTag { what, tag }),
}
}
pub fn put_timing(w: &mut Writer, v: &TimingImage) {
let o = &v.options;
w.bool(o.per_bit_vectors);
w.bool(o.negative_timing_checks);
put_opt_u64(w, o.reject_limit_pct);
put_opt_u64(w, o.error_limit_pct);
put_opt_u8(w, o.pulse_style);
put_opt_u8(w, o.showcancelled.map(u8::from));
put_opt_u8(w, o.sdf_mtm);
w.bytes(&v.plan);
}
pub fn get_timing(r: &mut Reader<'_>) -> Result<TimingImage, ImageError> {
let per_bit_vectors = r.bool()?;
let negative_timing_checks = r.bool()?;
let reject_limit_pct = get_opt_u64(r, "reject limit")?;
let error_limit_pct = get_opt_u64(r, "error limit")?;
let pulse_style = get_opt_enum(r, "pulse style", 1)?;
let showcancelled = get_opt_enum(r, "showcancelled", 1)?.map(|v| v == 1);
let sdf_mtm = get_opt_enum(r, "sdf mtm", 2)?;
Ok(TimingImage {
options: TimingOptionsImage {
per_bit_vectors,
negative_timing_checks,
reject_limit_pct,
error_limit_pct,
pulse_style,
showcancelled,
sdf_mtm,
},
plan: r.bytes()?,
})
}
fn put_assert_kind(w: &mut Writer, k: AssertKind) {
w.u8(match k {
AssertKind::Assert => 0,
AssertKind::Assume => 1,
AssertKind::CoverProperty => 2,
AssertKind::CoverSequence => 3,
AssertKind::Restrict => 4,
AssertKind::Expect => 5,
});
}
fn get_assert_kind(r: &mut Reader<'_>) -> Result<AssertKind, ImageError> {
Ok(match r.u8()? {
0 => AssertKind::Assert,
1 => AssertKind::Assume,
2 => AssertKind::CoverProperty,
3 => AssertKind::CoverSequence,
4 => AssertKind::Restrict,
5 => AssertKind::Expect,
tag => {
return Err(ImageError::BadTag {
what: "assertion kind",
tag,
});
}
})
}
fn put_site(w: &mut Writer, s: &Option<SiteDesc>) {
match s {
None => w.u8(0),
Some(SiteDesc {
assertion_type,
directive_type,
scope,
label,
}) => {
w.u8(1);
w.u32(*assertion_type);
w.u32(*directive_type);
w.str(scope);
w.str(label);
}
}
}
fn get_site(r: &mut Reader<'_>) -> Result<Option<SiteDesc>, ImageError> {
match r.u8()? {
0 => Ok(None),
1 => Ok(Some(SiteDesc {
assertion_type: r.u32()?,
directive_type: r.u32()?,
scope: r.str()?,
label: r.str()?,
})),
tag => Err(ImageError::BadTag {
what: "assertion site",
tag,
}),
}
}
fn put_u32_pairs(w: &mut Writer, v: &[(u32, u32)]) {
w.len(v.len());
for (a, b) in v {
w.u32(*a);
w.u32(*b);
}
}
fn get_u32_pairs(r: &mut Reader<'_>) -> Result<Vec<(u32, u32)>, ImageError> {
let n = r.len()?;
(0..n).map(|_| Ok((r.u32()?, r.u32()?))).collect()
}
fn put_assertion(w: &mut Writer, a: &AssertionImage) {
let AssertionImage {
plan_index,
instance,
kernel,
frame_words,
label,
kind,
ir,
n_clocks,
hist_clocks,
leaf_reads,
leaf_writes,
activated,
captured,
disable_mixed,
tick_history,
history_depth,
future,
future_clock,
src_offset,
leaf_src,
row_len,
snap,
ticks,
leaves,
live_leaves,
cases,
site,
end_cell,
current_words,
observed_words,
coll_words,
coll_nests,
spawn_calls,
} = a;
w.u32(*plan_index);
w.u32(instance.0);
w.u32(*kernel);
w.u32(*frame_words);
w.str(label);
put_assert_kind(w, *kind);
crate::sva::codec::put_property_ir(w, ir);
w.u32(*n_clocks);
w.u32(*hist_clocks);
w.len(leaf_reads.len());
for reads in leaf_reads {
w.len(reads.len());
for v in reads {
w.u32(v.0);
}
}
w.len(leaf_writes.len());
for v in leaf_writes {
put_opt_u32(w, v.map(|v| v.0));
}
w.bool(*activated);
w.len(captured.len());
for v in captured {
w.u32(v.0);
}
w.bool(*disable_mixed);
w.bool(*tick_history);
w.u32(*history_depth);
w.bool(*future);
w.u32(*future_clock);
put_opt_u64(w, *src_offset);
w.len(leaf_src.len());
for (buf, lo, hi) in leaf_src {
w.u32(*buf);
w.u64(*lo);
w.u64(*hi);
}
w.u32(*row_len);
w.u32(*snap);
w.len(ticks.len());
for t in ticks {
w.u32(*t);
}
w.len(leaves.len());
for l in leaves {
put_opt_u32(w, *l);
}
w.len(live_leaves.len());
for l in live_leaves {
put_opt_u32(w, *l);
}
w.len(cases.len());
for CasePairImage {
selector,
label,
symbol,
} in cases
{
w.u32(selector.0);
w.u32(label.0);
w.u32(*symbol);
}
put_site(w, site);
put_opt_u32(w, *end_cell);
w.len(current_words.len());
for (id, base, n) in current_words {
w.str(id);
w.u32(*base);
w.u32(*n);
}
w.len(observed_words.len());
for (base, n) in observed_words {
w.u32(*base);
w.u32(*n);
}
w.len(coll_words.len());
for base in coll_words {
w.u32(*base);
}
w.len(coll_nests.len());
for n in coll_nests {
put_nest(w, n);
}
w.len(spawn_calls.len());
for c in spawn_calls {
w.u32(c.call);
w.u32(c.kernel);
w.u32(c.frame_words);
w.u32(c.entry_pc);
}
}
fn get_assertion(r: &mut Reader<'_>) -> Result<AssertionImage, ImageError> {
let plan_index = r.u32()?;
let instance = InstanceId(r.u32()?);
let kernel = r.u32()?;
let frame_words = r.u32()?;
let label = r.str()?;
let kind = get_assert_kind(r)?;
let ir = crate::sva::codec::get_property_ir(r)?;
let n_clocks = r.u32()?;
let hist_clocks = r.u32()?;
let n = r.len()?;
let mut leaf_reads = Vec::with_capacity(n);
for _ in 0..n {
let m = r.len()?;
leaf_reads.push(
(0..m)
.map(|_| r.u32().map(LocalVarId))
.collect::<Result<_, _>>()?,
);
}
let n = r.len()?;
let leaf_writes = (0..n)
.map(|_| get_opt_u32(r).map(|v| v.map(LocalVarId)))
.collect::<Result<_, _>>()?;
let activated = r.bool()?;
let n = r.len()?;
let captured = (0..n)
.map(|_| r.u32().map(LocalVarId))
.collect::<Result<_, _>>()?;
let disable_mixed = r.bool()?;
let tick_history = r.bool()?;
let history_depth = r.u32()?;
let future = r.bool()?;
let future_clock = r.u32()?;
let src_offset = get_opt_u64(r, "AssertionImage::src_offset")?;
let n = r.len()?;
let leaf_src = (0..n)
.map(|_| Ok((r.u32()?, r.u64()?, r.u64()?)))
.collect::<Result<_, ImageError>>()?;
let row_len = r.u32()?;
let snap = r.u32()?;
let n = r.len()?;
let ticks = (0..n).map(|_| r.u32()).collect::<Result<_, _>>()?;
let n = r.len()?;
let leaves = (0..n).map(|_| get_opt_u32(r)).collect::<Result<_, _>>()?;
let n = r.len()?;
let live_leaves = (0..n).map(|_| get_opt_u32(r)).collect::<Result<_, _>>()?;
let n = r.len()?;
let cases = (0..n)
.map(|_| {
Ok(CasePairImage {
selector: LeafId(r.u32()?),
label: LeafId(r.u32()?),
symbol: r.u32()?,
})
})
.collect::<Result<_, ImageError>>()?;
let site = get_site(r)?;
let end_cell = get_opt_u32(r)?;
let n = r.len()?;
let current_words = (0..n)
.map(|_| Ok((r.str()?, r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let observed_words = (0..n)
.map(|_| Ok((r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let coll_words = (0..n).map(|_| r.u32()).collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let coll_nests = (0..n)
.map(|_| get_nest(r))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let spawn_calls = (0..n)
.map(|_| {
Ok(SpawnCallImage {
call: r.u32()?,
kernel: r.u32()?,
frame_words: r.u32()?,
entry_pc: r.u32()?,
})
})
.collect::<Result<_, ImageError>>()?;
Ok(AssertionImage {
plan_index,
instance,
kernel,
frame_words,
label,
kind,
ir,
n_clocks,
hist_clocks,
leaf_reads,
leaf_writes,
activated,
captured,
disable_mixed,
tick_history,
history_depth,
future,
future_clock,
src_offset,
leaf_src,
row_len,
snap,
ticks,
leaves,
live_leaves,
cases,
site,
end_cell,
current_words,
observed_words,
coll_words,
coll_nests,
spawn_calls,
})
}
pub fn put_verif(w: &mut Writer, v: &VerifImage) {
let VerifImage {
assertions,
free_groups,
sampled,
clocking,
covergroups,
} = v;
w.len(assertions.len());
for a in assertions {
put_assertion(w, a);
}
w.len(free_groups.len());
for FreeGroupImage {
instance,
vars,
rng,
} in free_groups
{
w.u32(instance.0);
w.len(vars.len());
for FreeVarImage {
id,
slot,
width,
wide,
} in vars
{
w.str(id);
w.u32(*slot);
w.u32(*width);
w.bool(*wide);
}
w.u64(*rng);
}
w.len(sampled.len());
for SampledImage {
instance,
sites,
p_copies,
} in sampled
{
w.u32(instance.0);
w.len(sites.len());
for SampledSiteImage { guard, shifts } in sites {
w.u32(*guard);
put_u32_pairs(w, shifts);
}
put_u32_pairs(w, p_copies);
}
w.len(clocking.len());
for ClockingImage {
instance,
terms,
gates,
samples,
skews,
parts,
counter,
term_owners,
} in clocking
{
w.u32(instance.0);
w.len(term_owners.len());
for o in term_owners {
w.u32(o.0);
}
w.len(gates.len());
for (term, g) in gates {
w.u32(*term);
w.u32(*g);
}
w.len(terms.len());
for (slot, edge) in terms {
w.u32(*slot);
put_opt_edge(w, *edge);
}
w.len(samples.len());
for (src, dst, width) in samples {
w.u32(*src);
w.u32(*dst);
w.u32(*width);
}
w.len(skews.len());
for k in skews {
w.u64(*k);
}
w.len(parts.len());
for ps in parts {
w.len(ps.len());
for (slot, width, lsb, len) in ps {
w.u32(*slot);
w.u32(*width);
w.u32(*lsb);
w.u32(*len);
}
}
w.u32(*counter);
}
w.len(covergroups.len());
for CovGroupImage {
type_index,
site,
instance,
decl,
kernels,
sampler,
class_clock,
blocks,
} in covergroups
{
w.u32(*type_index);
w.u32(*site);
w.u32(instance.0);
put_covgroup(w, decl);
w.len(kernels.len());
for k in kernels {
w.u32(*k);
}
match sampler {
None => w.u8(0),
Some(CovSamplerImage {
terms,
gates,
exprs,
strobe,
}) => {
w.u8(1);
w.len(terms.len());
for (slot, edge) in terms {
w.u32(*slot);
put_opt_edge(w, *edge);
}
w.len(gates.len());
for (term, g) in gates {
w.u32(*term);
w.u32(*g);
}
w.len(exprs.len());
for x in exprs {
w.u32(x.func);
put_opt_edge(w, x.edge);
w.len(x.wake.len());
for t in &x.wake {
w.u32(*t);
}
w.len(x.gates.len());
for g in &x.gates {
w.u32(*g);
}
}
w.bool(*strobe);
}
}
match class_clock {
None => w.u8(0),
Some(CovClassClockImage {
terms,
gates,
strobe,
}) => {
w.u8(1);
w.len(terms.len());
for (k, edge) in terms {
w.u32(*k);
put_opt_edge(w, *edge);
}
w.len(gates.len());
for (term, k) in gates {
w.u32(*term);
w.u32(*k);
}
w.bool(*strobe);
}
}
w.len(blocks.len());
for b in blocks {
w.u64(*b);
}
}
}
pub fn get_verif(r: &mut Reader<'_>) -> Result<VerifImage, ImageError> {
let n = r.len()?;
let assertions = (0..n).map(|_| get_assertion(r)).collect::<Result<_, _>>()?;
let n = r.len()?;
let mut free_groups = Vec::with_capacity(n);
for _ in 0..n {
let instance = InstanceId(r.u32()?);
let m = r.len()?;
let vars = (0..m)
.map(|_| {
Ok(FreeVarImage {
id: r.str()?,
slot: r.u32()?,
width: r.u32()?,
wide: r.bool()?,
})
})
.collect::<Result<_, ImageError>>()?;
free_groups.push(FreeGroupImage {
instance,
vars,
rng: r.u64()?,
});
}
let n = r.len()?;
let mut sampled = Vec::with_capacity(n);
for _ in 0..n {
let instance = InstanceId(r.u32()?);
let m = r.len()?;
let mut sites = Vec::with_capacity(m);
for _ in 0..m {
sites.push(SampledSiteImage {
guard: r.u32()?,
shifts: get_u32_pairs(r)?,
});
}
sampled.push(SampledImage {
instance,
sites,
p_copies: get_u32_pairs(r)?,
});
}
let n = r.len()?;
let mut clocking = Vec::with_capacity(n);
for _ in 0..n {
let instance = InstanceId(r.u32()?);
let m = r.len()?;
let term_owners = (0..m)
.map(|_| Ok(InstanceId(r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let gates = (0..m)
.map(|_| Ok((r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let terms = (0..m)
.map(|_| Ok((r.u32()?, get_opt_edge(r)?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let samples = (0..m)
.map(|_| Ok((r.u32()?, r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let skews = (0..m).map(|_| r.u64()).collect::<Result<_, ImageError>>()?;
let m = r.len()?;
let parts = (0..m)
.map(|_| {
let k = r.len()?;
(0..k)
.map(|_| Ok((r.u32()?, r.u32()?, r.u32()?, r.u32()?)))
.collect::<Result<Vec<_>, ImageError>>()
})
.collect::<Result<_, ImageError>>()?;
clocking.push(ClockingImage {
instance,
terms,
gates,
samples,
skews,
parts,
counter: r.u32()?,
term_owners,
});
}
let n = r.len()?;
let mut covergroups = Vec::with_capacity(n);
for _ in 0..n {
let type_index = r.u32()?;
let site = r.u32()?;
let instance = InstanceId(r.u32()?);
let decl = get_covgroup(r)?;
let m = r.len()?;
let kernels = (0..m).map(|_| r.u32()).collect::<Result<_, _>>()?;
let sampler = match r.u8()? {
0 => None,
1 => {
let m = r.len()?;
let terms = (0..m)
.map(|_| Ok((r.u32()?, get_opt_edge(r)?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let gates = (0..n)
.map(|_| Ok((r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let mut exprs = Vec::with_capacity(n);
for _ in 0..n {
let func = r.u32()?;
let edge = get_opt_edge(r)?;
let m = r.len()?;
let wake = (0..m).map(|_| r.u32()).collect::<Result<_, _>>()?;
let m = r.len()?;
let gates = (0..m).map(|_| r.u32()).collect::<Result<_, _>>()?;
exprs.push(CovExprTermImage {
func,
edge,
wake,
gates,
});
}
Some(CovSamplerImage {
terms,
gates,
exprs,
strobe: r.bool()?,
})
}
tag => {
return Err(ImageError::BadTag {
what: "covergroup sampler",
tag,
});
}
};
let class_clock = match r.u8()? {
0 => None,
1 => {
let m = r.len()?;
let terms = (0..m)
.map(|_| Ok((r.u32()?, get_opt_edge(r)?)))
.collect::<Result<_, ImageError>>()?;
let n = r.len()?;
let gates = (0..n)
.map(|_| Ok((r.u32()?, r.u32()?)))
.collect::<Result<_, ImageError>>()?;
Some(CovClassClockImage {
terms,
gates,
strobe: r.bool()?,
})
}
tag => {
return Err(ImageError::BadTag {
what: "covergroup class clock",
tag,
});
}
};
let m = r.len()?;
let blocks = (0..m).map(|_| r.u64()).collect::<Result<_, _>>()?;
covergroups.push(CovGroupImage {
type_index,
site,
instance,
decl,
kernels,
sampler,
class_clock,
blocks,
});
}
Ok(VerifImage {
assertions,
free_groups,
sampled,
clocking,
covergroups,
})
}
impl DesignImage {
pub fn encode(&self) -> Vec<u8> {
let mut body = Writer::default();
body.u32(self.abi_version);
body.i32(self.tick_exp);
body.bool(self.x_initial_zero);
put_templates(&mut body, &self.templates);
put_instances(&mut body, &self.instances);
put_foreign(&mut body, &self.foreign);
put_x_windows(&mut body, &self.x_windows);
put_wires(&mut body, &self.wires);
put_groups(&mut body, &self.groups);
put_networks(&mut body, &self.networks);
put_symbols(&mut body, &self.symbols);
put_kernels(&mut body, &self.kernels);
put_fork_table(&mut body, &self.fork_table);
put_fork_branches(&mut body, &self.fork_branches);
put_wait_table(&mut body, &self.wait_table);
put_strings(&mut body, &self.strings);
put_collections(&mut body, &self.collections);
put_classes(&mut body, &self.classes);
put_dump_signals(&mut body, &self.dump_signals);
match &self.stmt_cov {
Some(sites) => {
body.u8(1);
put_stmt_sites(&mut body, sites);
}
None => body.u8(0),
}
put_fsms(&mut body, &self.fsms);
put_dpi(&mut body, &self.dpi);
put_verif(&mut body, &self.verif);
put_timing(&mut body, &self.timing);
body.bytes(&self.randomize);
body.bytes(&self.native_nets);
body.bytes(&self.vpi);
let body = body.buf;
let mut out = Writer::default();
out.buf.extend_from_slice(&IMAGE_MAGIC);
out.u32(IMAGE_VERSION);
out.u64(body.len() as u64);
out.u64(fnv1a(&body));
out.buf.extend_from_slice(&body);
out.buf
}
pub fn decode(bytes: &[u8]) -> Result<DesignImage, ImageError> {
let mut r = Reader::new(bytes);
if r.take(8)? != IMAGE_MAGIC {
return Err(ImageError::BadMagic);
}
let version = r.u32()?;
if version != IMAGE_VERSION {
return Err(ImageError::BadVersion {
found: version,
expected: IMAGE_VERSION,
});
}
let body_len = r.u64()?;
let hash = r.u64()?;
if body_len > (bytes.len() - r.pos) as u64 {
return Err(ImageError::Truncated);
}
let body = r.take(body_len as usize)?;
if !r.done() {
return Err(ImageError::TrailingBytes);
}
if fnv1a(body) != hash {
return Err(ImageError::BadHash);
}
let mut r = Reader::new(body);
let image = DesignImage {
abi_version: r.u32()?,
tick_exp: r.i32()?,
x_initial_zero: r.bool()?,
templates: get_templates(&mut r)?,
instances: get_instances(&mut r)?,
foreign: get_foreign(&mut r)?,
x_windows: get_x_windows(&mut r)?,
wires: get_wires(&mut r)?,
groups: get_groups(&mut r)?,
networks: get_networks(&mut r)?,
symbols: get_symbols(&mut r)?,
kernels: get_kernels(&mut r)?,
fork_table: get_fork_table(&mut r)?,
fork_branches: get_fork_branches(&mut r)?,
wait_table: get_wait_table(&mut r)?,
strings: get_strings(&mut r)?,
collections: get_collections(&mut r)?,
classes: get_classes(&mut r)?,
dump_signals: get_dump_signals(&mut r)?,
stmt_cov: match r.u8()? {
0 => None,
1 => Some(get_stmt_sites(&mut r)?),
tag => {
return Err(ImageError::BadTag {
what: "statement coverage",
tag,
});
}
},
fsms: get_fsms(&mut r)?,
dpi: get_dpi(&mut r)?,
verif: get_verif(&mut r)?,
timing: get_timing(&mut r)?,
randomize: r.bytes()?,
native_nets: r.bytes()?,
vpi: r.bytes()?,
};
if !r.done() {
return Err(ImageError::TrailingBytes);
}
image.validate()?;
Ok(image)
}
pub fn validate(&self) -> Result<(), ImageError> {
let sym = |i: u32| (i as usize) < self.symbols.len();
let inst = |id: InstanceId| self.instances.iter().any(|i| i.id == id);
for i in &self.instances {
if self.layout_of(&i.template).is_none() {
return Err(ImageError::BadReference("instance template"));
}
}
for k in &self.kernels {
if !sym(k.symbol) {
return Err(ImageError::BadReference("kernel symbol"));
}
if !inst(k.instance) {
return Err(ImageError::BadReference("kernel instance"));
}
}
for (_, symbol) in &self.dpi.exports {
if !sym(*symbol) {
return Err(ImageError::BadReference("DPI export symbol"));
}
}
for a in &self.verif.assertions {
let cases_ok = a.cases.iter().all(|c| sym(c.symbol));
let leaves_ok = a.leaves.iter().flatten().all(|&l| sym(l));
if !inst(a.instance) {
return Err(ImageError::BadReference("assertion instance"));
}
if !sym(a.kernel) || !sym(a.snap) || !a.ticks.iter().all(|&t| sym(t)) {
return Err(ImageError::BadReference("assertion symbol"));
}
if !cases_ok || !leaves_ok {
return Err(ImageError::BadReference("assertion leaf symbol"));
}
}
for g in &self.verif.free_groups {
if !inst(g.instance) {
return Err(ImageError::BadReference("free variable instance"));
}
}
for s in &self.verif.sampled {
if !inst(s.instance) {
return Err(ImageError::BadReference("sampled value instance"));
}
if s.sites.iter().any(|site| !sym(site.guard)) {
return Err(ImageError::BadReference("sampled guard symbol"));
}
}
for c in &self.verif.clocking {
if !inst(c.instance) {
return Err(ImageError::BadReference("clocking block instance"));
}
}
for g in &self.verif.covergroups {
if !inst(g.instance) {
return Err(ImageError::BadReference("covergroup instance"));
}
if !g.kernels.iter().all(|&k| sym(k)) {
return Err(ImageError::BadReference("covergroup kernel symbol"));
}
}
for b in &self.fork_branches {
if !sym(b.symbol) {
return Err(ImageError::BadReference("fork branch symbol"));
}
}
for s in &self.fork_table {
if s.kernel as usize >= self.fork_branches.len() {
return Err(ImageError::BadReference("fork table kernel"));
}
}
for f in &self.foreign {
if !inst(f.instance) || f.targets.iter().any(|(o, _, _)| !inst(*o)) {
return Err(ImageError::BadReference("foreign slot instance"));
}
}
for x in &self.x_windows {
if !inst(x.instance) {
return Err(ImageError::BadReference("x window instance"));
}
}
for x in &self.wires {
if !inst(x.src.instance) || !inst(x.dst.instance) {
return Err(ImageError::BadReference("wire instance"));
}
}
for g in &self.groups {
if g.nets.iter().any(|(i, _)| !inst(*i)) {
return Err(ImageError::BadReference("group net instance"));
}
}
if (!self.groups.is_empty() || !self.networks.is_empty()) && self.bank().is_none() {
return Err(ImageError::BadReference("driver bank"));
}
for n in &self.networks {
if n.groups.iter().any(|&g| g as usize >= self.groups.len()) {
return Err(ImageError::BadReference("network group"));
}
for s in &n.switches {
if s.a.0 as usize >= n.groups.len() || s.b.0 as usize >= n.groups.len() {
return Err(ImageError::BadReference("switch end"));
}
if let ConductsImage::When(ControlImage::Bit { instance, .. }, _) = s.conducts
&& !inst(instance)
{
return Err(ImageError::BadReference("switch control instance"));
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn slot(key: u32, class: SlotClass, word: u32, width: u32) -> SlotDescriptor {
SlotDescriptor {
key: SymbolKey(key),
offset: SlotOffset {
class,
word_offset: word,
bit_width: width,
elem_word_count: 1,
row_stride_words: 0,
num_rows: 0,
},
initial: None,
net_info: None,
}
}
fn sample_layout() -> StateLayout {
let mut wide = slot(1, SlotClass::Wide, 0, 100);
wide.initial = Some(FourStateValue::Wide {
aval: vec![u64::MAX, 0xf],
bval: vec![0, 3],
});
let mut net = slot(2, SlotClass::Inline, 2, 1);
net.net_info = Some(NetLayout {
kind: NetKind::TriReg,
shape: NetShape::FullStrengthLattice,
});
net.initial = Some(FourStateValue::Narrow((1u128 << 64) | 5));
StateLayout {
slots: vec![slot(0, SlotClass::Inline, 0, 8), wide, net],
inline_words: 3,
wide_words: 2,
}
}
fn key(n: u32) -> TemplateKey {
TemplateKey {
definition: n,
params_hash: u64::from(n) * 7,
}
}
fn sample_image() -> DesignImage {
let mut img = DesignImage::empty(1);
img.tick_exp = -9;
img.x_initial_zero = true;
img.templates = vec![
TemplateImage {
key: key(1),
layout: sample_layout(),
},
TemplateImage {
key: key(u32::MAX),
layout: StateLayout {
slots: vec![],
inline_words: 0,
wide_words: 0,
},
},
];
img.instances = vec![
InstanceImage {
id: InstanceId(0),
template: key(1),
kind: InstanceKind::Elaborated,
path: "top".into(),
},
InstanceImage {
id: InstanceId(u32::MAX),
template: key(u32::MAX),
kind: InstanceKind::Package,
path: String::new(),
},
InstanceImage {
id: InstanceId(u32::MAX - 1),
template: key(u32::MAX),
kind: InstanceKind::Bank,
path: String::new(),
},
];
let place = |instance: u32, slot: u32, width: u32, class: PlaceClass| PlaceImage {
instance: InstanceId(instance),
slot,
width,
class,
};
img.wires = vec![
WireImage {
src: place(0, 1, 8, PlaceClass::Inline),
src_range: None,
dst: place(u32::MAX - 1, 0, 8, PlaceClass::Inline),
dst_range: None,
ranged: false,
},
WireImage {
src: place(0, 2, 100, PlaceClass::Wide),
src_range: Some((70, 4)),
dst: place(0, 3, 4, PlaceClass::Inline),
dst_range: Some((0, 4)),
ranged: true,
},
WireImage {
src: place(0, 4, 32, PlaceClass::Memory { rows: 16 }),
src_range: None,
dst: place(0, 5, 32, PlaceClass::Memory { rows: 8 }),
dst_range: None,
ranged: false,
},
];
let weak = DriveStrengthPair {
s0: DriveStrength::Weak,
s1: DriveStrength::Pull,
};
img.groups = vec![
GroupImage {
kind: NetKind::TriReg,
width: 4,
nets: vec![(InstanceId(0), 2), (InstanceId(u32::MAX), 1)],
drivers: vec![
DriverImage {
slot: 0,
strength: weak,
ambiguous: Some(1),
src: Some(StrengthSrc {
key: 0xbeef,
bit: 1,
out_bit: 2,
resistive: true,
tick: 9,
}),
},
DriverImage {
slot: 2,
strength: DriveStrengthPair {
s0: DriveStrength::Supply,
s1: DriveStrength::Strong,
},
ambiguous: None,
src: None,
},
],
switched: false,
decay: Some(50),
strength_keys: vec![0xfeed, 7],
strength_tick: Some(5),
sides: vec![SideSpec {
key: 9,
inside: vec![true, false],
}],
charge: 6,
},
GroupImage {
kind: NetKind::Wire,
width: 1,
nets: vec![(InstanceId(0), 3)],
drivers: vec![],
switched: true,
decay: None,
strength_keys: vec![],
strength_tick: None,
sides: vec![],
charge: 4,
},
];
img.networks = vec![NetworkImage {
groups: vec![1],
switches: vec![
SwitchImage {
a: (0, 0),
b: (0, 0),
conducts: ConductsImage::Always,
resistive: false,
},
SwitchImage {
a: (0, 0),
b: (0, 0),
conducts: ConductsImage::When(
ControlImage::Bit {
instance: InstanceId(0),
slot: 1,
width: 70,
bit: 69,
},
false,
),
resistive: true,
},
SwitchImage {
a: (0, 0),
b: (0, 0),
conducts: ConductsImage::When(ControlImage::Const(2), true),
resistive: false,
},
],
}];
img.foreign = vec![ForeignImage {
instance: InstanceId(0),
own_slots: 3,
targets: vec![(InstanceId(u32::MAX), 4, None)],
}];
img.x_windows = vec![XWindow {
instance: InstanceId(0),
slot: 2,
value: FourStateValue::Narrow(0x1234),
}];
let a = img.intern("vj_proc_0");
let b = img.intern("vj_proc_1");
assert_eq!(img.intern("vj_proc_0"), a);
let c = img.intern("vj_fork_0");
img.kernels = vec![
KernelImage {
symbol: a,
frame_words: 4,
kind: ProcessKind::Always,
instance: InstanceId(0),
role: KernelRole::Process,
level_head: true,
reactive: false,
program_initial: false,
registered: true,
read_set: vec![0, 2],
read_windows: vec![(2, 1, 3)],
reset: None,
disable_scopes: vec![(3, "lp".to_owned(), 7)],
},
KernelImage {
symbol: b,
frame_words: 0,
kind: ProcessKind::AlwaysFF,
instance: InstanceId(0),
role: KernelRole::ContAssign,
level_head: false,
reactive: true,
program_initial: true,
registered: false,
read_set: vec![],
read_windows: vec![],
reset: Some((1, EdgeKind::Negedge)),
disable_scopes: vec![],
},
];
img.fork_table = vec![SpawnSpec {
kernel: 0,
entry_pc: 3,
parent: 9,
disable_name: vec!["outer".to_owned(), "inner".to_owned()],
}];
img.fork_branches = vec![ForkBranchImage {
symbol: c,
frame_words: 2,
captures: vec![(1, 2), (3, 4)],
disable_scopes: vec![(5, "blk".to_owned(), 6)],
}];
img.wait_table = vec![
WaitTableEntry {
slot: 1,
edge: None,
select: None,
},
WaitTableEntry {
slot: 2,
edge: Some(EdgeKind::Posedge),
select: Some((3, 1)),
},
];
img.strings = vec![b"hello".to_vec(), b"a b".to_vec()];
img.collections = vec![
FrozenData::Seq {
stride: 2,
words: vec![1, 2, 3, 4],
},
FrozenData::Map {
entries: vec![
(Key::Int(-5), vec![7]),
(Key::Big(vec![1, 2]), vec![8]),
(Key::Str(b"k".to_vec()), vec![9]),
],
default: Some(vec![6]),
},
FrozenData::Map {
entries: vec![],
default: None,
},
];
let slot = |offset, words| FieldSlot { offset, words };
img.classes = vec![TemplateSnapshot {
id: ClassTemplateId(3),
handle_fields: vec![slot(1, 1)],
collection_fields: vec![slot(4, 1)],
nested_handles: vec![NestedHandle {
slot: slot(6, 3),
bit: 65,
coll: true,
}],
ancestors: vec![ClassTemplateId(1), ClassTemplateId(3)],
rand_props: vec![
RandProp {
slot: slot(0, 1),
kind: RandKind::Rand,
},
RandProp {
slot: slot(2, 2),
kind: RandKind::RandC,
},
],
}];
let export_symbol = img.intern("__vj_export$sv_fn");
img.dpi = DpiImage {
imports: vec!["c_add".to_owned(), "c_puts".to_owned()],
exports: vec![("sv_fn".to_owned(), export_symbol)],
variants: vec![("sv_fn".to_owned(), "top.u1".to_owned(), 2)],
task_fibers: true,
};
img.verif = sample_verif(&mut img);
img.timing = sample_timing();
img.randomize = vec![1, 2, 3, 250];
img.native_nets = vec![7, 8];
img.vpi = vec![9, 10, 11];
img.dump_signals = vec![DumpSignalImage {
instance: InstanceId(0),
def_name: "top_def".to_owned(),
scope: vec!["top".to_owned(), "blk".to_owned()],
scope_kinds: vec![ScopeKind::Module, ScopeKind::Fork],
name: "r".to_owned(),
width: 70,
var_type: VcdVarType::Tri1,
slot: 5,
wide: true,
real: false,
class_handle: false,
port: Some(PortDir::Inout),
strength_key: Some(0xdead_beef),
side_key: Some(0xfade),
}];
img
}
fn sample_verif(img: &mut DesignImage) -> VerifImage {
use crate::sva::ir::{Automaton, ClockId, Edge, State, ThreadStateId, Transition};
use crate::sva::property::{Prop, PropDecl, PropId, PropertyIr, SeqRef, Strength};
let mut automaton = Automaton::empty();
automaton.states = vec![
State {
edges: vec![Edge {
guard: Some(LeafId(0)),
label: Transition::Delay {
min: 1,
max: Some(3),
},
target: ThreadStateId(1),
}],
},
State {
edges: vec![Edge {
guard: None,
label: Transition::Match {
actions: Vec::new(),
},
target: ThreadStateId(1),
}],
},
];
let ir = PropertyIr {
automaton,
props: vec![
Prop::Seq {
seq: SeqRef {
entry: ThreadStateId(0),
admits_empty: false,
clock: ClockId(0),
end_clock: ClockId(0),
},
strength: Some(Strength::Strong),
},
Prop::Not(PropId(0)),
],
decls: vec![PropDecl {
name: "p".to_owned(),
body: PropId(1),
}],
root: PropId(1),
disable: Some(LeafId(1)),
nv_rules: Vec::new(),
};
let kernel = img.intern("a0_action");
let snap = img.intern("a0_snap");
let tick = img.intern("a0_t0");
let leaf = img.intern("a0_l0");
let case = img.intern("a0_c0");
let guard = img.intern("vj_sva_guard_0");
VerifImage {
assertions: vec![AssertionImage {
plan_index: 3,
instance: InstanceId(0),
kernel,
frame_words: 4,
label: "top.chk".to_owned(),
kind: AssertKind::CoverSequence,
ir,
n_clocks: 1,
hist_clocks: 0,
leaf_reads: vec![vec![LocalVarId(0)], Vec::new()],
leaf_writes: vec![None, Some(LocalVarId(1))],
activated: true,
captured: vec![LocalVarId(1)],
disable_mixed: true,
tick_history: true,
history_depth: 2,
future: true,
future_clock: 0,
src_offset: Some(42),
leaf_src: vec![(0, 10, 20), (0, 30, 40)],
row_len: 5,
snap,
ticks: vec![tick],
leaves: vec![Some(leaf), None],
live_leaves: vec![None, None],
cases: vec![CasePairImage {
selector: LeafId(0),
label: LeafId(1),
symbol: case,
}],
site: Some(SiteDesc {
assertion_type: 1,
directive_type: 2,
scope: "top".to_owned(),
label: "top.chk".to_owned(),
}),
end_cell: Some(7),
current_words: vec![("top.chk.v#0".to_owned(), 2, 1)],
observed_words: vec![(1, 1)],
coll_words: vec![3],
coll_nests: vec![crate::collections::Nest {
slots: vec![(64, crate::collections::Nest::default())],
}],
spawn_calls: vec![SpawnCallImage {
call: 1,
kernel: 6,
frame_words: 5,
entry_pc: 2,
}],
}],
free_groups: vec![FreeGroupImage {
instance: InstanceId(0),
vars: vec![FreeVarImage {
id: "top.chk.v#0".to_owned(),
slot: 9,
width: 70,
wide: true,
}],
rng: 0xdead_beef_u64,
}],
sampled: vec![SampledImage {
instance: InstanceId(0),
sites: vec![SampledSiteImage {
guard,
shifts: vec![(1, 2), (2, 3)],
}],
p_copies: vec![(4, 5)],
}],
clocking: vec![ClockingImage {
instance: InstanceId(0),
terms: vec![(1, Some(EdgeKind::Posedge)), (2, None)],
gates: vec![(0, 6)],
samples: vec![(3, 4, 8)],
skews: vec![0],
parts: vec![vec![(1, 8, 2, 4)]],
counter: 5,
term_owners: vec![InstanceId(0), InstanceId(1)],
}],
covergroups: vec![
CovGroupImage {
type_index: 0,
site: 0,
instance: InstanceId(0),
decl: crate::covergroup::decl::CovGroupDecl::new("cg").coverpoint(
crate::covergroup::decl::CoverpointDecl {
name: "a".to_owned(),
ty: crate::covergroup::value::TypeInfo::unsigned(4),
expr: crate::covergroup::value::Expr::Kernel(0),
iff: None,
bins: vec![crate::covergroup::decl::BinDecl::ranges(
"b",
vec![crate::covergroup::decl::RangeItem::Single(
crate::covergroup::value::Expr::int(1),
)],
)],
enum_values: None,
options: Vec::new(),
type_options: Vec::new(),
},
),
kernels: vec![0],
sampler: Some(CovSamplerImage {
terms: vec![(1, Some(EdgeKind::Negedge)), (2, None)],
gates: vec![(1, 4)],
exprs: vec![CovExprTermImage {
func: 9,
edge: Some(EdgeKind::Posedge),
wake: vec![1, 0],
gates: vec![3],
}],
strobe: true,
}),
class_clock: Some(CovClassClockImage {
terms: vec![(0, Some(EdgeKind::Posedge)), (0, None)],
gates: vec![(1, 0)],
strobe: true,
}),
blocks: vec![7, 0x8000_0000_0000_0001],
},
CovGroupImage {
type_index: 1,
site: 0xdead_beef,
instance: InstanceId(0),
decl: crate::covergroup::decl::CovGroupDecl::new("pkg"),
kernels: Vec::new(),
sampler: None,
class_clock: None,
blocks: Vec::new(),
},
],
}
}
#[test]
fn verif_section_round_trips_and_is_cross_checked() {
let img = sample_image();
assert!(!img.verif.is_empty());
round_trip(img.verif.clone(), put_verif, get_verif);
let mut bad = img.clone();
bad.verif.assertions[0].snap = bad.symbols.len() as u32;
assert_eq!(
DesignImage::decode(&bad.encode()),
Err(ImageError::BadReference("assertion symbol"))
);
let mut bad = img.clone();
bad.verif.sampled[0].sites[0].guard = u32::MAX;
assert_eq!(
DesignImage::decode(&bad.encode()),
Err(ImageError::BadReference("sampled guard symbol"))
);
let mut bad = img.clone();
bad.verif.clocking[0].instance = InstanceId(77);
assert_eq!(
DesignImage::decode(&bad.encode()),
Err(ImageError::BadReference("clocking block instance"))
);
let mut bad = img.clone();
bad.verif.covergroups[0].instance = InstanceId(77);
assert_eq!(
DesignImage::decode(&bad.encode()),
Err(ImageError::BadReference("covergroup instance"))
);
let mut bad = img.clone();
bad.verif.covergroups[0].kernels[0] = u32::MAX;
assert_eq!(
DesignImage::decode(&bad.encode()),
Err(ImageError::BadReference("covergroup kernel symbol"))
);
let mut w = Writer::default();
put_assert_kind(&mut w, AssertKind::Expect);
let mut tag = w.buf.clone();
tag[0] = 99;
assert_eq!(
get_assert_kind(&mut Reader::new(&tag)),
Err(ImageError::BadTag {
what: "assertion kind",
tag: 99
})
);
}
fn round_trip<T: PartialEq + std::fmt::Debug>(
value: T,
put: impl Fn(&mut Writer, &T),
get: impl Fn(&mut Reader<'_>) -> Result<T, ImageError>,
) {
let mut w = Writer::default();
put(&mut w, &value);
let mut r = Reader::new(&w.buf);
let back = get(&mut r).expect("decodes");
assert!(r.done(), "the section is consumed exactly");
assert_eq!(back, value);
}
#[test]
fn four_state_values_round_trip() {
for v in [
FourStateValue::Narrow(0),
FourStateValue::Narrow(u128::MAX),
FourStateValue::Wide {
aval: vec![1, 2, 3],
bval: vec![4, 5, 6],
},
FourStateValue::Wide {
aval: vec![],
bval: vec![],
},
] {
round_trip(v, put_four_state, get_four_state);
}
}
#[test]
fn layouts_round_trip() {
round_trip(sample_layout(), put_layout, get_layout);
}
#[test]
fn every_section_round_trips() {
let img = sample_image();
round_trip(
img.templates.clone(),
|w, v| put_templates(w, v),
get_templates,
);
round_trip(
img.instances.clone(),
|w, v| put_instances(w, v),
get_instances,
);
round_trip(img.foreign.clone(), |w, v| put_foreign(w, v), get_foreign);
round_trip(
img.x_windows.clone(),
|w, v| put_x_windows(w, v),
get_x_windows,
);
round_trip(img.wires.clone(), |w, v| put_wires(w, v), get_wires);
round_trip(img.groups.clone(), |w, v| put_groups(w, v), get_groups);
round_trip(
img.networks.clone(),
|w, v| put_networks(w, v),
get_networks,
);
round_trip(img.symbols.clone(), |w, v| put_symbols(w, v), get_symbols);
round_trip(img.kernels.clone(), |w, v| put_kernels(w, v), get_kernels);
round_trip(
img.fork_table.clone(),
|w, v| put_fork_table(w, v),
get_fork_table,
);
round_trip(
img.fork_branches.clone(),
|w, v| put_fork_branches(w, v),
get_fork_branches,
);
round_trip(
img.wait_table.clone(),
|w, v| put_wait_table(w, v),
get_wait_table,
);
round_trip(img.strings.clone(), |w, v| put_strings(w, v), get_strings);
round_trip(
img.collections.clone(),
|w, v| put_collections(w, v),
get_collections,
);
round_trip(img.classes.clone(), |w, v| put_classes(w, v), get_classes);
round_trip(
img.dump_signals.clone(),
|w, v| put_dump_signals(w, v),
get_dump_signals,
);
round_trip(img.dpi.clone(), put_dpi, get_dpi);
round_trip(
vec![StmtSiteImage {
def: "top".to_string(),
buffer: 2,
start: 10,
end: 20,
kind: 7,
nest: 1,
}],
|w, v| put_stmt_sites(w, v),
get_stmt_sites,
);
round_trip(
vec![FsmImage {
instance: InstanceId(1),
name: "cs".to_string(),
parts: vec![FsmPartImage {
slot: 3,
msb: 3,
lsb: 0,
}],
states: vec![("S0".to_string(), 0), ("S1".to_string(), 1)],
}],
|w, v| put_fsms(w, v),
get_fsms,
);
}
fn sample_timing() -> TimingImage {
TimingImage {
options: TimingOptionsImage {
per_bit_vectors: true,
negative_timing_checks: true,
reject_limit_pct: Some(12.5f64.to_bits()),
error_limit_pct: None,
pulse_style: Some(1),
showcancelled: Some(false),
sdf_mtm: Some(2),
},
plan: vec![0, 1, 2, 255],
}
}
#[test]
fn timing_section_round_trips() {
round_trip(sample_timing(), put_timing, get_timing);
round_trip(TimingImage::default(), put_timing, get_timing);
let mut img = DesignImage::empty(1);
img.timing = sample_timing();
assert_eq!(DesignImage::decode(&img.encode()).unwrap(), img);
let mut w = Writer::default();
put_timing(&mut w, &sample_timing());
w.buf[2] = 7;
let mut r = Reader::new(&w.buf);
assert!(get_timing(&mut r).is_err());
}
#[test]
fn whole_image_round_trips_and_encodes_deterministically() {
let img = sample_image();
let bytes = img.encode();
assert_eq!(bytes, sample_image().encode(), "equal images, equal bytes");
assert_eq!(&bytes[..8], &IMAGE_MAGIC);
assert_eq!(DesignImage::decode(&bytes).unwrap(), img);
let empty = DesignImage::empty(1);
assert_eq!(DesignImage::decode(&empty.encode()).unwrap(), empty);
}
#[test]
fn corruption_is_refused_by_name() {
let bytes = sample_image().encode();
for n in 0..bytes.len() {
assert!(
DesignImage::decode(&bytes[..n]).is_err(),
"prefix of {n} bytes decoded"
);
}
let mut bad = bytes.clone();
bad[0] ^= 1;
assert_eq!(DesignImage::decode(&bad), Err(ImageError::BadMagic));
let mut bad = bytes.clone();
bad[8] = 99;
assert!(matches!(
DesignImage::decode(&bad),
Err(ImageError::BadVersion { found: 99, .. })
));
let mut bad = bytes.clone();
let last = bad.len() - 1;
bad[last] ^= 0x40;
assert_eq!(DesignImage::decode(&bad), Err(ImageError::BadHash));
let mut bad = bytes.clone();
bad.push(0);
assert_eq!(DesignImage::decode(&bad), Err(ImageError::TrailingBytes));
}
#[test]
fn dangling_references_are_refused() {
let mut img = sample_image();
img.kernels[0].symbol = 99;
assert_eq!(
DesignImage::decode(&img.encode()),
Err(ImageError::BadReference("kernel symbol"))
);
let mut img = sample_image();
img.networks[0].groups[0] = 9;
assert_eq!(
DesignImage::decode(&img.encode()),
Err(ImageError::BadReference("network group"))
);
let mut img = sample_image();
img.instances.pop();
assert_eq!(
DesignImage::decode(&img.encode()),
Err(ImageError::BadReference("wire instance"))
);
let mut img = sample_image();
img.instances[0].template = key(1234);
assert_eq!(
DesignImage::decode(&img.encode()),
Err(ImageError::BadReference("instance template"))
);
}
}