use celox::Simulator;
#[path = "test_utils/mod.rs"]
#[macro_use]
mod test_utils;
all_backends! {
fn test_independent_clock_domains(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (
clk_a: input 'a clock,
clk_b: input 'b clock,
rst: input 'a reset,
da: input 'a logic<8>,
db: input 'b logic<8>,
qa: output 'a logic<8>,
qb: output 'b logic<8>
) {
var ra: 'a logic<8>;
var rb: 'b logic<8>;
always_ff (clk_a, rst) {
if_reset {
ra = 8'd0;
} else {
ra = da;
}
}
unsafe (cdc) {
always_ff (clk_b, rst) {
if_reset {
rb = 8'd0;
} else {
rb = db;
}
}
}
assign qa = ra;
assign qb = rb;
}
"#; }
@build Simulator::builder(code, "Top");
let clk_a = sim.event("clk_a");
let clk_b = sim.event("clk_b");
let rst = sim.signal("rst");
let da = sim.signal("da");
let db = sim.signal("db");
let qa = sim.signal("qa");
let qb = sim.signal("qb");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(da, 0xAAu8);
io.set(db, 0xBBu8);
})
.unwrap();
sim.tick(clk_a).unwrap();
sim.tick(clk_b).unwrap();
assert_eq!(sim.get(qa), 0u8.into());
assert_eq!(sim.get(qb), 0u8.into());
sim.modify(|io| io.set(rst, 1u8)).unwrap();
sim.tick(clk_a).unwrap();
assert_eq!(sim.get(qa), 0xAAu8.into());
assert_eq!(sim.get(qb), 0u8.into());
sim.tick(clk_b).unwrap();
assert_eq!(sim.get(qa), 0xAAu8.into());
assert_eq!(sim.get(qb), 0xBBu8.into());
}
fn test_clock_domain_crossing_pattern(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (
clk_fast: input 'a clock,
clk_slow: input 'b clock,
rst: input 'a reset,
count_out: output 'a logic<4>,
sample_out: output 'b logic<4>
) {
var counter: 'a logic<4>;
var sample: 'b logic<4>;
// Fast domain: counter increments
always_ff (clk_fast, rst) {
if_reset {
counter = 4'd0;
} else {
counter = counter + 4'd1;
}
}
// Slow domain: samples the counter (CDC)
unsafe (cdc) {
always_ff (clk_slow, rst) {
if_reset {
sample = 4'd0;
} else {
sample = counter;
}
}
}
assign count_out = counter;
assign sample_out = sample;
}
"#; }
@build Simulator::builder(code, "Top");
let clk_fast = sim.event("clk_fast");
let clk_slow = sim.event("clk_slow");
let rst = sim.signal("rst");
let count_out = sim.signal("count_out");
let sample_out = sim.signal("sample_out");
sim.modify(|io| io.set(rst, 0u8)).unwrap();
sim.tick(clk_fast).unwrap();
sim.tick(clk_slow).unwrap();
assert_eq!(sim.get(count_out), 0u8.into());
assert_eq!(sim.get(sample_out), 0u8.into());
sim.modify(|io| io.set(rst, 1u8)).unwrap();
sim.tick(clk_fast).unwrap();
sim.tick(clk_fast).unwrap();
sim.tick(clk_fast).unwrap();
assert_eq!(sim.get(count_out), 3u8.into());
assert_eq!(sim.get(sample_out), 0u8.into());
sim.tick(clk_slow).unwrap();
assert_eq!(sim.get(sample_out), 3u8.into());
sim.tick(clk_fast).unwrap();
sim.tick(clk_fast).unwrap();
assert_eq!(sim.get(count_out), 5u8.into());
assert_eq!(sim.get(sample_out), 3u8.into());
}
fn test_separate_resets_per_domain(sim) {
@ignore_on(veryl);
@setup { let code = r#"
module Top (
clk_a: input 'a clock,
clk_b: input 'b clock,
rst_a: input 'a reset,
rst_b: input 'b reset,
da: input 'a logic<8>,
db: input 'b logic<8>,
qa: output 'a logic<8>,
qb: output 'b logic<8>
) {
var ra: 'a logic<8>;
var rb: 'b logic<8>;
always_ff (clk_a, rst_a) {
if_reset {
ra = 8'hAA;
} else {
ra = da;
}
}
always_ff (clk_b, rst_b) {
if_reset {
rb = 8'hBB;
} else {
rb = db;
}
}
assign qa = ra;
assign qb = rb;
}
"#; }
@build Simulator::builder(code, "Top");
let clk_a = sim.event("clk_a");
let clk_b = sim.event("clk_b");
let rst_a = sim.signal("rst_a");
let rst_b = sim.signal("rst_b");
let da = sim.signal("da");
let db = sim.signal("db");
let qa = sim.signal("qa");
let qb = sim.signal("qb");
sim.modify(|io| {
io.set(rst_a, 0u8);
io.set(rst_b, 1u8);
io.set(da, 0x11u8);
io.set(db, 0x22u8);
})
.unwrap();
sim.tick(clk_a).unwrap();
sim.tick(clk_b).unwrap();
assert_eq!(sim.get(qa), 0xAAu8.into()); assert_eq!(sim.get(qb), 0x22u8.into());
sim.modify(|io| {
io.set(rst_a, 1u8);
io.set(rst_b, 0u8);
})
.unwrap();
sim.tick(clk_a).unwrap();
sim.tick(clk_b).unwrap();
assert_eq!(sim.get(qa), 0x11u8.into()); assert_eq!(sim.get(qb), 0xBBu8.into());
}
}