add sram and main_memory_and_io modules
This commit is contained in:
parent
5bdc71acc3
commit
a1147f0f05
3 changed files with 236476 additions and 0 deletions
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use crate::{config::CpuConfig, next_pc::FETCH_BLOCK_ID_WIDTH, util::array_vec::ArrayVec};
|
||||
use fayalite::{
|
||||
bitvec::{order::Lsb0, view::BitView},
|
||||
bundle::BundleType,
|
||||
expr::Valueless,
|
||||
int::UIntInRangeType,
|
||||
|
|
@ -1404,3 +1405,298 @@ pub fn memory_interface_adapter_no_split<OutputInterfaces: Type + MemoryInterfac
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// get [`MemoryInterfaceConfig`] for [`sram()`]
|
||||
pub const fn sram_config(suggested_config: MemoryInterfaceConfig) -> MemoryInterfaceConfig {
|
||||
let MemoryInterfaceConfig {
|
||||
log2_bus_width_in_bytes,
|
||||
queue_capacity: _,
|
||||
op_id_width,
|
||||
address_range,
|
||||
} = suggested_config;
|
||||
let size = address_range
|
||||
.size()
|
||||
.expect("must use AddressRange::Limited")
|
||||
.get();
|
||||
|
||||
assert!(size <= usize::MAX as u64, "address_range is too big");
|
||||
|
||||
let bus_width_in_bytes = suggested_config.bus_width_in_bytes();
|
||||
|
||||
let start = address_range.start().0;
|
||||
|
||||
assert!(
|
||||
start.is_multiple_of(bus_width_in_bytes as u64)
|
||||
&& size.is_multiple_of(bus_width_in_bytes as u64),
|
||||
"address_range must start and end at a multiple of bus_width_in_bytes",
|
||||
);
|
||||
MemoryInterfaceConfig {
|
||||
log2_bus_width_in_bytes,
|
||||
queue_capacity: const { NonZeroUsize::new(3).unwrap() },
|
||||
op_id_width,
|
||||
address_range,
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module]
|
||||
pub fn sram(config: PhantomConst<MemoryInterfaceConfig>, initial_contents: impl AsRef<[u8]>) {
|
||||
let initial_contents = initial_contents.as_ref();
|
||||
|
||||
// sram_config makes sure address_size is properly aligned and not too big
|
||||
|
||||
assert_eq!(sram_config(*config.get()), *config.get());
|
||||
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let input_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||
m.input(MemoryInterface[config]);
|
||||
|
||||
let address_range = config.get().address_range;
|
||||
|
||||
let size = address_range
|
||||
.size()
|
||||
.expect("already checked by sram_config")
|
||||
.get();
|
||||
|
||||
let bus_width_in_bytes = config.get().bus_width_in_bytes();
|
||||
|
||||
let address_range_start = address_range.start().0;
|
||||
|
||||
let element_ty = Array[UInt::<8>::new_static()][bus_width_in_bytes];
|
||||
|
||||
#[hdl]
|
||||
let mut mem = memory_array(Array[element_ty][size as usize / bus_width_in_bytes]);
|
||||
|
||||
mem.read_latency(0);
|
||||
mem.write_latency(const { NonZeroUsize::new(1).unwrap() });
|
||||
mem.read_under_write(ReadUnderWrite::Old);
|
||||
|
||||
if !initial_contents.is_empty() {
|
||||
let mut initial_bits = BitVec::<_, Lsb0>::repeat(
|
||||
false,
|
||||
mem.get_array_type()
|
||||
.expect("already set by memory_array")
|
||||
.canonical()
|
||||
.bit_width(),
|
||||
);
|
||||
|
||||
assert!(initial_contents.len() <= size as usize);
|
||||
|
||||
let initial_contents_bits = initial_contents.view_bits::<Lsb0>();
|
||||
|
||||
initial_bits[..initial_contents_bits.len()].clone_from_bitslice(initial_contents_bits);
|
||||
|
||||
mem.initial_value_bit_slice(initial_bits.intern_deref());
|
||||
}
|
||||
|
||||
let read_port = mem.new_read_port();
|
||||
|
||||
connect(read_port.addr, read_port.ty().addr.zero());
|
||||
connect(read_port.clk, cd.clk);
|
||||
connect(read_port.en, false);
|
||||
|
||||
let write_port = mem.new_write_port();
|
||||
|
||||
connect(write_port.addr, write_port.ty().addr.zero());
|
||||
connect(write_port.clk, cd.clk);
|
||||
connect(write_port.en, false);
|
||||
connect(write_port.data, repeat(0u8, bus_width_in_bytes));
|
||||
connect(write_port.mask, repeat(false, bus_width_in_bytes));
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct OpStart<C: PhantomConstGet<MemoryInterfaceConfig>> {
|
||||
sram_addr: HdlOption<UInt<64>>,
|
||||
start: MemoryOperationStart<C>,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
let before_rw_reg = reg_builder()
|
||||
.clock_domain(cd)
|
||||
.reset(HdlOption[OpStart[config]].HdlNone());
|
||||
#[hdl]
|
||||
let next_before_rw = wire(before_rw_reg.ty());
|
||||
|
||||
#[hdl]
|
||||
let rw_reg = reg_builder()
|
||||
.clock_domain(cd)
|
||||
.reset(HdlOption[OpStart[config]].HdlNone());
|
||||
#[hdl]
|
||||
let next_rw = wire(rw_reg.ty());
|
||||
|
||||
#[hdl]
|
||||
let finished_reg = reg_builder()
|
||||
.clock_domain(cd)
|
||||
.reset(HdlOption[MemoryOperationFinish[config]].HdlNone());
|
||||
#[hdl]
|
||||
let next_finished = wire(finished_reg.ty());
|
||||
|
||||
connect(next_before_rw, next_before_rw.ty().HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(start) = input_interface.start.data {
|
||||
#[hdl]
|
||||
let sram_addr_unchecked: UInt<64> = wire();
|
||||
connect_any(sram_addr_unchecked, start.addr - address_range_start);
|
||||
#[hdl]
|
||||
let sram_addr = wire();
|
||||
#[hdl]
|
||||
if sram_addr_unchecked.cmp_lt(size) {
|
||||
connect(sram_addr, HdlSome(sram_addr_unchecked));
|
||||
} else {
|
||||
connect(sram_addr, HdlNone());
|
||||
}
|
||||
connect(
|
||||
next_before_rw,
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
OpStart::<_> { sram_addr, start },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
connect(next_rw, before_rw_reg);
|
||||
|
||||
connect(next_finished, next_finished.ty().HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(rw) = rw_reg {
|
||||
#[hdl]
|
||||
if let HdlSome(sram_addr) = rw.sram_addr {
|
||||
let addr = sram_addr >> config.get().log2_bus_width_in_bytes as usize;
|
||||
connect_any(read_port.addr, addr);
|
||||
connect_any(write_port.addr, addr);
|
||||
let kind = MemoryOperationFinishKind.Success(rw.start.kind);
|
||||
#[hdl]
|
||||
match rw.start.kind {
|
||||
MemoryOperationKind::Read => {
|
||||
connect(read_port.en, true);
|
||||
connect(
|
||||
next_finished,
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
MemoryOperationFinish::<_> {
|
||||
kind,
|
||||
read_data: read_port.data,
|
||||
config,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
MemoryOperationKind::Write => {
|
||||
connect(write_port.en, true);
|
||||
connect(write_port.data, rw.start.write_data);
|
||||
connect(write_port.mask, rw.start.rw_mask);
|
||||
connect(
|
||||
next_finished,
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
MemoryOperationFinish::<_> {
|
||||
kind,
|
||||
read_data: repeat(0u8, bus_width_in_bytes),
|
||||
config,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connect(
|
||||
next_finished,
|
||||
HdlSome(
|
||||
#[hdl]
|
||||
MemoryOperationFinish::<_> {
|
||||
kind: MemoryOperationFinishKind.Error(MemoryOperationErrorKind.Generic()),
|
||||
read_data: repeat(0u8, bus_width_in_bytes),
|
||||
config,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
connect(input_interface.finish.data, finished_reg);
|
||||
|
||||
connect(input_interface.start.ready, input_interface.finish.ready);
|
||||
#[hdl]
|
||||
if input_interface.finish.ready {
|
||||
connect(before_rw_reg, next_before_rw);
|
||||
connect(rw_reg, next_rw);
|
||||
connect(finished_reg, next_finished);
|
||||
}
|
||||
|
||||
connect(
|
||||
input_interface.next_op_ids,
|
||||
input_interface.ty().next_op_ids.HdlNone(),
|
||||
);
|
||||
}
|
||||
|
||||
#[hdl_module]
|
||||
pub fn main_memory_and_io(
|
||||
config: PhantomConst<MemoryInterfaceConfig>,
|
||||
clock_input_properties: PhantomConst<peripherals::ClockInputProperties>,
|
||||
sram_address_range: AddressRange,
|
||||
sram_initial_contents: impl AsRef<[u8]>,
|
||||
uart_start_address: u64,
|
||||
uart_baud_rate: f64,
|
||||
uart_receiver_queue_size: NonZeroUsize,
|
||||
) {
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||
m.input(MemoryInterface[config]);
|
||||
#[hdl]
|
||||
let uart: peripherals::Uart = m.output();
|
||||
|
||||
let mut sram_config = *config.get();
|
||||
sram_config.address_range = sram_address_range;
|
||||
sram_config = self::sram_config(sram_config);
|
||||
let sram_config = PhantomConst::new_sized(sram_config);
|
||||
|
||||
#[hdl]
|
||||
let sram = instance(sram(sram_config, sram_initial_contents.as_ref()));
|
||||
|
||||
connect(sram.cd, cd);
|
||||
|
||||
let uart_config = PhantomConst::new_sized(simple_uart::simple_uart_memory_interface_config(
|
||||
config.get().op_id_width,
|
||||
Wrapping(uart_start_address),
|
||||
));
|
||||
|
||||
#[hdl]
|
||||
let simple_uart = instance(simple_uart::simple_uart(
|
||||
uart_config,
|
||||
clock_input_properties,
|
||||
uart_baud_rate,
|
||||
uart_receiver_queue_size,
|
||||
));
|
||||
|
||||
connect(simple_uart.cd, cd);
|
||||
connect(uart, simple_uart.uart);
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct OutputInterfaces<
|
||||
SramConfig: PhantomConstGet<MemoryInterfaceConfig>,
|
||||
UartConfig: PhantomConstGet<MemoryInterfaceConfig>,
|
||||
> {
|
||||
sram: MemoryInterface<SramConfig>,
|
||||
uart: MemoryInterface<UartConfig>,
|
||||
}
|
||||
|
||||
impl<
|
||||
SramConfig: Type + PhantomConstGet<MemoryInterfaceConfig>,
|
||||
UartConfig: Type + PhantomConstGet<MemoryInterfaceConfig>,
|
||||
> MemoryInterfacesBundle for OutputInterfaces<SramConfig, UartConfig>
|
||||
{
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
let adapter = instance(memory_interface_adapter_no_split(
|
||||
config,
|
||||
OutputInterfaces[sram_config][uart_config],
|
||||
));
|
||||
|
||||
connect(adapter.cd, cd);
|
||||
connect(adapter.input_interface, memory_interface);
|
||||
connect(sram.input_interface, adapter.output_interfaces.sram);
|
||||
connect(simple_uart.memory_interface, adapter.output_interfaces.uart);
|
||||
}
|
||||
|
|
|
|||
235875
crates/cpu/tests/expected/main_memory_and_io.vcd
generated
Normal file
235875
crates/cpu/tests/expected/main_memory_and_io.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
305
crates/cpu/tests/main_memory_and_io.rs
Normal file
305
crates/cpu/tests/main_memory_and_io.rs
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use cpu::{
|
||||
main_memory_and_io::{
|
||||
AddressRange, MemoryInterface, MemoryInterfaceConfig, MemoryOperationErrorKind,
|
||||
MemoryOperationFinish, MemoryOperationFinishKind, MemoryOperationKind,
|
||||
MemoryOperationStart, main_memory_and_io, simple_uart::SIMPLE_UART_TRANSMIT_OFFSET,
|
||||
},
|
||||
next_pc::FETCH_BLOCK_ID_WIDTH,
|
||||
};
|
||||
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
|
||||
use std::num::{NonZeroU64, NonZeroUsize, Wrapping};
|
||||
|
||||
const CLOCK_FREQUENCY: f64 = 2_000_000.0;
|
||||
|
||||
const fn half_period(frequency: f64) -> SimDuration {
|
||||
SimDuration::from_attos((SimDuration::from_secs(1).as_attos() as f64 / frequency / 2.0) as u128)
|
||||
}
|
||||
|
||||
const CLOCK_HALF_PERIOD: SimDuration = half_period(CLOCK_FREQUENCY);
|
||||
|
||||
fn clock_input_properties() -> PhantomConst<peripherals::ClockInputProperties> {
|
||||
peripherals::ClockInput::new(CLOCK_FREQUENCY).properties
|
||||
}
|
||||
|
||||
const SRAM_START: u64 = 0x1000;
|
||||
const SRAM_SIZE: NonZeroU64 = NonZeroU64::new(0x30).unwrap();
|
||||
const UART_START: u64 = 0x100000;
|
||||
const UART_BAUD_RATE: f64 = 115200.0;
|
||||
const UART_RECEIVER_QUEUE_SIZE: NonZeroUsize = NonZeroUsize::new(16).unwrap();
|
||||
const SRAM_INITIAL_CONTENTS: &[u8; SRAM_SIZE.get() as usize] =
|
||||
b"This is a main memory and I/O test. ";
|
||||
const SRAM_WRITING_CONTENTS: &[u8; SRAM_SIZE.get() as usize] =
|
||||
b"Testing. Hello World. abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
const CONFIG: MemoryInterfaceConfig =
|
||||
MemoryInterfaceConfig::new(3, 8, FETCH_BLOCK_ID_WIDTH, AddressRange::Full);
|
||||
const BUS_WIDTH_IN_BYTES: usize = CONFIG.bus_width_in_bytes();
|
||||
|
||||
fn config() -> PhantomConst<MemoryInterfaceConfig> {
|
||||
PhantomConst::new_sized(CONFIG)
|
||||
}
|
||||
|
||||
#[hdl_module]
|
||||
fn test_harness() {
|
||||
let config = config();
|
||||
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||
m.input(MemoryInterface[config]);
|
||||
|
||||
#[hdl]
|
||||
let dut = instance(main_memory_and_io(
|
||||
config,
|
||||
clock_input_properties(),
|
||||
AddressRange::Limited {
|
||||
start: Wrapping(SRAM_START),
|
||||
size: SRAM_SIZE,
|
||||
},
|
||||
SRAM_INITIAL_CONTENTS,
|
||||
UART_START,
|
||||
UART_BAUD_RATE,
|
||||
UART_RECEIVER_QUEUE_SIZE,
|
||||
));
|
||||
connect(dut.cd, cd);
|
||||
connect(dut.memory_interface, memory_interface);
|
||||
connect(dut.uart.rx, dut.uart.tx); // loop back for testing
|
||||
}
|
||||
|
||||
struct MainMemoryAndIoTester {
|
||||
sim: Simulation<test_harness>,
|
||||
next_op_id: u64,
|
||||
}
|
||||
|
||||
impl MainMemoryAndIoTester {
|
||||
fn reset(&mut self) {
|
||||
let sim = &mut self.sim;
|
||||
sim.write(
|
||||
sim.io().memory_interface.start.data,
|
||||
sim.io().ty().memory_interface.start.data.HdlNone(),
|
||||
);
|
||||
sim.write(sim.io().memory_interface.finish.ready, false);
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, true);
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, false);
|
||||
}
|
||||
fn wait(&mut self, cycles: u64) {
|
||||
let sim = &mut self.sim;
|
||||
sim.write(
|
||||
sim.io().memory_interface.start.data,
|
||||
sim.io().ty().memory_interface.start.data.HdlNone(),
|
||||
);
|
||||
sim.write(sim.io().memory_interface.finish.ready, false);
|
||||
for _ in 0..cycles {
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
#[hdl]
|
||||
fn try_rw_op(
|
||||
&mut self,
|
||||
addr: u64,
|
||||
write: Option<[u8; BUS_WIDTH_IN_BYTES]>,
|
||||
rw_mask: [bool; BUS_WIDTH_IN_BYTES],
|
||||
max_wait: u64,
|
||||
) -> Result<[u8; BUS_WIDTH_IN_BYTES], SimValue<MemoryOperationErrorKind>> {
|
||||
let config = config();
|
||||
let sim = &mut self.sim;
|
||||
let op_id = self.next_op_id.cast_to(UInt[FETCH_BLOCK_ID_WIDTH]);
|
||||
self.next_op_id += 1;
|
||||
sim.write(
|
||||
sim.io().memory_interface.start.data,
|
||||
#[hdl(sim)]
|
||||
(sim.io().ty().memory_interface.start.data).HdlSome(match write {
|
||||
Some(write) =>
|
||||
{
|
||||
#[hdl(sim)]
|
||||
MemoryOperationStart::<_> {
|
||||
kind: #[hdl(sim)]
|
||||
MemoryOperationKind.Write(),
|
||||
addr,
|
||||
write_data: &write[..],
|
||||
rw_mask: &rw_mask[..],
|
||||
op_id,
|
||||
config,
|
||||
}
|
||||
}
|
||||
None =>
|
||||
{
|
||||
#[hdl(sim)]
|
||||
MemoryOperationStart::<_> {
|
||||
kind: #[hdl(sim)]
|
||||
MemoryOperationKind.Read(),
|
||||
addr,
|
||||
write_data: &[0u8; BUS_WIDTH_IN_BYTES][..],
|
||||
rw_mask: &rw_mask[..],
|
||||
op_id,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
sim.write(sim.io().memory_interface.finish.ready, true);
|
||||
let mut cleared_start = false;
|
||||
for _ in 0..max_wait {
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
let start_ready = sim.read_bool(sim.io().memory_interface.start.ready);
|
||||
let finish_data = sim.read(sim.io().memory_interface.finish.data);
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(CLOCK_HALF_PERIOD);
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
if start_ready && !cleared_start {
|
||||
cleared_start = true;
|
||||
sim.write(
|
||||
sim.io().memory_interface.start.data,
|
||||
sim.io().ty().memory_interface.start.data.HdlNone(),
|
||||
);
|
||||
}
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(finish) = finish_data {
|
||||
#[hdl(sim)]
|
||||
let MemoryOperationFinish::<_> {
|
||||
kind,
|
||||
read_data,
|
||||
config: _,
|
||||
} = finish;
|
||||
#[hdl(sim)]
|
||||
match kind {
|
||||
MemoryOperationFinishKind::Success(_) => {
|
||||
return Ok(std::array::from_fn(|i| read_data[i].as_int()));
|
||||
}
|
||||
MemoryOperationFinishKind::Error(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
panic!("waited too many cycles");
|
||||
}
|
||||
#[track_caller]
|
||||
fn try_read<const N: usize>(
|
||||
&mut self,
|
||||
addr: u64,
|
||||
max_wait: u64,
|
||||
) -> Result<[u8; N], SimValue<MemoryOperationErrorKind>> {
|
||||
const { assert!(N <= BUS_WIDTH_IN_BYTES) }
|
||||
let bus_addr = addr - addr % BUS_WIDTH_IN_BYTES as u64;
|
||||
let mut rw_mask = [false; BUS_WIDTH_IN_BYTES];
|
||||
let start = (addr % BUS_WIDTH_IN_BYTES as u64) as usize;
|
||||
rw_mask[start..start + N].fill(true);
|
||||
self.try_rw_op(bus_addr, None, rw_mask, max_wait)
|
||||
.map(|v| std::array::from_fn(|i| v[i + start]))
|
||||
}
|
||||
#[track_caller]
|
||||
fn read<const N: usize>(&mut self, addr: u64, max_wait: u64) -> [u8; N] {
|
||||
self.try_read(addr, max_wait).expect("read returned error")
|
||||
}
|
||||
#[track_caller]
|
||||
fn try_write<const N: usize>(
|
||||
&mut self,
|
||||
addr: u64,
|
||||
data: [u8; N],
|
||||
max_wait: u64,
|
||||
) -> Result<(), SimValue<MemoryOperationErrorKind>> {
|
||||
const { assert!(N <= BUS_WIDTH_IN_BYTES) }
|
||||
let bus_addr = addr - addr % BUS_WIDTH_IN_BYTES as u64;
|
||||
let mut rw_mask = [false; BUS_WIDTH_IN_BYTES];
|
||||
let mut write_data = [0u8; BUS_WIDTH_IN_BYTES];
|
||||
let start = (addr % BUS_WIDTH_IN_BYTES as u64) as usize;
|
||||
rw_mask[start..start + N].fill(true);
|
||||
write_data[start..start + N].copy_from_slice(&data);
|
||||
self.try_rw_op(bus_addr, Some(write_data), rw_mask, max_wait)?;
|
||||
Ok(())
|
||||
}
|
||||
#[track_caller]
|
||||
fn write<const N: usize>(&mut self, addr: u64, data: [u8; N], max_wait: u64) {
|
||||
self.try_write(addr, data, max_wait)
|
||||
.expect("read returned error")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[hdl]
|
||||
fn test_main_memory_and_io() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(test_harness());
|
||||
let writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
struct DumpVcdOnDrop {
|
||||
writer: Option<RcWriter>,
|
||||
}
|
||||
impl Drop for DumpVcdOnDrop {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mut writer) = self.writer.take() {
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut writer = DumpVcdOnDrop {
|
||||
writer: Some(writer),
|
||||
};
|
||||
let mut tester = MainMemoryAndIoTester { sim, next_op_id: 0 };
|
||||
tester.reset();
|
||||
|
||||
const UART_MESSAGE: &str = "Test.";
|
||||
for b in UART_MESSAGE.bytes() {
|
||||
tester.write(UART_START + SIMPLE_UART_TRANSMIT_OFFSET, [b], 200);
|
||||
}
|
||||
|
||||
for (i, expected_chunk) in SRAM_INITIAL_CONTENTS
|
||||
.as_chunks::<BUS_WIDTH_IN_BYTES>()
|
||||
.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let addr = (i as u64 * BUS_WIDTH_IN_BYTES as u64) + SRAM_START;
|
||||
let chunk = tester.read::<BUS_WIDTH_IN_BYTES>(addr, 10);
|
||||
assert_eq!(chunk, *expected_chunk);
|
||||
}
|
||||
|
||||
for (i, chunk) in SRAM_WRITING_CONTENTS
|
||||
.as_chunks::<BUS_WIDTH_IN_BYTES>()
|
||||
.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let addr = (i as u64 * BUS_WIDTH_IN_BYTES as u64) + SRAM_START;
|
||||
tester.write(addr, *chunk, 10);
|
||||
}
|
||||
|
||||
for (i, expected_chunk) in SRAM_WRITING_CONTENTS
|
||||
.as_chunks::<BUS_WIDTH_IN_BYTES>()
|
||||
.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let addr = (i as u64 * BUS_WIDTH_IN_BYTES as u64) + SRAM_START;
|
||||
let chunk = tester.read::<BUS_WIDTH_IN_BYTES>(addr, 10);
|
||||
assert_eq!(chunk, *expected_chunk);
|
||||
}
|
||||
|
||||
tester.wait(400);
|
||||
|
||||
for expected_byte in UART_MESSAGE.bytes() {
|
||||
let [byte] = tester.read(UART_START + SIMPLE_UART_TRANSMIT_OFFSET, 10);
|
||||
assert_eq!(byte, expected_byte);
|
||||
}
|
||||
|
||||
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("expected/main_memory_and_io.vcd") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue