cpu/crates/cpu/tests/simple_uart.rs

175 lines
5.7 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::main_memory_and_io::simple_uart::{transmitter, uart_clock_gen};
use fayalite::{
prelude::*,
sim::vcd::VcdWriterDecls,
util::{RcWriter, ready_valid::ReadyValid},
};
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_FREQUENCY: f64 = 200_000.0;
const CLOCK_HALF_PERIOD: SimDuration = half_period(CLOCK_FREQUENCY);
const BAUD_RATE: f64 = 9600.0;
const BAUD_HALF_PERIOD: SimDuration = half_period(BAUD_RATE);
const BAUD_PERIOD: SimDuration = BAUD_HALF_PERIOD.saturating_add(BAUD_HALF_PERIOD);
fn clock_input_properties() -> PhantomConst<peripherals::ClockInputProperties> {
peripherals::ClockInput::new(CLOCK_FREQUENCY).properties
}
#[hdl_module(extern)]
fn reference_baud_rate_clk_gen() {
#[hdl]
let reference_baud_rate_clk: Clock = m.output();
m.extern_module_simulation_fn(
reference_baud_rate_clk,
async |reference_baud_rate_clk, mut sim| {
loop {
sim.write_clock(reference_baud_rate_clk, false).await;
sim.advance_time(BAUD_HALF_PERIOD).await;
sim.write_clock(reference_baud_rate_clk, true).await;
sim.advance_time(BAUD_HALF_PERIOD).await;
}
},
);
}
#[hdl_module(extern)]
fn sim_receiver() {
#[hdl]
let tx: Bool = m.input();
#[hdl]
let text_out: SimOnly<String> = m.output();
m.extern_module_simulation_fn((tx, text_out), async |(tx, text_out), mut sim| {
let mut text = SimOnlyValue::new(String::new());
sim.write(text_out, &text).await;
loop {
// wait for the starting edge of the start bit
while sim.read_bool(tx).await {
sim.wait_for_changes([tx], None).await;
}
// wait till the middle of the start bit
sim.advance_time(BAUD_HALF_PERIOD).await;
let mut byte = 0u8;
for i in 0..u8::BITS {
// wait till the middle of the next data bit
sim.advance_time(BAUD_PERIOD).await;
if sim.read_bool(tx).await {
byte |= 1 << i;
}
}
assert!(byte.is_ascii());
text.push(byte as char);
sim.write(text_out, &text).await;
// wait till the middle of the stop bit
sim.advance_time(BAUD_PERIOD).await;
// we're in the middle of the stop bit, the stop bit is a one so we can just loop and wait for
// the next transition to zero which is the next start bit.
}
});
}
#[hdl_module]
fn transmitter_and_uart_clock_gen() {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let tx: Bool = m.output();
#[hdl]
let input_byte: ReadyValid<UInt<8>> = m.input();
#[hdl]
let reference_baud_rate_clk: Clock = m.output();
#[hdl]
let text_out: SimOnly<String> = m.output();
#[hdl]
let sim_receiver = instance(sim_receiver());
connect(sim_receiver.tx, tx);
connect(text_out, sim_receiver.text_out);
#[hdl]
let reference_baud_rate_clk_gen = instance(reference_baud_rate_clk_gen());
connect(
reference_baud_rate_clk,
reference_baud_rate_clk_gen.reference_baud_rate_clk,
);
#[hdl]
let transmitter = instance(transmitter());
connect(transmitter.cd, cd);
connect(transmitter.input_byte, input_byte);
connect(tx, transmitter.tx);
#[hdl]
let clock_gen = instance(uart_clock_gen(clock_input_properties(), BAUD_RATE));
connect(clock_gen.cd, cd);
connect(transmitter.tick, clock_gen.tick);
}
#[test]
#[hdl]
fn test_transmitter_and_uart_clock_gen() {
let _n = SourceLocation::normalize_files_for_tests();
let m = transmitter_and_uart_clock_gen();
let mut sim = Simulation::new(m);
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),
};
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
let text = "Hi!\n";
let mut bytes = text.as_bytes().iter();
for cycle in 0..1000 {
let input_byte = match bytes.clone().next() {
Some(b) if cycle > 10 =>
{
#[hdl(sim)]
HdlSome(b)
}
_ =>
{
#[hdl(sim)]
HdlNone()
}
};
sim.write(sim.io().input_byte.data, &input_byte);
sim.advance_time(CLOCK_HALF_PERIOD);
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(CLOCK_HALF_PERIOD);
if sim.read_bool(sim.io().input_byte.ready) {
#[hdl(sim)]
if let HdlSome(_) = input_byte {
bytes.next();
}
}
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, false);
}
assert_eq!(sim.read(sim.io().text_out).as_str(), text);
assert!(bytes.as_slice().is_empty());
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/transmitter_and_uart_clock_gen.vcd") {
panic!();
}
}
// TODO: test receiver()
// TODO: test simple_uart()