use crate::prelude::*;
use crate::seamap::SeaMap;
use futures::future::BoxFuture;
use intmap::IntMap;
use lazy_mut::lazy_mut;
use std::cell::{Ref, RefMut};
use std::collections::VecDeque;
lazy_mut! { pub(crate) static mut ASSERTION_MAP: SeaMap<String, Assertion> = SeaMap::new(); }
type Generator = RstbObj<Box<dyn Fn() -> BoxFuture<'static, RstbResult>>>;
type SequenceGenerator = RstbObj<Box<dyn Fn(AssertionContext) -> BoxFuture<'static, RstbResult>>>;
#[macro_export]
macro_rules! add_assertion {
($name: expr, $trigger: expr, $condition: expr, $checker: expr, $keep_hist: expr, $hist_depth: expr) => {
Assertion::add_assertion(
$name,
$trigger,
move || $condition.boxed(),
move |ctx| $checker(ctx).boxed(),
$keep_hist,
$hist_depth,
);
};
($name: expr, $trigger: expr, $condition: expr, $checker: expr) => {
Assertion::add_assertion(
$name,
$trigger,
move || $condition.boxed(),
move |ctx| $checker(ctx).boxed(),
vec![],
0,
);
};
}
#[macro_export]
macro_rules! check {
($bool: expr) => {
match $bool {
true => Ok(Val::None),
false => Err(Val::None),
}
};
}
struct SigHist {
enabled: bool,
history: IntMap<VecDeque<Val>>,
task_hdl: Option<JoinHandle>,
}
struct Stats {
triggered: u32,
failed: u32,
passed: u32,
}
#[derive(Clone)]
struct AssertionContextInner {
trigger: Trigger,
history: RstbObj<SigHist>,
dut: SimObject,
}
#[derive(Clone)]
pub struct AssertionContext(AssertionContextInner);
impl AssertionContext {
pub fn trig(&self) -> Trigger {
self.0.trigger.clone()
}
pub fn dut(&self) -> SimObject {
self.0.dut
}
pub fn sig_hist(&self, sig: SimObject, clocks: usize) -> Val {
let hist_ref = self.hist();
let hist = hist_ref
.history
.get(sig.handle() as u64)
.expect("Could not find history in context.");
hist[clocks].clone()
}
fn hist(&self) -> Ref<SigHist> {
self.0.history.get()
}
pub fn rose(&self, sig: SimObject) -> bool {
sig.u32() == 1 && self.sig_hist(sig, 1) != Val::Int(1)
}
pub fn fell(&self, sig: SimObject) -> bool {
sig.u32() == 0 && self.sig_hist(sig, 1) != Val::Int(0)
}
pub fn stable(&self, sig: SimObject) -> bool {
self.sig_hist(sig, 1) == Val::Int(sig.u32())
}
pub fn changed(&self, sig: SimObject) -> bool {
self.sig_hist(sig, 1) != Val::Int(sig.u32())
}
fn hist_mut(&self) -> RefMut<SigHist> {
self.0.history.get_mut()
}
}
pub struct Assertion {
name: String,
enabled: RstbObj<bool>,
condition: Generator,
checker: SequenceGenerator,
ctx: AssertionContext,
stats: RstbObj<Stats>,
}
impl Assertion {
#[allow(unreachable_code)]
async fn run(&'static self) -> RstbResult {
loop {
if self.ctx.hist().task_hdl.is_none() && self.ctx.hist().enabled {
let task_hdl = Task::spawn(async move {
loop {
self.ctx.trig().await;
Trigger::read_only_prio().await;
for (hdl, val_vec) in self.ctx.hist_mut().history.iter_mut() {
val_vec.pop_back();
let obj = SimObject::from_handle(*hdl as usize)
.expect("Could not resolve SimObject from handle.");
val_vec.push_front(Val::Int(obj.u32()));
}
}
Ok(Val::None)
});
self.ctx.hist_mut().task_hdl = Some(task_hdl);
}
self.ctx.trig().await;
Trigger::read_only().await;
if *self.enabled.get() {
let condition = (self.condition.get())();
let checker = (self.checker.get())(self.ctx.clone());
let _task = Task::spawn(async move {
condition.await?;
self.trigger();
let r = checker.await;
match r {
Ok(_) => self.pass(),
Err(_) => self.fail(),
}
Ok(Val::None)
});
}
}
Ok(Val::None)
}
pub fn add_assertion(
name: &str,
trigger: Trigger,
condition: impl Fn() -> BoxFuture<'static, RstbResult> + 'static,
checker: impl Fn(AssertionContext) -> BoxFuture<'static, RstbResult> + 'static,
keep_hist: Vec<SimObject>,
hist_depth: u32,
) {
let mut sig_hist = IntMap::new();
for obj in keep_hist {
let vec = vec![Val::None; (hist_depth + 1) as usize];
sig_hist.insert(obj.handle() as u64, vec.into());
}
let assertion = Self {
name: name.to_string(),
enabled: RstbObj::new(true),
ctx: AssertionContext(AssertionContextInner {
dut: SimObject::get_root().unwrap(),
trigger,
history: RstbObj::new(SigHist {
enabled: hist_depth != 0,
history: sig_hist,
task_hdl: None,
}),
}),
condition: RstbObj::new(Box::new(condition)),
checker: RstbObj::new(Box::new(checker)),
stats: RstbObj::new(Stats {
triggered: 0,
passed: 0,
failed: 0,
}),
};
unsafe {
ASSERTION_MAP.init();
if !ASSERTION_MAP.contains_key(name) {
ASSERTION_MAP.insert(name.to_string(), assertion);
}
}
}
fn trigger(&self) {
self.stats.get_mut().triggered += 1;
}
fn fail(&self) {
self.stats.get_mut().failed += 1;
}
fn pass(&self) {
self.stats.get_mut().passed += 1;
}
pub fn enable(&self) {
*self.enabled.get_mut() = true;
}
pub fn disable(&self) {
*self.enabled.get_mut() = false;
}
pub fn result_str(&self) -> String {
let stats = self.stats.get();
format!(
"Assertion {}: Triggered: {}, Passed: {}, Failed: {}.",
self.name, stats.triggered, stats.passed, stats.failed
)
}
pub fn result(&self) -> RstbResult {
let s = self.stats.get();
match s.triggered == s.passed && s.failed == 0 {
true => Ok(Val::None),
false => Err(Val::None)
}
}
}
pub fn run_all_assertions() {
unsafe {
ASSERTION_MAP.init();
for (_, assertion) in ASSERTION_MAP.iter() {
let fut = assertion.run();
Task::spawn(fut);
}
}
}
pub fn disable_all_assertions() {
unsafe {
ASSERTION_MAP.init();
for (_, assertion) in ASSERTION_MAP.iter() {
assertion.disable()
}
}
}
pub fn enable_all_assertions() {
unsafe {
ASSERTION_MAP.init();
for (_, assertion) in ASSERTION_MAP.iter() {
assertion.enable()
}
}
}
pub fn run_assertion(name: &str) {
unsafe {
ASSERTION_MAP.init();
if ASSERTION_MAP.contains_key(name) {
let fut = ASSERTION_MAP.get(name).unwrap().run();
Task::spawn(fut);
} else {
panic!("Assertion {} not previously defined!", name);
}
}
}
pub(crate) fn tear_down_assertions() {
unsafe {
ASSERTION_MAP.init();
for (_, a) in ASSERTION_MAP.iter() {
let _ = a.ctx.hist_mut().task_hdl.take();
let mut stats = a.stats.get_mut();
stats.failed += stats.triggered - stats.passed - stats.failed;
}
}
}
pub fn print_assertion_stats() {
unsafe {
ASSERTION_MAP.init();
for (_, assertion) in ASSERTION_MAP.iter() {
SIM_IF.log("Assertion results:");
SIM_IF.log(assertion.result_str().as_str());
}
}
}