sim: add WIP memory test
All checks were successful
/ deps (push) Successful in 18s
/ test (push) Successful in 5m16s
/ deps (pull_request) Successful in 14s
/ test (pull_request) Successful in 5m19s

This commit is contained in:
Jacob Lifshay 2024-12-11 23:28:15 -08:00
parent 8616ee4737
commit 393f78a14d
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
11 changed files with 1155 additions and 304 deletions

File diff suppressed because it is too large Load diff

View file

@ -990,6 +990,7 @@ macro_rules! make_state_part_kinds {
pub(crate) struct State {
pub(crate) insns: Interned<Insns<InsnsBuildingDone>>,
pub(crate) pc: usize,
pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
$(pub(crate) $state_field: StatePart<$StateKind>,)*
$(pub(crate) $type_field: StatePart<$TypeKind>,)*
}
@ -999,6 +1000,7 @@ macro_rules! make_state_part_kinds {
Self {
insns,
pc: 0,
memory_write_log: Vec::with_capacity(32),
$($state_field: StatePart::new(&insns.state_layout.$state_field.layout_data),)*
$($type_field: StatePart::new(&insns.state_layout.ty.$type_field.layout_data),)*
}
@ -1009,6 +1011,7 @@ macro_rules! make_state_part_kinds {
insns: &self.insns.insns,
pc: self.pc,
orig_pc: &mut self.pc,
memory_write_log: &mut self.memory_write_log,
$($state_field: self.$state_field.borrow(),)*
$($type_field: self.$type_field.borrow(),)*
}
@ -1021,6 +1024,7 @@ macro_rules! make_state_part_kinds {
pub(crate) insns: &'a [Insn],
pub(crate) orig_pc: &'a mut usize,
pub(crate) pc: usize,
pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
$(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)*
$(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)*
}
@ -2146,6 +2150,7 @@ impl State {
let Self {
insns: _,
pc,
memory_write_log: _,
memories: _,
small_slots: _,
big_slots: _,
@ -2172,6 +2177,18 @@ impl BorrowedState<'_> {
}
Some(retval)
}
fn log_memory_write(&mut self, memory: StatePartIndex<StatePartKindMemories>, addr: SmallUInt) {
let Ok(addr) = usize::try_from(addr) else {
return;
};
if addr < self.memories.value.len() {
let log_entry = (memory, addr);
if self.memory_write_log.last().copied() == Some(log_entry) {
return;
}
self.memory_write_log.push(log_entry);
}
}
}
impl TypeIndexRange {
@ -2898,6 +2915,7 @@ impl_insns! {
} => {
let addr = state.small_slots[addr];
memory_write_big::<UInt>(&mut state.memories[memory], addr, stride, start, width, &mut state.big_slots[value]);
state.log_memory_write(memory, addr);
next!();
}
MemoryWriteSInt {
@ -2916,6 +2934,7 @@ impl_insns! {
} => {
let addr = state.small_slots[addr];
memory_write_big::<SInt>(&mut state.memories[memory], addr, stride, start, width, &mut state.big_slots[value]);
state.log_memory_write(memory, addr);
next!();
}
Return => {

View file

@ -9,13 +9,14 @@ use crate::{
time::{SimDuration, SimInstant},
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance,
TraceMemPort, TraceModule, TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId,
TraceScope, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule,
TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset,
TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
},
};
use bitvec::slice::BitSlice;
use bitvec::{order::Lsb0, slice::BitSlice};
use std::{
fmt::{self, Display},
fmt,
io::{self, Write},
mem,
};
@ -112,20 +113,20 @@ macro_rules! trait_arg {
(
trait $Arg:ident {
$(
fn $fn:ident(self) -> $ty:ty;
fn $fn:ident(&mut self) -> $ty:ty;
)*
}
) => {
trait $Arg: Sized {
$(fn $fn(self) -> $ty {
$(fn $fn(&mut self) -> $ty {
unreachable!()
})*
}
$(
impl $Arg for $ty {
fn $fn(self) -> $ty {
self
fn $fn(&mut self) -> $ty {
self.reborrow()
}
}
)*
@ -134,21 +135,52 @@ macro_rules! trait_arg {
trait_arg! {
trait Arg {
fn module(self) -> ArgModule;
fn module_body(self) -> ArgModuleBody;
fn in_type(self) -> ArgInType;
fn module(&mut self) -> ArgModule<'_>;
fn module_body(&mut self) -> ArgModuleBody<'_>;
fn in_type(&mut self) -> ArgInType<'_>;
}
}
struct ArgModule {}
struct ArgModule<'a> {
properties: &'a mut VcdWriterProperties,
}
struct ArgModuleBody {}
impl<'a> ArgModule<'a> {
fn reborrow(&mut self) -> ArgModule<'_> {
ArgModule {
properties: self.properties,
}
}
}
#[derive(Clone, Copy)]
struct ArgInType {
struct ArgModuleBody<'a> {
properties: &'a mut VcdWriterProperties,
}
impl<'a> ArgModuleBody<'a> {
fn reborrow(&mut self) -> ArgModuleBody<'_> {
ArgModuleBody {
properties: self.properties,
}
}
}
struct ArgInType<'a> {
source_var_type: &'static str,
sink_var_type: &'static str,
duplex_var_type: &'static str,
properties: &'a mut VcdWriterProperties,
}
impl<'a> ArgInType<'a> {
fn reborrow(&mut self) -> ArgInType<'_> {
ArgInType {
source_var_type: self.source_var_type,
sink_var_type: self.sink_var_type,
duplex_var_type: self.duplex_var_type,
properties: self.properties,
}
}
}
trait WriteTrace: Copy {
@ -179,11 +211,10 @@ impl WriteTrace for TraceScalar {
}
}
fn write_scalar_id<W: io::Write>(writer: &mut W, id: TraceScalarId) -> io::Result<()> {
fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
let min_char = b'!';
let max_char = b'~';
let base = (max_char - min_char + 1) as usize;
let mut id = id.as_usize();
loop {
let digit = (id % base) as u8 + min_char;
id /= base;
@ -195,7 +226,7 @@ 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<()> {
fn write_escaped<W: io::Write>(writer: &mut W, value: impl fmt::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);
@ -247,14 +278,47 @@ fn is_unescaped_verilog_identifier(ident: &str) -> bool {
}
fn write_vcd_var<W: io::Write>(
properties: &mut VcdWriterProperties,
memory_element_part_body: MemoryElementPartBody,
writer: &mut W,
var_type: &str,
size: usize,
id: TraceScalarId,
location: TraceLocation,
name: &str,
) -> io::Result<()> {
let id = match location {
TraceLocation::Scalar(id) => id.as_usize(),
TraceLocation::Memory(TraceMemoryLocation {
id,
depth,
stride: _,
start,
len,
}) => {
let MemoryProperties {
element_parts,
element_part_index,
element_index,
} = &mut properties.memory_properties[id.as_usize()];
let first_id;
if let Some(element_part) = element_parts.get(*element_part_index) {
first_id = element_part.first_id;
} else {
first_id = properties.next_scalar_id;
properties.next_scalar_id += depth;
element_parts.push(MemoryElementPart {
first_id,
start,
len,
body: memory_element_part_body,
});
}
*element_part_index += 1;
first_id + *element_index
}
};
write!(writer, "$var {var_type} {size} ")?;
write_scalar_id(writer, id)?;
write_vcd_id(writer, id)?;
writer.write_all(b" ")?;
if !is_unescaped_verilog_identifier(name) {
writer.write_all(b"\\")?;
@ -264,31 +328,49 @@ fn write_vcd_var<W: io::Write>(
}
impl WriteTrace for TraceUInt {
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, mut arg: A) -> io::Result<()> {
let ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
} = arg.in_type();
let Self { id, name, ty, flow } = self;
let var_type = match flow {
let Self {
location,
name,
ty,
flow,
} = self;
let mut var_type = match flow {
Flow::Source => source_var_type,
Flow::Sink => sink_var_type,
Flow::Duplex => duplex_var_type,
};
if ty.width() == 0 {
write_vcd_var(writer, "string", ty.width(), id, &name)
} else {
write_vcd_var(writer, var_type, ty.width(), id, &name)
var_type = "string";
}
write_vcd_var(
properties,
MemoryElementPartBody::Scalar,
writer,
var_type,
ty.width(),
location,
&name,
)
}
}
impl WriteTrace for TraceSInt {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, ty, flow } = self;
let Self {
location,
name,
ty,
flow,
} = self;
TraceUInt {
id,
location,
name,
ty: UInt::new_dyn(ty.width()),
flow,
@ -299,9 +381,13 @@ impl WriteTrace for TraceSInt {
impl WriteTrace for TraceBool {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, flow } = self;
let Self {
location,
name,
flow,
} = self;
TraceUInt {
id,
location,
name,
flow,
ty: UInt::new_dyn(1),
@ -312,46 +398,93 @@ impl WriteTrace for TraceBool {
impl WriteTrace for TraceFieldlessEnum {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, ty, flow } = self;
TraceEnumDiscriminant { id, name, ty, flow }.write_trace(writer, arg)
let Self {
location,
name,
ty,
flow,
} = self;
TraceEnumDiscriminant {
location,
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<()> {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgInType {
source_var_type: _,
sink_var_type: _,
duplex_var_type: _,
properties,
} = arg.in_type();
let Self {
id,
location,
name,
ty: _,
ty,
flow: _,
} = self;
write_vcd_var(writer, "string", 1, id, &name)
write_vcd_var(
properties,
MemoryElementPartBody::EnumDiscriminant { ty },
writer,
"string",
1,
location,
&name,
)
}
}
impl WriteTrace for TraceClock {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, flow } = self;
TraceBool { id, name, flow }.write_trace(writer, arg)
let Self {
location,
name,
flow,
} = self;
TraceBool {
location,
name,
flow,
}
.write_trace(writer, arg)
}
}
impl WriteTrace for TraceSyncReset {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, flow } = self;
TraceBool { id, name, flow }.write_trace(writer, arg)
let Self {
location,
name,
flow,
} = self;
TraceBool {
location,
name,
flow,
}
.write_trace(writer, arg)
}
}
impl WriteTrace for TraceAsyncReset {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let Self { id, name, flow } = self;
TraceBool { id, name, flow }.write_trace(writer, arg)
let Self {
location,
name,
flow,
} = self;
TraceBool {
location,
name,
flow,
}
.write_trace(writer, arg)
}
}
@ -360,6 +493,7 @@ impl WriteTrace for TraceScope {
match self {
Self::Module(v) => v.write_trace(writer, arg),
Self::Instance(v) => v.write_trace(writer, arg),
Self::Mem(v) => v.write_trace(writer, arg),
Self::MemPort(v) => v.write_trace(writer, arg),
Self::Wire(v) => v.write_trace(writer, arg),
Self::Reg(v) => v.write_trace(writer, arg),
@ -372,12 +506,12 @@ impl WriteTrace for TraceScope {
}
impl WriteTrace for TraceModule {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let ArgModule {} = arg.module();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModule { properties } = arg.module();
let Self { name, children } = self;
write_vcd_scope(writer, "module", &name, |writer| {
for child in children {
child.write_trace(writer, ArgModuleBody {})?;
child.write_trace(writer, ArgModuleBody { properties })?;
}
Ok(())
})
@ -385,8 +519,8 @@ impl WriteTrace for TraceModule {
}
impl WriteTrace for TraceInstance {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let ArgModuleBody {} = arg.module_body();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
name: _,
instance_io,
@ -399,21 +533,74 @@ impl WriteTrace for TraceInstance {
source_var_type: "wire",
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
},
)?;
module.write_trace(writer, ArgModule {})
module.write_trace(writer, ArgModule { properties })
}
}
impl WriteTrace for TraceMem {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
id,
name,
stride: _,
element_type,
ports,
array_type,
} = self;
write_vcd_scope(writer, "struct", &*name, |writer| {
write_vcd_scope(writer, "struct", "contents", |writer| {
for element_index in 0..array_type.len() {
write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| {
properties.memory_properties[id.as_usize()].element_index = element_index;
properties.memory_properties[id.as_usize()].element_part_index = 0;
element_type.write_trace(
writer,
ArgInType {
source_var_type: "reg",
sink_var_type: "reg",
duplex_var_type: "reg",
properties,
},
)
})?;
}
Ok(())
})?;
for port in ports {
port.write_trace(writer, ArgModuleBody { properties })?;
}
Ok(())
})
}
}
impl WriteTrace for TraceMemPort {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
todo!()
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
name: _,
bundle,
ty: _,
} = self;
bundle.write_trace(
writer,
ArgInType {
source_var_type: "wire",
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
},
)
}
}
impl WriteTrace for TraceWire {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let ArgModuleBody {} = arg.module_body();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
name: _,
child,
@ -425,14 +612,15 @@ impl WriteTrace for TraceWire {
source_var_type: "wire",
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
},
)
}
}
impl WriteTrace for TraceReg {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let ArgModuleBody {} = arg.module_body();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
name: _,
child,
@ -444,14 +632,15 @@ impl WriteTrace for TraceReg {
source_var_type: "reg",
sink_var_type: "reg",
duplex_var_type: "reg",
properties,
},
)
}
}
impl WriteTrace for TraceModuleIO {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let ArgModuleBody {} = arg.module_body();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body();
let Self {
name: _,
child,
@ -464,14 +653,15 @@ impl WriteTrace for TraceModuleIO {
source_var_type: "wire",
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
},
)
}
}
impl WriteTrace for TraceBundle {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let arg = arg.in_type();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type();
let Self {
name,
fields,
@ -480,7 +670,7 @@ impl WriteTrace for TraceBundle {
} = self;
write_vcd_scope(writer, "struct", &name, |writer| {
for field in fields {
field.write_trace(writer, arg)?;
field.write_trace(writer, arg.reborrow())?;
}
Ok(())
})
@ -488,8 +678,8 @@ impl WriteTrace for TraceBundle {
}
impl WriteTrace for TraceArray {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let arg = arg.in_type();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type();
let Self {
name,
elements,
@ -498,7 +688,7 @@ impl WriteTrace for TraceArray {
} = self;
write_vcd_scope(writer, "struct", &name, |writer| {
for element in elements {
element.write_trace(writer, arg)?;
element.write_trace(writer, arg.reborrow())?;
}
Ok(())
})
@ -506,8 +696,8 @@ impl WriteTrace for TraceArray {
}
impl WriteTrace for TraceEnumWithFields {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
let arg = arg.in_type();
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type();
let Self {
name,
discriminant,
@ -516,9 +706,9 @@ impl WriteTrace for TraceEnumWithFields {
flow: _,
} = self;
write_vcd_scope(writer, "struct", &name, |writer| {
discriminant.write_trace(writer, arg)?;
discriminant.write_trace(writer, arg.reborrow())?;
for field in non_empty_fields {
field.write_trace(writer, arg)?;
field.write_trace(writer, arg.reborrow())?;
}
Ok(())
})
@ -529,76 +719,184 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
type Error = io::Error;
type TraceWriter = VcdWriter<W>;
fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error> {
fn write_decls(
self,
module: TraceModule,
trace_scalar_id_count: usize,
trace_memory_id_count: usize,
) -> Result<Self::TraceWriter, Self::Error> {
let Self {
mut writer,
timescale,
} = self;
writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
module.write_trace(&mut writer, ArgModule {})?;
let mut properties = VcdWriterProperties {
next_scalar_id: trace_scalar_id_count,
memory_properties: (0..trace_memory_id_count)
.map(|_| MemoryProperties {
element_parts: Vec::with_capacity(8),
element_part_index: 0,
element_index: 0,
})
.collect(),
};
module.write_trace(
&mut writer,
ArgModule {
properties: &mut properties,
},
)?;
writeln!(writer, "$enddefinitions $end")?;
writeln!(writer, "$dumpvars")?;
Ok(VcdWriter {
writer,
finished_init: false,
timescale,
properties,
})
}
}
enum MemoryElementPartBody {
Scalar,
EnumDiscriminant { ty: Enum },
}
struct MemoryElementPart {
first_id: usize,
start: usize,
len: usize,
body: MemoryElementPartBody,
}
struct MemoryProperties {
element_parts: Vec<MemoryElementPart>,
element_part_index: usize,
element_index: usize,
}
struct VcdWriterProperties {
next_scalar_id: usize,
memory_properties: Box<[MemoryProperties]>,
}
pub struct VcdWriter<W: io::Write + 'static> {
writer: W,
finished_init: bool,
timescale: SimDuration,
properties: VcdWriterProperties,
}
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")
}
fn write_string_value_change(
writer: &mut impl io::Write,
value: impl fmt::Display,
id: usize,
) -> io::Result<()> {
writer.write_all(b"s")?;
write_escaped(writer, value)?;
writer.write_all(b" ")?;
write_vcd_id(writer, id)?;
writer.write_all(b"\n")
}
fn write_bits_value_change(
writer: &mut impl io::Write,
value: &BitSlice,
id: usize,
) -> io::Result<()> {
match value.len() {
0 => writer.write_all(b"s0 ")?,
1 => writer.write_all(if value[0] { b"1" } else { b"0" })?,
_ => {
writer.write_all(b"b")?;
let mut any_ones = false;
for bit in value.iter().rev() {
if *bit {
any_ones = true;
writer.write_all(b"1")?;
} else if any_ones {
writer.write_all(b"0")?;
}
}
if !any_ones {
writer.write_all(b"0")?;
}
writer.write_all(b" ")?;
}
}
write_vcd_id(writer, id)?;
writer.write_all(b"\n")
}
fn write_enum_discriminant_value_change(
writer: &mut impl io::Write,
variant_index: usize,
ty: Enum,
id: usize,
) -> io::Result<()> {
write_string_value_change(
writer,
format_args!(
"{} ({variant_index})",
ty.variants()
.get(variant_index)
.map(|v| &*v.name)
.unwrap_or("<invalid>"),
),
id,
)
}
impl<W: io::Write> TraceWriter for VcdWriter<W> {
type Error = io::Error;
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
match value.len() {
0 => self.writer.write_all(b"s0 ")?,
1 => self.writer.write_all(if value[0] { b"1" } else { b"0" })?,
_ => {
self.writer.write_all(b"b")?;
let mut any_ones = false;
for bit in value.iter().rev() {
if *bit {
any_ones = true;
self.writer.write_all(b"1")?;
} else if any_ones {
self.writer.write_all(b"0")?;
}
fn set_memory_element(
&mut self,
memory: TraceMemoryId,
element_index: usize,
element_data: &BitSlice,
) -> Result<(), Self::Error> {
for &MemoryElementPart {
first_id,
start,
len,
ref body,
} in &self.properties.memory_properties[memory.as_usize()].element_parts
{
match body {
MemoryElementPartBody::Scalar => write_bits_value_change(
&mut self.writer,
&element_data[start..start + len],
first_id + element_index,
)?,
MemoryElementPartBody::EnumDiscriminant { ty } => {
let mut variant_index = 0;
BitSlice::<usize, Lsb0>::from_element_mut(&mut variant_index)[..len]
.clone_from_bitslice(&element_data[start..start + len]);
write_enum_discriminant_value_change(
&mut self.writer,
variant_index,
*ty,
first_id + element_index,
)?
}
if !any_ones {
self.writer.write_all(b"0")?;
}
self.writer.write_all(b" ")?;
}
}
write_scalar_id(&mut self.writer, id)?;
self.writer.write_all(b"\n")
Ok(())
}
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
write_bits_value_change(&mut self.writer, value, id.as_usize())
}
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
self.set_signal_uint(id, value)
write_bits_value_change(&mut self.writer, value, id.as_usize())
}
fn finish_init(&mut self) -> Result<(), Self::Error> {
@ -630,16 +928,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
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,
)
write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize())
}
}
@ -649,6 +938,7 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
writer: _,
finished_init,
timescale,
properties: _,
} = self;
f.debug_struct("VcdWriter")
.field("finished_init", finished_init)

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{
int::UIntValue,
prelude::*,
@ -7,6 +8,7 @@ use fayalite::{
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation},
util::RcWriter,
};
use std::num::NonZeroUsize;
#[hdl_module(outline_generated)]
pub fn connect_const() {
@ -492,4 +494,113 @@ fn test_enums() {
}
}
// TODO: add tests for memories
#[hdl_module(outline_generated)]
pub fn memories() {
#[hdl]
let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
#[hdl]
let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
#[hdl]
let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]);
mem.read_latency(0);
mem.write_latency(NonZeroUsize::new(1).unwrap());
mem.read_under_write(ReadUnderWrite::Old);
connect_any(mem.new_read_port(), r);
connect_any(mem.new_write_port(), w);
}
#[cfg(todo)] // TODO: finish
#[hdl]
#[test]
fn test_memories() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(memories());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
#[derive(Debug, PartialEq, Eq)]
struct IO {
r_addr: u8,
r_en: bool,
r_data: (u8, i8),
w_addr: u8,
w_en: bool,
w_data: (u8, i8),
w_mask: (bool, bool),
}
let io_cycles = [IO {
r_addr: 0,
r_en: false,
r_data: (0, 0),
w_addr: 0,
w_en: false,
w_data: (0, 0),
w_mask: (false, false),
}];
for (
cycle,
expected @ IO {
r_addr,
r_en,
r_data: _,
w_addr,
w_en,
w_data,
w_mask,
},
) in io_cycles.into_iter().enumerate()
{
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static());
sim.write_bool(sim.io().r.en, r_en);
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static());
sim.write_bool(sim.io().w.en, w_en);
sim.write_bool_or_int(sim.io().w.data.0, w_data.0);
sim.write_bool_or_int(sim.io().w.data.1, w_data.1);
sim.write_bool(sim.io().w.mask.0, w_mask.0);
sim.write_bool(sim.io().w.mask.1, w_mask.1);
let io = IO {
r_addr,
r_en,
r_data: (
sim.read_bool_or_int(sim.io().r.data.0)
.to_bigint()
.try_into()
.expect("known to be in range"),
sim.read_bool_or_int(sim.io().r.data.1)
.to_bigint()
.try_into()
.expect("known to be in range"),
),
w_addr,
w_en,
w_data,
w_mask,
};
assert_eq!(
expected,
io,
"cycle: {cycle}\nvcd:\n{}",
String::from_utf8(writer.take()).unwrap(),
);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().r.clk, true);
sim.write_clock(sim.io().w.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/memories.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/memories.txt") {
panic!();
}
}
// TODO: add more tests for memories

View file

@ -47,6 +47,7 @@ Simulation {
..
},
pc: 2,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -112,7 +113,7 @@ Simulation {
TraceModuleIO {
name: "o",
child: TraceUInt {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "o",
ty: UInt<8>,
flow: Sink,
@ -133,6 +134,7 @@ Simulation {
last_state: 0x05,
},
],
trace_memories: {},
trace_writers: [],
instant: 0 s,
clocks_triggered: [],

View file

@ -73,6 +73,7 @@ Simulation {
..
},
pc: 5,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -175,7 +176,7 @@ Simulation {
TraceModuleIO {
name: "reset_out",
child: TraceAsyncReset {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "reset_out",
flow: Sink,
},
@ -185,7 +186,7 @@ Simulation {
TraceModuleIO {
name: "bit_out",
child: TraceBool {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "bit_out",
flow: Sink,
},
@ -212,6 +213,7 @@ Simulation {
last_state: 0x1,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {

View file

@ -168,6 +168,7 @@ Simulation {
..
},
pc: 18,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -417,12 +418,12 @@ Simulation {
name: "cd",
fields: [
TraceClock {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceAsyncReset {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
@ -446,7 +447,7 @@ Simulation {
TraceModuleIO {
name: "count",
child: TraceUInt {
id: TraceScalarId(2),
location: TraceScalarId(2),
name: "count",
ty: UInt<4>,
flow: Sink,
@ -457,7 +458,7 @@ Simulation {
TraceReg {
name: "count_reg",
child: TraceUInt {
id: TraceScalarId(3),
location: TraceScalarId(3),
name: "count_reg",
ty: UInt<4>,
flow: Duplex,
@ -502,6 +503,7 @@ Simulation {
last_state: 0x3,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {

View file

@ -150,6 +150,7 @@ Simulation {
..
},
pc: 15,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -398,12 +399,12 @@ Simulation {
name: "cd",
fields: [
TraceClock {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceSyncReset {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
@ -427,7 +428,7 @@ Simulation {
TraceModuleIO {
name: "count",
child: TraceUInt {
id: TraceScalarId(2),
location: TraceScalarId(2),
name: "count",
ty: UInt<4>,
flow: Sink,
@ -438,7 +439,7 @@ Simulation {
TraceReg {
name: "count_reg",
child: TraceUInt {
id: TraceScalarId(3),
location: TraceScalarId(3),
name: "count_reg",
ty: UInt<4>,
flow: Duplex,
@ -483,6 +484,7 @@ Simulation {
last_state: 0x3,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {

View file

@ -875,6 +875,7 @@ Simulation {
..
},
pc: 100,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -1333,12 +1334,12 @@ Simulation {
name: "cd",
fields: [
TraceClock {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceSyncReset {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
@ -1362,7 +1363,7 @@ Simulation {
TraceModuleIO {
name: "en",
child: TraceBool {
id: TraceScalarId(2),
location: TraceScalarId(2),
name: "en",
flow: Source,
},
@ -1372,7 +1373,7 @@ Simulation {
TraceModuleIO {
name: "which_in",
child: TraceUInt {
id: TraceScalarId(3),
location: TraceScalarId(3),
name: "which_in",
ty: UInt<2>,
flow: Source,
@ -1383,7 +1384,7 @@ Simulation {
TraceModuleIO {
name: "data_in",
child: TraceUInt {
id: TraceScalarId(4),
location: TraceScalarId(4),
name: "data_in",
ty: UInt<4>,
flow: Source,
@ -1394,7 +1395,7 @@ Simulation {
TraceModuleIO {
name: "which_out",
child: TraceUInt {
id: TraceScalarId(5),
location: TraceScalarId(5),
name: "which_out",
ty: UInt<2>,
flow: Sink,
@ -1405,7 +1406,7 @@ Simulation {
TraceModuleIO {
name: "data_out",
child: TraceUInt {
id: TraceScalarId(6),
location: TraceScalarId(6),
name: "data_out",
ty: UInt<4>,
flow: Sink,
@ -1418,7 +1419,7 @@ Simulation {
child: TraceEnumWithFields {
name: "the_reg",
discriminant: TraceEnumDiscriminant {
id: TraceScalarId(7),
location: TraceScalarId(7),
name: "$tag",
ty: Enum {
A,
@ -1432,13 +1433,13 @@ Simulation {
name: "B",
fields: [
TraceUInt {
id: TraceScalarId(8),
location: TraceScalarId(8),
name: "0",
ty: UInt<1>,
flow: Source,
},
TraceBool {
id: TraceScalarId(9),
location: TraceScalarId(9),
name: "1",
flow: Source,
},
@ -1458,13 +1459,13 @@ Simulation {
name: "a",
elements: [
TraceUInt {
id: TraceScalarId(10),
location: TraceScalarId(10),
name: "[0]",
ty: UInt<1>,
flow: Source,
},
TraceUInt {
id: TraceScalarId(11),
location: TraceScalarId(11),
name: "[1]",
ty: UInt<1>,
flow: Source,
@ -1474,7 +1475,7 @@ Simulation {
flow: Source,
},
TraceSInt {
id: TraceScalarId(12),
location: TraceScalarId(12),
name: "b",
ty: SInt<2>,
flow: Source,
@ -1623,6 +1624,7 @@ Simulation {
last_state: 0x3,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {

View file

@ -180,6 +180,7 @@ Simulation {
..
},
pc: 17,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -531,25 +532,25 @@ Simulation {
name: "o",
fields: [
TraceUInt {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "i",
ty: UInt<4>,
flow: Source,
},
TraceSInt {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "o",
ty: SInt<2>,
flow: Sink,
},
TraceSInt {
id: TraceScalarId(2),
location: TraceScalarId(2),
name: "i2",
ty: SInt<2>,
flow: Source,
},
TraceUInt {
id: TraceScalarId(3),
location: TraceScalarId(3),
name: "o2",
ty: UInt<4>,
flow: Sink,
@ -585,25 +586,25 @@ Simulation {
name: "child",
fields: [
TraceUInt {
id: TraceScalarId(8),
location: TraceScalarId(8),
name: "i",
ty: UInt<4>,
flow: Sink,
},
TraceSInt {
id: TraceScalarId(9),
location: TraceScalarId(9),
name: "o",
ty: SInt<2>,
flow: Source,
},
TraceSInt {
id: TraceScalarId(10),
location: TraceScalarId(10),
name: "i2",
ty: SInt<2>,
flow: Sink,
},
TraceUInt {
id: TraceScalarId(11),
location: TraceScalarId(11),
name: "o2",
ty: UInt<4>,
flow: Source,
@ -627,7 +628,7 @@ Simulation {
TraceModuleIO {
name: "i",
child: TraceUInt {
id: TraceScalarId(4),
location: TraceScalarId(4),
name: "i",
ty: UInt<4>,
flow: Source,
@ -638,7 +639,7 @@ Simulation {
TraceModuleIO {
name: "o",
child: TraceSInt {
id: TraceScalarId(5),
location: TraceScalarId(5),
name: "o",
ty: SInt<2>,
flow: Sink,
@ -649,7 +650,7 @@ Simulation {
TraceModuleIO {
name: "i2",
child: TraceSInt {
id: TraceScalarId(6),
location: TraceScalarId(6),
name: "i2",
ty: SInt<2>,
flow: Source,
@ -660,7 +661,7 @@ Simulation {
TraceModuleIO {
name: "o2",
child: TraceUInt {
id: TraceScalarId(7),
location: TraceScalarId(7),
name: "o2",
ty: UInt<4>,
flow: Sink,
@ -793,6 +794,7 @@ Simulation {
last_state: 0xe,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {

View file

@ -227,6 +227,7 @@ Simulation {
..
},
pc: 30,
memory_write_log: [],
memories: StatePart {
value: [],
},
@ -513,12 +514,12 @@ Simulation {
name: "cd",
fields: [
TraceClock {
id: TraceScalarId(0),
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceSyncReset {
id: TraceScalarId(1),
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
@ -542,7 +543,7 @@ Simulation {
TraceModuleIO {
name: "d",
child: TraceBool {
id: TraceScalarId(2),
location: TraceScalarId(2),
name: "d",
flow: Source,
},
@ -552,7 +553,7 @@ Simulation {
TraceModuleIO {
name: "q",
child: TraceBool {
id: TraceScalarId(3),
location: TraceScalarId(3),
name: "q",
flow: Sink,
},
@ -562,7 +563,7 @@ Simulation {
TraceReg {
name: "reg0",
child: TraceBool {
id: TraceScalarId(4),
location: TraceScalarId(4),
name: "reg0",
flow: Duplex,
},
@ -571,7 +572,7 @@ Simulation {
TraceReg {
name: "reg1",
child: TraceBool {
id: TraceScalarId(5),
location: TraceScalarId(5),
name: "reg1",
flow: Duplex,
},
@ -580,7 +581,7 @@ Simulation {
TraceReg {
name: "reg2",
child: TraceBool {
id: TraceScalarId(6),
location: TraceScalarId(6),
name: "reg2",
flow: Duplex,
},
@ -589,7 +590,7 @@ Simulation {
TraceReg {
name: "reg3",
child: TraceBool {
id: TraceScalarId(7),
location: TraceScalarId(7),
name: "reg3",
flow: Duplex,
},
@ -663,6 +664,7 @@ Simulation {
last_state: 0x0,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {