WIP adding enums to simulator
This commit is contained in:
parent
d3f52292a1
commit
4422157db8
|
@ -5,6 +5,7 @@
|
|||
|
||||
use crate::{
|
||||
bundle::{BundleField, BundleType},
|
||||
enum_::{EnumType, EnumVariant},
|
||||
expr::{
|
||||
ops,
|
||||
target::{
|
||||
|
@ -36,7 +37,7 @@ use crate::{
|
|||
ty::StaticType,
|
||||
util::{BitSliceWriteWithBase, DebugAsDisplay},
|
||||
};
|
||||
use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec};
|
||||
use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::{Signed, ToPrimitive, Zero};
|
||||
|
@ -1229,6 +1230,7 @@ pub struct Compiler {
|
|||
assignments: Assignments,
|
||||
clock_triggers: Vec<ClockTrigger>,
|
||||
compiled_value_to_clock_trigger_map: HashMap<CompiledValue<Clock>, ClockTrigger>,
|
||||
enum_discriminants: HashMap<CompiledValue<Enum>, StatePartIndex<StatePartKindSmallSlots>>,
|
||||
registers: Vec<Register>,
|
||||
traces: Vec<SimTrace<()>>,
|
||||
}
|
||||
|
@ -1252,6 +1254,7 @@ impl Compiler {
|
|||
assignments: Assignments::default(),
|
||||
clock_triggers: Vec::new(),
|
||||
compiled_value_to_clock_trigger_map: HashMap::new(),
|
||||
enum_discriminants: HashMap::new(),
|
||||
registers: Vec::new(),
|
||||
traces: Vec::new(),
|
||||
}
|
||||
|
@ -1308,7 +1311,7 @@ impl Compiler {
|
|||
flow,
|
||||
}
|
||||
.into(),
|
||||
CanonicalType::Bool(ty) => TraceBool {
|
||||
CanonicalType::Bool(_) => TraceBool {
|
||||
id: self.make_trace_scalar_helper(
|
||||
target,
|
||||
|index| SimTraceKind::SmallBool { index },
|
||||
|
@ -1318,10 +1321,27 @@ impl Compiler {
|
|||
flow,
|
||||
}
|
||||
.into(),
|
||||
CanonicalType::Array(ty) => unreachable!(),
|
||||
CanonicalType::Enum(ty) => todo!(),
|
||||
CanonicalType::Bundle(ty) => unreachable!(),
|
||||
CanonicalType::AsyncReset(ty) => TraceAsyncReset {
|
||||
CanonicalType::Array(_) => unreachable!(),
|
||||
CanonicalType::Enum(ty) => {
|
||||
assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width);
|
||||
let compiled_value = self.compile_value(target);
|
||||
let discriminant = self.compile_enum_discriminant(
|
||||
compiled_value.map_ty(Enum::from_canonical),
|
||||
target.target.base().source_location(),
|
||||
);
|
||||
TraceFieldlessEnum {
|
||||
id: self.new_sim_trace(SimTraceKind::EnumDiscriminant {
|
||||
index: discriminant,
|
||||
ty,
|
||||
}),
|
||||
name,
|
||||
ty,
|
||||
flow,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
CanonicalType::Bundle(_) => unreachable!(),
|
||||
CanonicalType::AsyncReset(_) => TraceAsyncReset {
|
||||
id: self.make_trace_scalar_helper(
|
||||
target,
|
||||
|index| SimTraceKind::SmallAsyncReset { index },
|
||||
|
@ -1331,7 +1351,7 @@ impl Compiler {
|
|||
flow,
|
||||
}
|
||||
.into(),
|
||||
CanonicalType::SyncReset(ty) => TraceSyncReset {
|
||||
CanonicalType::SyncReset(_) => TraceSyncReset {
|
||||
id: self.make_trace_scalar_helper(
|
||||
target,
|
||||
|index| SimTraceKind::SmallSyncReset { index },
|
||||
|
@ -1342,7 +1362,7 @@ impl Compiler {
|
|||
}
|
||||
.into(),
|
||||
CanonicalType::Reset(_) => unreachable!(),
|
||||
CanonicalType::Clock(ty) => TraceClock {
|
||||
CanonicalType::Clock(_) => TraceClock {
|
||||
id: self.make_trace_scalar_helper(
|
||||
target,
|
||||
|index| SimTraceKind::SmallClock { index },
|
||||
|
@ -1381,7 +1401,13 @@ impl Compiler {
|
|||
}
|
||||
.into()
|
||||
}
|
||||
CanonicalType::Enum(ty) => todo!(),
|
||||
CanonicalType::Enum(ty) => {
|
||||
if ty.variants().iter().all(|v| v.ty.is_none()) {
|
||||
self.make_trace_scalar(target, name)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
CanonicalType::Bundle(ty) => {
|
||||
let fields = Interned::from_iter(ty.fields().iter().map(|field| {
|
||||
self.make_trace_decl_child(
|
||||
|
@ -2752,6 +2778,71 @@ impl Compiler {
|
|||
self.compiled_value_to_clock_trigger_map.insert(clk, retval);
|
||||
retval
|
||||
}
|
||||
fn compile_enum_discriminant(
|
||||
&mut self,
|
||||
enum_value: CompiledValue<Enum>,
|
||||
source_location: SourceLocation,
|
||||
) -> StatePartIndex<StatePartKindSmallSlots> {
|
||||
if let Some(&retval) = self.enum_discriminants.get(&enum_value) {
|
||||
return retval;
|
||||
}
|
||||
let retval_ty = Enum::new(
|
||||
enum_value
|
||||
.layout
|
||||
.ty
|
||||
.variants()
|
||||
.iter()
|
||||
.map(|variant| EnumVariant {
|
||||
name: variant.name,
|
||||
ty: None,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
let retval = if retval_ty == enum_value.layout.ty
|
||||
&& enum_value.range.len() == TypeLen::A_SMALL_SLOT
|
||||
{
|
||||
enum_value.range.small_slots.start
|
||||
} else {
|
||||
let retval = self
|
||||
.insns
|
||||
.state_layout
|
||||
.ty
|
||||
.small_slots
|
||||
.allocate(&StatePartLayout::scalar(SlotDebugData {
|
||||
name: Interned::default(),
|
||||
ty: retval_ty.canonical(),
|
||||
}))
|
||||
.start;
|
||||
let discriminant_bit_width = enum_value.layout.ty.discriminant_bit_width();
|
||||
let discriminant_mask = !(!0u64 << discriminant_bit_width);
|
||||
let insn = match enum_value.range.len() {
|
||||
TypeLen::A_BIG_SLOT => Insn::AndBigWithSmallImmediate {
|
||||
dest: retval,
|
||||
lhs: enum_value.range.big_slots.start,
|
||||
rhs: discriminant_mask,
|
||||
},
|
||||
TypeLen::A_SMALL_SLOT => {
|
||||
if discriminant_bit_width == enum_value.layout.ty.type_properties().bit_width {
|
||||
Insn::CopySmall {
|
||||
dest: retval,
|
||||
src: enum_value.range.small_slots.start,
|
||||
}
|
||||
} else {
|
||||
Insn::AndSmallImmediate {
|
||||
dest: retval,
|
||||
lhs: enum_value.range.small_slots.start,
|
||||
rhs: discriminant_mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.add_assignment(Interned::default(), [insn], source_location);
|
||||
retval
|
||||
};
|
||||
self.enum_discriminants.insert(enum_value, retval);
|
||||
retval
|
||||
}
|
||||
fn compile_stmt_reg<R: ResetType>(
|
||||
&mut self,
|
||||
stmt_reg: StmtReg<R>,
|
||||
|
@ -3681,6 +3772,12 @@ pub trait TraceWriter: fmt::Debug + 'static {
|
|||
) -> Result<(), Self::Error> {
|
||||
self.set_signal_bool(id, value)
|
||||
}
|
||||
fn set_signal_enum_discriminant(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
variant_index: usize,
|
||||
ty: Enum,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
|
||||
|
@ -3717,6 +3814,12 @@ trait TraceWriterDynTrait: fmt::Debug + 'static {
|
|||
fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
||||
fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool)
|
||||
-> std::io::Result<()>;
|
||||
fn set_signal_enum_discriminant_dyn(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
variant_index: usize,
|
||||
ty: Enum,
|
||||
) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: TraceWriter> TraceWriterDynTrait for T {
|
||||
|
@ -3754,6 +3857,17 @@ impl<T: TraceWriter> TraceWriterDynTrait for T {
|
|||
) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_async_reset(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_enum_discriminant_dyn(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
variant_index: usize,
|
||||
ty: Enum,
|
||||
) -> std::io::Result<()> {
|
||||
Ok(
|
||||
TraceWriter::set_signal_enum_discriminant(self, id, variant_index, ty)
|
||||
.map_err(err_into_io)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
|
||||
|
@ -3800,6 +3914,15 @@ impl TraceWriter for DynTraceWriter {
|
|||
) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_async_reset_dyn(id, value)
|
||||
}
|
||||
fn set_signal_enum_discriminant(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
variant_index: usize,
|
||||
ty: Enum,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.0
|
||||
.set_signal_enum_discriminant_dyn(id, variant_index, ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -3894,6 +4017,10 @@ enum SimTraceKind {
|
|||
SmallClock {
|
||||
index: StatePartIndex<StatePartKindSmallSlots>,
|
||||
},
|
||||
EnumDiscriminant {
|
||||
index: StatePartIndex<StatePartKindSmallSlots>,
|
||||
ty: Enum,
|
||||
},
|
||||
}
|
||||
|
||||
impl SimTraceKind {
|
||||
|
@ -3913,6 +4040,9 @@ impl SimTraceKind {
|
|||
| SimTraceKind::SmallAsyncReset { index: _ }
|
||||
| SimTraceKind::SmallSyncReset { index: _ }
|
||||
| SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1),
|
||||
SimTraceKind::EnumDiscriminant { index: _, ty } => {
|
||||
BitVec::repeat(false, ty.discriminant_bit_width())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4042,6 +4172,16 @@ impl SimulationImpl {
|
|||
SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => {
|
||||
trace_writer.set_signal_clock(id, state[0])?;
|
||||
}
|
||||
SimTraceKind::EnumDiscriminant { ty, .. } => {
|
||||
let mut variant_index = [0; mem::size_of::<usize>()];
|
||||
variant_index.view_bits_mut::<Lsb0>()[0..state.len()]
|
||||
.clone_from_bitslice(state);
|
||||
trace_writer.set_signal_enum_discriminant(
|
||||
id,
|
||||
usize::from_le_bytes(variant_index),
|
||||
ty,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(trace_writer)
|
||||
|
@ -4091,7 +4231,8 @@ impl SimulationImpl {
|
|||
state.set(0, !self.state.big_slots[index].is_zero());
|
||||
}
|
||||
SimTraceKind::SmallUInt { index, ty: _ }
|
||||
| SimTraceKind::SmallSInt { index, ty: _ } => {
|
||||
| SimTraceKind::SmallSInt { index, ty: _ }
|
||||
| SimTraceKind::EnumDiscriminant { index, ty: _ } => {
|
||||
let bytes = self.state.small_slots[index].to_le_bytes();
|
||||
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
|
||||
let bitslice = &bitslice[..state.len()];
|
||||
|
|
|
@ -2260,6 +2260,35 @@ impl_insns! {
|
|||
state.small_slots[dest] = value;
|
||||
next!();
|
||||
}
|
||||
AndSmallImmediate {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||
#[kind = Input]
|
||||
lhs: StatePartIndex<StatePartKindSmallSlots>,
|
||||
#[kind = Immediate]
|
||||
rhs: SmallUInt,
|
||||
} => {
|
||||
let value = state.small_slots[lhs] & rhs;
|
||||
state.small_slots[dest] = value;
|
||||
next!();
|
||||
}
|
||||
AndBigWithSmallImmediate {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||
#[kind = Input]
|
||||
lhs: StatePartIndex<StatePartKindBigSlots>,
|
||||
#[kind = Immediate]
|
||||
rhs: SmallUInt,
|
||||
} => {
|
||||
let lhs = &state.big_slots[lhs];
|
||||
let mut lhs_lsb64 = lhs.iter_u64_digits().next().unwrap_or(0);
|
||||
if lhs.is_negative() {
|
||||
lhs_lsb64 = lhs_lsb64.wrapping_neg();
|
||||
}
|
||||
let value = lhs_lsb64 & rhs;
|
||||
state.small_slots[dest] = value;
|
||||
next!();
|
||||
}
|
||||
Or {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
enum_::{Enum, EnumType},
|
||||
expr::Flow,
|
||||
int::UInt,
|
||||
sim::{
|
||||
|
@ -13,7 +14,11 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
use std::{fmt, io, mem};
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
io::{self, Write},
|
||||
mem,
|
||||
};
|
||||
|
||||
pub struct VcdWriterDecls<W: io::Write + 'static> {
|
||||
writer: W,
|
||||
|
@ -190,6 +195,46 @@ fn write_scalar_id<W: io::Write>(writer: &mut W, id: TraceScalarId) -> io::Resul
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_escaped<W: io::Write>(writer: &mut W, value: impl Display) -> io::Result<()> {
|
||||
// escaping rules from function GTKWave uses to decode VCD strings:
|
||||
// https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
|
||||
struct Wrapper<W>(W);
|
||||
impl<W: io::Write> io::Write for Wrapper<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if buf.is_empty() {
|
||||
return self.0.write(buf);
|
||||
}
|
||||
let mut retval = 0;
|
||||
for &byte in buf {
|
||||
match byte {
|
||||
b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?,
|
||||
b'\n' => self.0.write_all(br"\n")?,
|
||||
b'\r' => self.0.write_all(br"\r")?,
|
||||
b'\t' => self.0.write_all(br"\t")?,
|
||||
0x7 => self.0.write_all(br"\a")?,
|
||||
0x8 => self.0.write_all(br"\b")?,
|
||||
0xC => self.0.write_all(br"\f")?,
|
||||
0xB => self.0.write_all(br"\v")?,
|
||||
_ => {
|
||||
if byte.is_ascii_graphic() {
|
||||
self.0.write_all(&[byte])?;
|
||||
} else {
|
||||
write!(self.0, r"\x{byte:02x}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval += 1;
|
||||
}
|
||||
Ok(retval)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
write!(Wrapper(writer), "{value}")
|
||||
}
|
||||
|
||||
fn write_vcd_var<W: io::Write>(
|
||||
writer: &mut W,
|
||||
var_type: &str,
|
||||
|
@ -251,13 +296,25 @@ impl WriteTrace for TraceBool {
|
|||
|
||||
impl WriteTrace for TraceFieldlessEnum {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
|
||||
todo!()
|
||||
let Self { id, name, ty, flow } = self;
|
||||
TraceEnumDiscriminant { id, name, ty, flow }.write_trace(writer, arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteTrace for TraceEnumDiscriminant {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
|
||||
todo!()
|
||||
let ArgInType {
|
||||
source_var_type: _,
|
||||
sink_var_type: _,
|
||||
duplex_var_type: _,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
id,
|
||||
name,
|
||||
ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
write_vcd_var(writer, "string", 1, id, &name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,6 +526,17 @@ impl<W: io::Write + 'static> VcdWriter<W> {
|
|||
pub fn timescale(&self) -> SimDuration {
|
||||
self.timescale
|
||||
}
|
||||
fn write_string_value_change(
|
||||
&mut self,
|
||||
value: impl Display,
|
||||
id: TraceScalarId,
|
||||
) -> io::Result<()> {
|
||||
self.writer.write_all(b"s")?;
|
||||
write_escaped(&mut self.writer, value)?;
|
||||
self.writer.write_all(b" ")?;
|
||||
write_scalar_id(&mut self.writer, id)?;
|
||||
self.writer.write_all(b"\n")
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
||||
|
@ -525,6 +593,24 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
|||
fn close(mut self) -> Result<(), Self::Error> {
|
||||
self.writer.flush()
|
||||
}
|
||||
|
||||
fn set_signal_enum_discriminant(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
variant_index: usize,
|
||||
ty: Enum,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.write_string_value_change(
|
||||
format_args!(
|
||||
"{} ({variant_index})",
|
||||
ty.variants()
|
||||
.get(variant_index)
|
||||
.map(|v| &*v.name)
|
||||
.unwrap_or("<invalid>"),
|
||||
),
|
||||
id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> fmt::Debug for VcdWriter<W> {
|
||||
|
|
Loading…
Reference in a new issue