1208 lines
34 KiB
Rust
1208 lines
34 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
use fayalite::{
|
|
int::UIntValue,
|
|
prelude::*,
|
|
reset::ResetType,
|
|
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation},
|
|
util::RcWriter,
|
|
};
|
|
use std::num::NonZeroUsize;
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn connect_const() {
|
|
#[hdl]
|
|
let o: UInt<8> = m.output();
|
|
connect(o, 5u8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect_const() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(connect_const());
|
|
sim.settle();
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/connect_const.txt") {
|
|
panic!();
|
|
}
|
|
assert_eq!(sim.read_bool_or_int(sim.io().o), UIntValue::from(5u8));
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn connect_const_reset() {
|
|
#[hdl]
|
|
let reset_out: Reset = m.output();
|
|
#[hdl]
|
|
let bit_out: Bool = m.output();
|
|
connect(reset_out, true.to_async_reset().to_reset());
|
|
connect(bit_out, reset_out.cast_to_static());
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect_const_reset() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(connect_const_reset());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.settle();
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/connect_const_reset.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/connect_const_reset.txt") {
|
|
panic!();
|
|
}
|
|
assert_eq!(sim.read_bool_or_int(sim.io().bit_out), true);
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn mod1_child() {
|
|
#[hdl]
|
|
let i: UInt<4> = m.input();
|
|
#[hdl]
|
|
let o: SInt<2> = m.output();
|
|
#[hdl]
|
|
let i2: SInt<2> = m.input();
|
|
#[hdl]
|
|
let o2: UInt<4> = m.output();
|
|
connect(o, i.cast_to_static());
|
|
connect(o2, i2.cast_to_static());
|
|
#[hdl]
|
|
if i.cmp_gt(5_hdl_u4) {
|
|
connect(o2, 0xF_hdl_u4);
|
|
}
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn mod1() {
|
|
#[hdl]
|
|
let child = instance(mod1_child());
|
|
#[hdl]
|
|
let o: mod1_child = m.output(Expr::ty(child));
|
|
connect(o, child);
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_mod1() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(mod1());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_bool_or_int(sim.io().o.i, 0x3_hdl_u4);
|
|
sim.write_bool_or_int(sim.io().o.i2, -2_hdl_i2);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_bool_or_int(sim.io().o.i, 0xA_hdl_u4);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/mod1.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/mod1.txt") {
|
|
panic!();
|
|
}
|
|
let expected = -2_hdl_i2;
|
|
assert_eq!(sim.read_bool_or_int(sim.io().o.o).to_expr(), expected);
|
|
let expected = 0xF_hdl_u4;
|
|
assert_eq!(sim.read_bool_or_int(sim.io().o.o2).to_expr(), expected);
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn counter<R: ResetType>() {
|
|
#[hdl]
|
|
let cd: ClockDomain<R> = m.input();
|
|
#[hdl]
|
|
let count_reg: UInt<4> = reg_builder().clock_domain(cd).reset(3_hdl_u4);
|
|
connect_any(count_reg, count_reg + 1_hdl_u1);
|
|
#[hdl]
|
|
let count: UInt<4> = m.output();
|
|
connect(count, count_reg);
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_counter_sync() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(counter::<SyncReset>());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.write_reset(sim.io().cd.rst, true);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.settle();
|
|
let reset_value = 3_hdl_u4;
|
|
assert_eq!(
|
|
reset_value,
|
|
sim.read_bool_or_int(sim.io().count).to_expr(),
|
|
"vcd:\n{}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.write_reset(sim.io().cd.rst, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
for i in 0..32u32 {
|
|
assert_eq!(
|
|
UInt::<4>::new_static().from_int_wrapping(i + 3),
|
|
sim.read_bool_or_int(sim.io().count),
|
|
"vcd:\n{}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/counter_sync.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/counter_sync.txt") {
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_counter_async() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(counter::<AsyncReset>());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.write_reset(sim.io().cd.rst, false);
|
|
sim.advance_time(SimDuration::from_nanos(500));
|
|
sim.write_reset(sim.io().cd.rst, true);
|
|
let reset_value = 3_hdl_u4;
|
|
assert_eq!(
|
|
reset_value,
|
|
sim.read_bool_or_int(sim.io().count).to_expr(),
|
|
"vcd:\n{}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.advance_time(SimDuration::from_nanos(500));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_nanos(500));
|
|
sim.write_reset(sim.io().cd.rst, false);
|
|
sim.advance_time(SimDuration::from_nanos(500));
|
|
for i in 0..32u32 {
|
|
assert_eq!(
|
|
UInt::<4>::new_static().from_int_wrapping(i + 3),
|
|
sim.read_bool_or_int(sim.io().count),
|
|
"vcd:\n{}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/counter_async.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/counter_async.txt") {
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn shift_register() {
|
|
#[hdl]
|
|
let cd: ClockDomain<SyncReset> = m.input();
|
|
#[hdl]
|
|
let d: Bool = m.input();
|
|
#[hdl]
|
|
let q: Bool = m.output();
|
|
#[hdl]
|
|
let reg0: Bool = reg_builder().clock_domain(cd).reset(false);
|
|
connect(reg0, d);
|
|
#[hdl]
|
|
let reg1: Bool = reg_builder().clock_domain(cd).reset(false);
|
|
connect(reg1, reg0);
|
|
#[hdl]
|
|
let reg2: Bool = reg_builder().clock_domain(cd).reset(false);
|
|
connect(reg2, reg1);
|
|
#[hdl]
|
|
let reg3: Bool = reg_builder().clock_domain(cd).reset(false);
|
|
connect(reg3, reg2);
|
|
connect(q, reg3);
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_shift_register() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(shift_register());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.write_reset(sim.io().cd.rst, true);
|
|
sim.write_bool(sim.io().d, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_nanos(100));
|
|
sim.write_reset(sim.io().cd.rst, false);
|
|
sim.advance_time(SimDuration::from_nanos(900));
|
|
let data = [
|
|
false, true, true, false, false, true, false, true, true, true, true,
|
|
];
|
|
for cycle in 0..32usize {
|
|
if let Some(out_cycle) = cycle.checked_sub(4) {
|
|
if let Some(expected) = data.get(out_cycle) {
|
|
assert_eq!(
|
|
*expected,
|
|
sim.read_bool(sim.io().q),
|
|
"vcd:\n{}\ncycle: {cycle}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
}
|
|
}
|
|
sim.write_bool(sim.io().d, data.get(cycle).copied().unwrap_or(false));
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/shift_register.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/shift_register.txt") {
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn enums() {
|
|
#[hdl]
|
|
let cd: ClockDomain<SyncReset> = m.input();
|
|
#[hdl]
|
|
let en: Bool = m.input();
|
|
#[hdl]
|
|
let which_in: UInt<2> = m.input();
|
|
#[hdl]
|
|
let data_in: UInt<4> = m.input();
|
|
#[hdl]
|
|
let which_out: UInt<2> = m.output();
|
|
#[hdl]
|
|
let data_out: UInt<4> = m.output();
|
|
|
|
#[hdl]
|
|
struct MyStruct<T> {
|
|
a: T,
|
|
b: SInt<2>,
|
|
}
|
|
|
|
#[hdl]
|
|
enum MyEnum {
|
|
A,
|
|
B((UInt<1>, Bool)),
|
|
C(MyStruct<Array<UInt<1>, 2>>),
|
|
}
|
|
|
|
#[hdl]
|
|
let the_reg = reg_builder().clock_domain(cd).reset(MyEnum.A());
|
|
|
|
#[hdl]
|
|
if en {
|
|
#[hdl]
|
|
if which_in.cmp_eq(0_hdl_u2) {
|
|
connect(the_reg, MyEnum.A());
|
|
} else if which_in.cmp_eq(1_hdl_u2) {
|
|
connect(the_reg, MyEnum.B((data_in[0].cast_to_static(), data_in[1])));
|
|
} else {
|
|
connect(
|
|
the_reg,
|
|
MyEnum.C(
|
|
#[hdl]
|
|
MyStruct {
|
|
a: #[hdl]
|
|
[data_in[0].cast_to_static(), data_in[1].cast_to_static()],
|
|
b: data_in[2..4].cast_to_static(),
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[hdl]
|
|
match the_reg {
|
|
MyEnum::A => {
|
|
connect(which_out, 0_hdl_u2);
|
|
connect(data_out, 0_hdl_u4);
|
|
}
|
|
MyEnum::B(v) => {
|
|
connect(which_out, 1_hdl_u2);
|
|
connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1));
|
|
}
|
|
MyEnum::C(v) => {
|
|
connect(which_out, 2_hdl_u2);
|
|
connect_any(data_out, v.cast_to_bits());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_enums() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(enums());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.write_reset(sim.io().cd.rst, true);
|
|
sim.write_bool(sim.io().en, false);
|
|
sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2);
|
|
sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_nanos(100));
|
|
sim.write_reset(sim.io().cd.rst, false);
|
|
sim.advance_time(SimDuration::from_nanos(900));
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
struct IO {
|
|
en: bool,
|
|
which_in: u8,
|
|
data_in: u8,
|
|
which_out: u8,
|
|
data_out: u8,
|
|
}
|
|
let io_cycles = [
|
|
IO {
|
|
en: false,
|
|
which_in: 0,
|
|
data_in: 0,
|
|
which_out: 0,
|
|
data_out: 0,
|
|
},
|
|
IO {
|
|
en: true,
|
|
which_in: 1,
|
|
data_in: 0,
|
|
which_out: 0,
|
|
data_out: 0,
|
|
},
|
|
IO {
|
|
en: false,
|
|
which_in: 0,
|
|
data_in: 0,
|
|
which_out: 1,
|
|
data_out: 0,
|
|
},
|
|
IO {
|
|
en: true,
|
|
which_in: 1,
|
|
data_in: 0xF,
|
|
which_out: 1,
|
|
data_out: 0,
|
|
},
|
|
IO {
|
|
en: true,
|
|
which_in: 1,
|
|
data_in: 0xF,
|
|
which_out: 1,
|
|
data_out: 0x3,
|
|
},
|
|
IO {
|
|
en: true,
|
|
which_in: 2,
|
|
data_in: 0xF,
|
|
which_out: 1,
|
|
data_out: 0x3,
|
|
},
|
|
IO {
|
|
en: true,
|
|
which_in: 2,
|
|
data_in: 0xF,
|
|
which_out: 2,
|
|
data_out: 0xF,
|
|
},
|
|
];
|
|
for (
|
|
cycle,
|
|
expected @ IO {
|
|
en,
|
|
which_in,
|
|
data_in,
|
|
which_out: _,
|
|
data_out: _,
|
|
},
|
|
) in io_cycles.into_iter().enumerate()
|
|
{
|
|
sim.write_bool(sim.io().en, en);
|
|
sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static());
|
|
sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static());
|
|
let io = IO {
|
|
en,
|
|
which_in,
|
|
data_in,
|
|
which_out: sim
|
|
.read_bool_or_int(sim.io().which_out)
|
|
.to_bigint()
|
|
.try_into()
|
|
.expect("known to be in range"),
|
|
data_out: sim
|
|
.read_bool_or_int(sim.io().data_out)
|
|
.to_bigint()
|
|
.try_into()
|
|
.expect("known to be in range"),
|
|
};
|
|
assert_eq!(
|
|
expected,
|
|
io,
|
|
"vcd:\n{}\ncycle: {cycle}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.write_clock(sim.io().cd.clk, false);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
sim.write_clock(sim.io().cd.clk, true);
|
|
sim.advance_time(SimDuration::from_micros(1));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/enums.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/enums.txt") {
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[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),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x1, 0x23),
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: (0x10, 0x20),
|
|
w_mask: (true, true),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x10, 0x20),
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: (0x30, 0x40),
|
|
w_mask: (false, true),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x10, 0x40),
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: (0x50, 0x60),
|
|
w_mask: (true, false),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x50, 0x40),
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: (0x70, -0x80),
|
|
w_mask: (false, false),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x50, 0x40),
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: (0x90, 0xA0u8 as i8),
|
|
w_mask: (false, false),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x50, 0x40),
|
|
w_addr: 1,
|
|
w_en: true,
|
|
w_data: (0x90, 0xA0u8 as i8),
|
|
w_mask: (true, true),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x50, 0x40),
|
|
w_addr: 2,
|
|
w_en: true,
|
|
w_data: (0xB0, 0xC0u8 as i8),
|
|
w_mask: (true, true),
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: (0x50, 0x40),
|
|
w_addr: 2,
|
|
w_en: false,
|
|
w_data: (0xD0, 0xE0u8 as i8),
|
|
w_mask: (true, true),
|
|
},
|
|
IO {
|
|
r_addr: 1,
|
|
r_en: true,
|
|
r_data: (0x90, 0xA0u8 as i8),
|
|
w_addr: 2,
|
|
w_en: false,
|
|
w_data: (0xD0, 0xE0u8 as i8),
|
|
w_mask: (true, true),
|
|
},
|
|
IO {
|
|
r_addr: 2,
|
|
r_en: true,
|
|
r_data: (0xB0, 0xC0u8 as i8),
|
|
w_addr: 2,
|
|
w_en: false,
|
|
w_data: (0xD0, 0xE0u8 as i8),
|
|
w_mask: (true, true),
|
|
},
|
|
];
|
|
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,
|
|
"vcd:\n{}\ncycle: {cycle}",
|
|
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!();
|
|
}
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn memories2() {
|
|
#[hdl]
|
|
let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input();
|
|
#[hdl]
|
|
let mut mem = memory_with_init([HdlSome(true); 5]);
|
|
mem.read_latency(1);
|
|
mem.write_latency(NonZeroUsize::new(1).unwrap());
|
|
mem.read_under_write(ReadUnderWrite::New);
|
|
let rw_port = mem.new_rw_port();
|
|
connect_any(rw_port.addr, rw.addr);
|
|
connect(rw_port.en, rw.en);
|
|
connect(rw_port.clk, rw.clk);
|
|
connect_any(rw.rdata, rw_port.rdata.cast_to_bits());
|
|
connect(rw_port.wmode, rw.wmode);
|
|
connect(rw_port.wdata, HdlNone());
|
|
#[hdl]
|
|
if rw.wdata[0] {
|
|
connect(rw_port.wdata, HdlSome(rw.wdata[1]));
|
|
}
|
|
connect(rw_port.wmask, rw.wmask);
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_memories2() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(memories2());
|
|
let mut writer = RcWriter::default();
|
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
sim.write_clock(sim.io().rw.clk, false);
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
struct IO {
|
|
addr: u8,
|
|
en: bool,
|
|
rdata: u8,
|
|
wmode: bool,
|
|
wdata: u8,
|
|
wmask: bool,
|
|
}
|
|
let io_cycles = [
|
|
IO {
|
|
addr: 0,
|
|
en: false,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: true,
|
|
rdata: 0x3,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: false,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 0,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 3,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 1,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 1,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 2,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 2,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 3,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 3,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 4,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 2,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 5,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 1,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 6,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 1,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 7,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: true,
|
|
wdata: 1,
|
|
wmask: true,
|
|
},
|
|
IO {
|
|
addr: 7,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 6,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 5,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 4,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 3,
|
|
en: true,
|
|
rdata: 3,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 2,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: true,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 1,
|
|
en: true,
|
|
rdata: 1,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
IO {
|
|
addr: 0,
|
|
en: false,
|
|
rdata: 0,
|
|
wmode: false,
|
|
wdata: 0,
|
|
wmask: false,
|
|
},
|
|
];
|
|
for (
|
|
cycle,
|
|
expected @ IO {
|
|
addr,
|
|
en,
|
|
rdata: _,
|
|
wmode,
|
|
wdata,
|
|
wmask,
|
|
},
|
|
) in io_cycles.into_iter().enumerate()
|
|
{
|
|
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static());
|
|
sim.write_bool(sim.io().rw.en, en);
|
|
sim.write_bool(sim.io().rw.wmode, wmode);
|
|
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static());
|
|
sim.write_bool(sim.io().rw.wmask, wmask);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
sim.write_clock(sim.io().rw.clk, true);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
let io = IO {
|
|
addr,
|
|
en,
|
|
rdata: sim
|
|
.read_bool_or_int(sim.io().rw.rdata)
|
|
.to_bigint()
|
|
.try_into()
|
|
.expect("known to be in range"),
|
|
wmode,
|
|
wdata,
|
|
wmask,
|
|
};
|
|
assert_eq!(
|
|
expected,
|
|
io,
|
|
"vcd:\n{}\ncycle: {cycle}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
sim.write_clock(sim.io().rw.clk, false);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/memories2.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/memories2.txt") {
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[hdl_module(outline_generated)]
|
|
pub fn memories3() {
|
|
#[hdl]
|
|
let r: fayalite::memory::ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
|
|
#[hdl]
|
|
let w: fayalite::memory::WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
|
|
#[hdl]
|
|
let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory();
|
|
mem.depth(8);
|
|
mem.read_latency(2);
|
|
mem.write_latency(NonZeroUsize::new(2).unwrap());
|
|
mem.read_under_write(ReadUnderWrite::Old);
|
|
connect_any(mem.new_read_port(), r);
|
|
connect_any(mem.new_write_port(), w);
|
|
}
|
|
|
|
#[hdl]
|
|
#[test]
|
|
fn test_memories3() {
|
|
let _n = SourceLocation::normalize_files_for_tests();
|
|
let mut sim = Simulation::new(memories3());
|
|
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, Clone, Copy)]
|
|
struct IO {
|
|
r_addr: u8,
|
|
r_en: bool,
|
|
r_data: [u8; 8],
|
|
w_addr: u8,
|
|
w_en: bool,
|
|
w_data: [u8; 8],
|
|
w_mask: [bool; 8],
|
|
}
|
|
let io_cycles = [
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: false,
|
|
r_data: [0; 8],
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
|
|
w_mask: [false, true, false, true, true, false, false, true],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0; 8],
|
|
w_addr: 1,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
|
|
w_addr: 1,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
|
|
w_addr: 0,
|
|
w_en: true,
|
|
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: false,
|
|
r_data: [0; 8],
|
|
w_addr: 1,
|
|
w_en: true,
|
|
w_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE],
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: false,
|
|
r_data: [0; 8],
|
|
w_addr: 2,
|
|
w_en: true,
|
|
w_data: *b"testing!",
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: false,
|
|
r_data: [0; 8],
|
|
w_addr: 3,
|
|
w_en: true,
|
|
w_data: *b"more tst",
|
|
w_mask: [true; 8],
|
|
},
|
|
IO {
|
|
r_addr: 0,
|
|
r_en: true,
|
|
r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
IO {
|
|
r_addr: 1,
|
|
r_en: true,
|
|
r_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE],
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
IO {
|
|
r_addr: 2,
|
|
r_en: true,
|
|
r_data: *b"testing!",
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
IO {
|
|
r_addr: 3,
|
|
r_en: true,
|
|
r_data: *b"more tst",
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
},
|
|
];
|
|
for cycle in 0..io_cycles.len() + 2 {
|
|
{
|
|
let IO {
|
|
r_addr,
|
|
r_en,
|
|
r_data: _,
|
|
w_addr,
|
|
w_en,
|
|
w_data,
|
|
w_mask,
|
|
} = io_cycles.get(cycle).copied().unwrap_or(IO {
|
|
r_addr: 0,
|
|
r_en: false,
|
|
r_data: [0; 8],
|
|
w_addr: 0,
|
|
w_en: false,
|
|
w_data: [0; 8],
|
|
w_mask: [false; 8],
|
|
});
|
|
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);
|
|
for (i, v) in w_data.into_iter().enumerate() {
|
|
sim.write_bool_or_int(sim.io().w.data[i], v);
|
|
}
|
|
for (i, v) in w_mask.into_iter().enumerate() {
|
|
sim.write_bool_or_int(sim.io().w.mask[i], v);
|
|
}
|
|
}
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
sim.write_clock(sim.io().r.clk, true);
|
|
sim.write_clock(sim.io().w.clk, true);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
if let Some(
|
|
expected @ IO {
|
|
r_addr,
|
|
r_en,
|
|
r_data: _,
|
|
w_addr,
|
|
w_en,
|
|
w_data,
|
|
w_mask,
|
|
},
|
|
) = cycle.checked_sub(1).and_then(|i| io_cycles.get(i).copied())
|
|
{
|
|
let io = IO {
|
|
r_addr,
|
|
r_en,
|
|
r_data: std::array::from_fn(|i| {
|
|
sim.read_bool_or_int(sim.io().r.data[i])
|
|
.to_bigint()
|
|
.try_into()
|
|
.expect("known to be in range")
|
|
}),
|
|
w_addr,
|
|
w_en,
|
|
w_data,
|
|
w_mask,
|
|
};
|
|
assert_eq!(
|
|
expected,
|
|
io,
|
|
"vcd:\n{}\ncycle: {cycle}",
|
|
String::from_utf8(writer.take()).unwrap(),
|
|
);
|
|
}
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
sim.write_clock(sim.io().r.clk, false);
|
|
sim.write_clock(sim.io().w.clk, false);
|
|
sim.advance_time(SimDuration::from_nanos(250));
|
|
}
|
|
sim.flush_traces().unwrap();
|
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
|
println!("####### VCD:\n{vcd}\n#######");
|
|
if vcd != include_str!("sim/expected/memories3.vcd") {
|
|
panic!();
|
|
}
|
|
let sim_debug = format!("{sim:#?}");
|
|
println!("#######\n{sim_debug}\n#######");
|
|
if sim_debug != include_str!("sim/expected/memories3.txt") {
|
|
panic!();
|
|
}
|
|
}
|