WIP adding enums to simulator
All checks were successful
/ deps (push) Successful in 23s
/ test (push) Successful in 5m17s
/ deps (pull_request) Successful in 13s
/ test (pull_request) Successful in 5m19s

This commit is contained in:
Jacob Lifshay 2024-12-02 21:06:23 -08:00
parent d3f52292a1
commit 4422157db8
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
3 changed files with 269 additions and 13 deletions

View file

@ -5,6 +5,7 @@
use crate::{ use crate::{
bundle::{BundleField, BundleType}, bundle::{BundleField, BundleType},
enum_::{EnumType, EnumVariant},
expr::{ expr::{
ops, ops,
target::{ target::{
@ -36,7 +37,7 @@ use crate::{
ty::StaticType, ty::StaticType,
util::{BitSliceWriteWithBase, DebugAsDisplay}, 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 hashbrown::{HashMap, HashSet};
use num_bigint::BigInt; use num_bigint::BigInt;
use num_traits::{Signed, ToPrimitive, Zero}; use num_traits::{Signed, ToPrimitive, Zero};
@ -1229,6 +1230,7 @@ pub struct Compiler {
assignments: Assignments, assignments: Assignments,
clock_triggers: Vec<ClockTrigger>, clock_triggers: Vec<ClockTrigger>,
compiled_value_to_clock_trigger_map: HashMap<CompiledValue<Clock>, ClockTrigger>, compiled_value_to_clock_trigger_map: HashMap<CompiledValue<Clock>, ClockTrigger>,
enum_discriminants: HashMap<CompiledValue<Enum>, StatePartIndex<StatePartKindSmallSlots>>,
registers: Vec<Register>, registers: Vec<Register>,
traces: Vec<SimTrace<()>>, traces: Vec<SimTrace<()>>,
} }
@ -1252,6 +1254,7 @@ impl Compiler {
assignments: Assignments::default(), assignments: Assignments::default(),
clock_triggers: Vec::new(), clock_triggers: Vec::new(),
compiled_value_to_clock_trigger_map: HashMap::new(), compiled_value_to_clock_trigger_map: HashMap::new(),
enum_discriminants: HashMap::new(),
registers: Vec::new(), registers: Vec::new(),
traces: Vec::new(), traces: Vec::new(),
} }
@ -1308,7 +1311,7 @@ impl Compiler {
flow, flow,
} }
.into(), .into(),
CanonicalType::Bool(ty) => TraceBool { CanonicalType::Bool(_) => TraceBool {
id: self.make_trace_scalar_helper( id: self.make_trace_scalar_helper(
target, target,
|index| SimTraceKind::SmallBool { index }, |index| SimTraceKind::SmallBool { index },
@ -1318,10 +1321,27 @@ impl Compiler {
flow, flow,
} }
.into(), .into(),
CanonicalType::Array(ty) => unreachable!(), CanonicalType::Array(_) => unreachable!(),
CanonicalType::Enum(ty) => todo!(), CanonicalType::Enum(ty) => {
CanonicalType::Bundle(ty) => unreachable!(), assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width);
CanonicalType::AsyncReset(ty) => TraceAsyncReset { 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( id: self.make_trace_scalar_helper(
target, target,
|index| SimTraceKind::SmallAsyncReset { index }, |index| SimTraceKind::SmallAsyncReset { index },
@ -1331,7 +1351,7 @@ impl Compiler {
flow, flow,
} }
.into(), .into(),
CanonicalType::SyncReset(ty) => TraceSyncReset { CanonicalType::SyncReset(_) => TraceSyncReset {
id: self.make_trace_scalar_helper( id: self.make_trace_scalar_helper(
target, target,
|index| SimTraceKind::SmallSyncReset { index }, |index| SimTraceKind::SmallSyncReset { index },
@ -1342,7 +1362,7 @@ impl Compiler {
} }
.into(), .into(),
CanonicalType::Reset(_) => unreachable!(), CanonicalType::Reset(_) => unreachable!(),
CanonicalType::Clock(ty) => TraceClock { CanonicalType::Clock(_) => TraceClock {
id: self.make_trace_scalar_helper( id: self.make_trace_scalar_helper(
target, target,
|index| SimTraceKind::SmallClock { index }, |index| SimTraceKind::SmallClock { index },
@ -1381,7 +1401,13 @@ impl Compiler {
} }
.into() .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) => { CanonicalType::Bundle(ty) => {
let fields = Interned::from_iter(ty.fields().iter().map(|field| { let fields = Interned::from_iter(ty.fields().iter().map(|field| {
self.make_trace_decl_child( self.make_trace_decl_child(
@ -2752,6 +2778,71 @@ impl Compiler {
self.compiled_value_to_clock_trigger_map.insert(clk, retval); self.compiled_value_to_clock_trigger_map.insert(clk, retval);
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>( fn compile_stmt_reg<R: ResetType>(
&mut self, &mut self,
stmt_reg: StmtReg<R>, stmt_reg: StmtReg<R>,
@ -3681,6 +3772,12 @@ pub trait TraceWriter: fmt::Debug + 'static {
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.set_signal_bool(id, value) 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>); 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_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool) fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool)
-> std::io::Result<()>; -> 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 { impl<T: TraceWriter> TraceWriterDynTrait for T {
@ -3754,6 +3857,17 @@ impl<T: TraceWriter> TraceWriterDynTrait for T {
) -> std::io::Result<()> { ) -> std::io::Result<()> {
Ok(TraceWriter::set_signal_async_reset(self, id, value).map_err(err_into_io)?) 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>); pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
@ -3800,6 +3914,15 @@ impl TraceWriter for DynTraceWriter {
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.0.set_signal_async_reset_dyn(id, value) 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)] #[derive(Debug)]
@ -3894,6 +4017,10 @@ enum SimTraceKind {
SmallClock { SmallClock {
index: StatePartIndex<StatePartKindSmallSlots>, index: StatePartIndex<StatePartKindSmallSlots>,
}, },
EnumDiscriminant {
index: StatePartIndex<StatePartKindSmallSlots>,
ty: Enum,
},
} }
impl SimTraceKind { impl SimTraceKind {
@ -3913,6 +4040,9 @@ impl SimTraceKind {
| SimTraceKind::SmallAsyncReset { index: _ } | SimTraceKind::SmallAsyncReset { index: _ }
| SimTraceKind::SmallSyncReset { index: _ } | SimTraceKind::SmallSyncReset { index: _ }
| SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1), | 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 { .. } => { SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => {
trace_writer.set_signal_clock(id, state[0])?; 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) Ok(trace_writer)
@ -4091,7 +4231,8 @@ impl SimulationImpl {
state.set(0, !self.state.big_slots[index].is_zero()); state.set(0, !self.state.big_slots[index].is_zero());
} }
SimTraceKind::SmallUInt { index, ty: _ } 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 bytes = self.state.small_slots[index].to_le_bytes();
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes); let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
let bitslice = &bitslice[..state.len()]; let bitslice = &bitslice[..state.len()];

View file

@ -2260,6 +2260,35 @@ impl_insns! {
state.small_slots[dest] = value; state.small_slots[dest] = value;
next!(); 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 { Or {
#[kind = Output] #[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>, dest: StatePartIndex<StatePartKindBigSlots>,

View file

@ -2,6 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
enum_::{Enum, EnumType},
expr::Flow, expr::Flow,
int::UInt, int::UInt,
sim::{ sim::{
@ -13,7 +14,11 @@ use crate::{
}, },
}; };
use bitvec::slice::BitSlice; 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> { pub struct VcdWriterDecls<W: io::Write + 'static> {
writer: W, writer: W,
@ -190,6 +195,46 @@ fn write_scalar_id<W: io::Write>(writer: &mut W, id: TraceScalarId) -> io::Resul
Ok(()) 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>( fn write_vcd_var<W: io::Write>(
writer: &mut W, writer: &mut W,
var_type: &str, var_type: &str,
@ -251,13 +296,25 @@ impl WriteTrace for TraceBool {
impl WriteTrace for TraceFieldlessEnum { impl WriteTrace for TraceFieldlessEnum {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> { 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 { impl WriteTrace for TraceEnumDiscriminant {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> { 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 { pub fn timescale(&self) -> SimDuration {
self.timescale 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> { 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> { fn close(mut self) -> Result<(), Self::Error> {
self.writer.flush() 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> { impl<W: io::Write> fmt::Debug for VcdWriter<W> {