forked from libre-chip/cpu
add test for simple_uart::receiver
This commit is contained in:
parent
b07ef2b363
commit
61e8ed96b1
3 changed files with 116873 additions and 358 deletions
116286
crates/cpu/tests/expected/receiver_and_uart_clock_gen.vcd
generated
Normal file
116286
crates/cpu/tests/expected/receiver_and_uart_clock_gen.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,16 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use cpu::main_memory_and_io::simple_uart::{receiver_no_queue, transmitter, uart_clock_gen};
|
use cpu::main_memory_and_io::simple_uart::{
|
||||||
|
ReceiverQueueStatus, receiver, receiver_no_queue, transmitter, uart_clock_gen,
|
||||||
|
};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
sim::vcd::VcdWriterDecls,
|
sim::vcd::VcdWriterDecls,
|
||||||
util::{RcWriter, ready_valid::ReadyValid},
|
util::{RcWriter, ready_valid::ReadyValid},
|
||||||
};
|
};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
const fn half_period(frequency: f64) -> SimDuration {
|
const fn half_period(frequency: f64) -> SimDuration {
|
||||||
SimDuration::from_attos((SimDuration::from_secs(1).as_attos() as f64 / frequency / 2.0) as u128)
|
SimDuration::from_attos((SimDuration::from_secs(1).as_attos() as f64 / frequency / 2.0) as u128)
|
||||||
|
|
@ -190,7 +193,7 @@ fn test_transmitter_and_uart_clock_gen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
#[hdl_module(extern)]
|
||||||
fn receiver_test_cases(text: Interned<str>) {
|
fn receiver_no_queue_test_cases(text: Interned<str>) {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let rx: Bool = m.output();
|
let rx: Bool = m.output();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -249,7 +252,7 @@ fn receiver_no_queue_and_uart_clock_gen(text: Interned<str>) {
|
||||||
let reference_baud_rate_clk: Clock = m.output();
|
let reference_baud_rate_clk: Clock = m.output();
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let test_cases = instance(receiver_test_cases(text));
|
let test_cases = instance(receiver_no_queue_test_cases(text));
|
||||||
connect(rx, test_cases.rx);
|
connect(rx, test_cases.rx);
|
||||||
connect(cur_byte, test_cases.cur_byte);
|
connect(cur_byte, test_cases.cur_byte);
|
||||||
|
|
||||||
|
|
@ -334,5 +337,231 @@ fn test_receiver_no_queue_and_uart_clock_gen() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test receiver()
|
#[hdl_module(extern)]
|
||||||
|
fn receiver_test_cases() {
|
||||||
|
#[hdl]
|
||||||
|
let clk: Clock = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let rx: Bool = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let cur_byte: UInt<8> = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let ready: Bool = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let text: SimOnly<String> = m.input();
|
||||||
|
m.register_clock_for_past(clk);
|
||||||
|
m.extern_module_simulation_fn(
|
||||||
|
(clk, rx, cur_byte, ready, text),
|
||||||
|
async |(clk, rx, cur_byte, ready, text), mut sim| {
|
||||||
|
sim.write(rx, true).await;
|
||||||
|
sim.write(ready, false).await;
|
||||||
|
loop {
|
||||||
|
sim.write(cur_byte, 0u8).await;
|
||||||
|
sim.wait_for_clock_edge(clk).await;
|
||||||
|
sim.write(ready, true).await;
|
||||||
|
let text = loop {
|
||||||
|
sim.wait_for_clock_edge(clk).await;
|
||||||
|
let text = sim.read_past(text, clk).await;
|
||||||
|
if text.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break text;
|
||||||
|
};
|
||||||
|
sim.write(ready, false).await;
|
||||||
|
sim.wait_for_clock_edge(clk).await;
|
||||||
|
for byte in text.bytes() {
|
||||||
|
sim.write(cur_byte, byte).await;
|
||||||
|
sim.write(rx, false).await; // start bit
|
||||||
|
sim.advance_time(BAUD_PERIOD).await;
|
||||||
|
for i in 0..u8::BITS {
|
||||||
|
sim.write(rx, byte & (1 << i) != 0).await; // data bit
|
||||||
|
sim.advance_time(BAUD_PERIOD).await;
|
||||||
|
}
|
||||||
|
sim.write(rx, true).await; // stop bit
|
||||||
|
sim.advance_time(BAUD_PERIOD).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
fn receiver_and_uart_clock_gen(queue_capacity: NonZeroUsize) {
|
||||||
|
#[hdl]
|
||||||
|
let cd: ClockDomain = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let rx: Bool = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let reference_baud_rate_clk: Clock = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let cur_byte: UInt<8> = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let ready: Bool = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let text: SimOnly<String> = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let output_byte: ReadyValid<UInt<8>> = m.output();
|
||||||
|
#[hdl]
|
||||||
|
let queue_status: ReceiverQueueStatus = m.output();
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let test_cases = instance(receiver_test_cases());
|
||||||
|
connect(test_cases.clk, cd.clk);
|
||||||
|
connect(rx, test_cases.rx);
|
||||||
|
connect(cur_byte, test_cases.cur_byte);
|
||||||
|
connect(ready, test_cases.ready);
|
||||||
|
connect(test_cases.text, text);
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// only need one stage of synchronization in the simulator
|
||||||
|
#[hdl]
|
||||||
|
let rx_synchronized = reg_builder().clock_domain(cd).reset(true);
|
||||||
|
|
||||||
|
connect(rx_synchronized, rx);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let receiver = instance(receiver(queue_capacity));
|
||||||
|
connect(receiver.cd, cd);
|
||||||
|
connect(receiver.rx_synchronized, rx_synchronized);
|
||||||
|
connect(output_byte, receiver.output_byte);
|
||||||
|
connect(queue_status, receiver.queue_status);
|
||||||
|
#[hdl]
|
||||||
|
let clock_gen = instance(uart_clock_gen(clock_input_properties(), BAUD_RATE));
|
||||||
|
connect(clock_gen.cd, cd);
|
||||||
|
connect(receiver.tick, clock_gen.tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[hdl]
|
||||||
|
fn test_receiver_and_uart_clock_gen() {
|
||||||
|
let _n = SourceLocation::normalize_files_for_tests();
|
||||||
|
const QUEUE_SIZE: NonZeroUsize = NonZeroUsize::new(4).expect("not zero");
|
||||||
|
let m = receiver_and_uart_clock_gen(QUEUE_SIZE);
|
||||||
|
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 empty_text = SimOnlyValue::new(String::new());
|
||||||
|
sim.write(sim.io().text, &empty_text);
|
||||||
|
sim.write_bool(sim.io().output_byte.ready, false);
|
||||||
|
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);
|
||||||
|
const TEST_TEXT: &str = "Test\n";
|
||||||
|
const { assert!(TEST_TEXT.len() > QUEUE_SIZE.get()) }
|
||||||
|
for bytes_before_dequeue in 0..=TEST_TEXT.len() {
|
||||||
|
println!("\nbytes_before_dequeue={bytes_before_dequeue}");
|
||||||
|
for _cycle in 0..10 {
|
||||||
|
if sim.read_bool(sim.io().ready) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
assert!(sim.read_bool(sim.io().ready));
|
||||||
|
let text = SimOnlyValue::new(TEST_TEXT[..bytes_before_dequeue].to_string());
|
||||||
|
dbg!(&text);
|
||||||
|
sim.write(sim.io().text, &text);
|
||||||
|
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(sim.io().text, &empty_text);
|
||||||
|
for _cycle in 0..2000 {
|
||||||
|
if sim.read_bool(sim.io().ready) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
assert!(sim.read_bool(sim.io().ready));
|
||||||
|
for _cycle in 0..10 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
dbg!(bytes_before_dequeue);
|
||||||
|
#[hdl(sim)]
|
||||||
|
match dbg!(sim.read(sim.io().queue_status)) {
|
||||||
|
ReceiverQueueStatus::QueueEmpty => {
|
||||||
|
assert_eq!(bytes_before_dequeue, 0);
|
||||||
|
}
|
||||||
|
ReceiverQueueStatus::QueueNotEmpty => {
|
||||||
|
assert!(bytes_before_dequeue > 0 && bytes_before_dequeue < QUEUE_SIZE.get() - 1);
|
||||||
|
}
|
||||||
|
ReceiverQueueStatus::QueueAlmostFull => {
|
||||||
|
assert_eq!(bytes_before_dequeue, QUEUE_SIZE.get() - 1);
|
||||||
|
}
|
||||||
|
ReceiverQueueStatus::QueueFull => {
|
||||||
|
assert_eq!(bytes_before_dequeue, QUEUE_SIZE.get());
|
||||||
|
}
|
||||||
|
ReceiverQueueStatus::QueueOverflowed => {
|
||||||
|
assert!(bytes_before_dequeue > QUEUE_SIZE.get());
|
||||||
|
}
|
||||||
|
ReceiverQueueStatus::Unknown => unreachable!(),
|
||||||
|
}
|
||||||
|
let expected_text = &TEST_TEXT[..bytes_before_dequeue.min(QUEUE_SIZE.get())];
|
||||||
|
dbg!(expected_text);
|
||||||
|
let mut received_text = String::new();
|
||||||
|
for expected_byte in expected_text.bytes() {
|
||||||
|
sim.write_bool(sim.io().output_byte.ready, true);
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(byte) = sim.read(sim.io().output_byte.data) {
|
||||||
|
let byte = byte.as_int();
|
||||||
|
assert_eq!(
|
||||||
|
byte, expected_byte,
|
||||||
|
"byte={:#x} {:?}, expected_byte={:#x} {:?}",
|
||||||
|
byte, byte as char, expected_byte, expected_byte as char,
|
||||||
|
);
|
||||||
|
assert!(byte.is_ascii());
|
||||||
|
received_text.push(byte as char);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#[hdl(sim)]
|
||||||
|
if let HdlSome(_) = sim.read(sim.io().output_byte.data) {
|
||||||
|
panic!("queue should be empty now");
|
||||||
|
}
|
||||||
|
sim.write_bool(sim.io().output_byte.ready, false);
|
||||||
|
}
|
||||||
|
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
||||||
|
println!("####### VCD:\n{vcd}\n#######");
|
||||||
|
if vcd != include_str!("expected/receiver_and_uart_clock_gen.vcd") {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: test simple_uart()
|
// TODO: test simple_uart()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue