WIP getting queue formal to pass -- passes for capacity <= 2
Some checks failed
/ test (push) Has been cancelled
Some checks failed
/ test (push) Has been cancelled
This commit is contained in:
parent
bc26fe32fd
commit
3e2fb9b94f
|
@ -12,7 +12,14 @@ use clap::{
|
||||||
Parser, Subcommand, ValueEnum, ValueHint,
|
Parser, Subcommand, ValueEnum, ValueHint,
|
||||||
};
|
};
|
||||||
use eyre::{eyre, Report};
|
use eyre::{eyre, Report};
|
||||||
use std::{error, ffi::OsString, fmt, io, path::PathBuf, process};
|
use std::{
|
||||||
|
error,
|
||||||
|
ffi::OsString,
|
||||||
|
fmt::{self, Write},
|
||||||
|
fs, io, mem,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process,
|
||||||
|
};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
||||||
|
@ -246,15 +253,51 @@ pub struct VerilogArgs {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct VerilogOutput {
|
pub struct VerilogOutput {
|
||||||
pub firrtl: FirrtlOutput,
|
pub firrtl: FirrtlOutput,
|
||||||
|
pub verilog_files: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerilogOutput {
|
impl VerilogOutput {
|
||||||
pub fn verilog_file(&self) -> PathBuf {
|
pub fn main_verilog_file(&self) -> PathBuf {
|
||||||
self.firrtl.file_with_ext("v")
|
self.firrtl.file_with_ext("v")
|
||||||
}
|
}
|
||||||
|
fn unadjusted_verilog_file(&self) -> PathBuf {
|
||||||
|
self.firrtl.file_with_ext("unadjusted.v")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerilogArgs {
|
impl VerilogArgs {
|
||||||
|
fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result<VerilogOutput> {
|
||||||
|
let input = fs::read_to_string(output.unadjusted_verilog_file())?;
|
||||||
|
let file_separator_prefix = "\n// ----- 8< ----- FILE \"";
|
||||||
|
let file_separator_suffix = "\" ----- 8< -----\n\n";
|
||||||
|
let mut input = &*input;
|
||||||
|
let main_verilog_file = output.main_verilog_file();
|
||||||
|
let mut file_name: Option<&Path> = Some(&main_verilog_file);
|
||||||
|
loop {
|
||||||
|
let (chunk, next_file_name) = if let Some((chunk, rest)) =
|
||||||
|
input.split_once(file_separator_prefix)
|
||||||
|
{
|
||||||
|
let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else {
|
||||||
|
return Err(CliError(eyre!("parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}")));
|
||||||
|
};
|
||||||
|
input = rest;
|
||||||
|
(chunk, Some(next_file_name.as_ref()))
|
||||||
|
} else {
|
||||||
|
(mem::take(&mut input), None)
|
||||||
|
};
|
||||||
|
let Some(file_name) = mem::replace(&mut file_name, next_file_name) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let file_name = output.firrtl.output_dir.join(file_name);
|
||||||
|
fs::write(&file_name, chunk)?;
|
||||||
|
if let Some(extension) = file_name.extension() {
|
||||||
|
if extension == "v" || extension == "sv" {
|
||||||
|
output.verilog_files.push(file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> {
|
fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> {
|
||||||
let Self {
|
let Self {
|
||||||
firrtl,
|
firrtl,
|
||||||
|
@ -265,14 +308,15 @@ impl VerilogArgs {
|
||||||
} = self;
|
} = self;
|
||||||
let output = VerilogOutput {
|
let output = VerilogOutput {
|
||||||
firrtl: firrtl_output,
|
firrtl: firrtl_output,
|
||||||
|
verilog_files: vec![],
|
||||||
};
|
};
|
||||||
let mut cmd = process::Command::new(firtool);
|
let mut cmd = process::Command::new(firtool);
|
||||||
cmd.arg(output.firrtl.firrtl_file());
|
cmd.arg(output.firrtl.firrtl_file());
|
||||||
cmd.arg("-o");
|
cmd.arg("-o");
|
||||||
cmd.arg(output.verilog_file());
|
cmd.arg(output.unadjusted_verilog_file());
|
||||||
if *debug {
|
if *debug {
|
||||||
cmd.arg("-g");
|
cmd.arg("-g");
|
||||||
cmd.arg("--preserve-values=named");
|
cmd.arg("--preserve-values=all");
|
||||||
}
|
}
|
||||||
if let Some(dialect) = verilog_dialect {
|
if let Some(dialect) = verilog_dialect {
|
||||||
cmd.args(dialect.firtool_extra_args());
|
cmd.args(dialect.firtool_extra_args());
|
||||||
|
@ -281,7 +325,7 @@ impl VerilogArgs {
|
||||||
cmd.current_dir(&output.firrtl.output_dir);
|
cmd.current_dir(&output.firrtl.output_dir);
|
||||||
let status = firrtl.base.run_external_command(cmd)?;
|
let status = firrtl.base.run_external_command(cmd)?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(output)
|
self.process_unadjusted_verilog_file(output)
|
||||||
} else {
|
} else {
|
||||||
Err(CliError(eyre!(
|
Err(CliError(eyre!(
|
||||||
"running {} failed: {status}",
|
"running {} failed: {status}",
|
||||||
|
@ -424,7 +468,7 @@ impl FormalOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormalArgs {
|
impl FormalArgs {
|
||||||
fn sby_contents(&self, output: &FormalOutput) -> String {
|
fn sby_contents(&self, output: &FormalOutput) -> Result<String> {
|
||||||
let Self {
|
let Self {
|
||||||
verilog: _,
|
verilog: _,
|
||||||
sby: _,
|
sby: _,
|
||||||
|
@ -448,15 +492,8 @@ impl FormalArgs {
|
||||||
}
|
}
|
||||||
let space_solver = OptArg(solver.as_ref());
|
let space_solver = OptArg(solver.as_ref());
|
||||||
let smtbmc_options = smtbmc_extra_args.join(" ");
|
let smtbmc_options = smtbmc_extra_args.join(" ");
|
||||||
let verilog_file = output
|
|
||||||
.verilog
|
|
||||||
.verilog_file()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.ok()
|
|
||||||
.expect("verilog file path is not UTF-8");
|
|
||||||
let top_module = &output.verilog.firrtl.top_module;
|
let top_module = &output.verilog.firrtl.top_module;
|
||||||
format!(
|
let mut retval = format!(
|
||||||
"[options]\n\
|
"[options]\n\
|
||||||
mode {mode}\n\
|
mode {mode}\n\
|
||||||
depth {depth}\n\
|
depth {depth}\n\
|
||||||
|
@ -465,18 +502,30 @@ impl FormalArgs {
|
||||||
[engines]\n\
|
[engines]\n\
|
||||||
smtbmc{space_solver} -- -- {smtbmc_options}\n\
|
smtbmc{space_solver} -- -- {smtbmc_options}\n\
|
||||||
\n\
|
\n\
|
||||||
[script]\n\
|
[script]\n"
|
||||||
read_verilog -sv -formal {verilog_file}\n\
|
);
|
||||||
prep -top {top_module}\n
|
for verilog_file in &output.verilog.verilog_files {
|
||||||
"
|
let verilog_file = verilog_file
|
||||||
)
|
.to_str()
|
||||||
|
.ok_or_else(|| CliError(eyre!("verilog file path is not UTF-8")))?;
|
||||||
|
if verilog_file.contains(|ch: char| {
|
||||||
|
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
|
||||||
|
}) {
|
||||||
|
return Err(CliError(eyre!(
|
||||||
|
"verilog file path contains characters that aren't permitted"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap();
|
||||||
|
}
|
||||||
|
writeln!(retval, "prep -top {top_module}").unwrap();
|
||||||
|
Ok(retval)
|
||||||
}
|
}
|
||||||
fn run_impl(&self, verilog_output: VerilogOutput) -> Result<FormalOutput> {
|
fn run_impl(&self, verilog_output: VerilogOutput) -> Result<FormalOutput> {
|
||||||
let output = FormalOutput {
|
let output = FormalOutput {
|
||||||
verilog: verilog_output,
|
verilog: verilog_output,
|
||||||
};
|
};
|
||||||
let sby_file = output.sby_file();
|
let sby_file = output.sby_file();
|
||||||
std::fs::write(&sby_file, self.sby_contents(&output))?;
|
std::fs::write(&sby_file, self.sby_contents(&output)?)?;
|
||||||
let mut cmd = process::Command::new(&self.sby);
|
let mut cmd = process::Command::new(&self.sby);
|
||||||
cmd.arg("-f");
|
cmd.arg("-f");
|
||||||
cmd.arg(sby_file);
|
cmd.arg(sby_file);
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
// 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 crate::{intern::Intern, prelude::*};
|
use crate::{
|
||||||
|
intern::{Intern, Interned},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum FormalKind {
|
pub enum FormalKind {
|
||||||
|
@ -32,7 +36,7 @@ pub fn formal_stmt_with_enable_and_loc(
|
||||||
kind,
|
kind,
|
||||||
clk,
|
clk,
|
||||||
pred,
|
pred,
|
||||||
en,
|
en: en & !formal_reset().cast_to_static::<Bool>(),
|
||||||
text: text.intern(),
|
text: text.intern(),
|
||||||
source_location,
|
source_location,
|
||||||
});
|
});
|
||||||
|
@ -160,11 +164,11 @@ endmodule
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn formal_reset() -> Expr<AsyncReset> {
|
pub fn formal_reset() -> Expr<SyncReset> {
|
||||||
#[hdl_module(extern)]
|
#[hdl_module(extern)]
|
||||||
fn formal_reset() {
|
fn formal_reset() {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let rst: AsyncReset = m.output();
|
let rst: SyncReset = m.output();
|
||||||
m.annotate_module(BlackBoxInlineAnnotation {
|
m.annotate_module(BlackBoxInlineAnnotation {
|
||||||
path: "fayalite_formal_reset.v".intern(),
|
path: "fayalite_formal_reset.v".intern(),
|
||||||
text: r"module __fayalite_formal_reset(output rst);
|
text: r"module __fayalite_formal_reset(output rst);
|
||||||
|
@ -180,7 +184,8 @@ endmodule
|
||||||
});
|
});
|
||||||
m.verilog_name("__fayalite_formal_reset");
|
m.verilog_name("__fayalite_formal_reset");
|
||||||
}
|
}
|
||||||
|
static MOD: OnceLock<Interned<Module<formal_reset>>> = OnceLock::new();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let formal_reset = instance(formal_reset());
|
let formal_reset = instance(*MOD.get_or_init(formal_reset));
|
||||||
formal_reset.rst
|
formal_reset.rst
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,14 @@ use crate::{
|
||||||
firrtl::ExportOptions,
|
firrtl::ExportOptions,
|
||||||
};
|
};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::sync::OnceLock;
|
use hashbrown::HashMap;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{
|
||||||
|
fmt::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
sync::{Mutex, OnceLock},
|
||||||
|
};
|
||||||
|
|
||||||
fn assert_formal_helper() -> FormalArgs {
|
fn assert_formal_helper() -> FormalArgs {
|
||||||
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
||||||
|
@ -15,8 +22,84 @@ fn assert_formal_helper() -> FormalArgs {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CargoMetadata {
|
||||||
|
target_directory: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cargo_target_dir() -> &'static Path {
|
||||||
|
static RETVAL: OnceLock<PathBuf> = OnceLock::new();
|
||||||
|
RETVAL.get_or_init(|| {
|
||||||
|
let output = Command::new(
|
||||||
|
std::env::var_os("CARGO")
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("cargo".as_ref()),
|
||||||
|
)
|
||||||
|
.arg("metadata")
|
||||||
|
.output()
|
||||||
|
.expect("can't run `cargo metadata`");
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"can't run `cargo metadata`:\n{}\nexited with status: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr),
|
||||||
|
output.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let CargoMetadata { target_directory } =
|
||||||
|
serde_json::from_slice(&output.stdout).expect("can't parse output of `cargo metadata`");
|
||||||
|
PathBuf::from(target_directory)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
|
||||||
|
static DIRS: Mutex<Option<HashMap<String, u64>>> = Mutex::new(None);
|
||||||
|
let test_name = test_name.to_string();
|
||||||
|
// don't use line/column numbers since that constantly changes as you edit tests
|
||||||
|
let file = std::panic::Location::caller().file();
|
||||||
|
// simple reproducible hash
|
||||||
|
let simple_hash = file.bytes().chain(test_name.bytes()).fold(
|
||||||
|
((file.len() as u32) << 16).wrapping_add(test_name.len() as u32),
|
||||||
|
|mut h, b| {
|
||||||
|
h = h.wrapping_mul(0xaa0d184b);
|
||||||
|
h ^= h.rotate_right(5);
|
||||||
|
h ^= h.rotate_right(13);
|
||||||
|
h.wrapping_add(b as u32)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let mut dir = String::with_capacity(64);
|
||||||
|
write!(dir, "{simple_hash:08x}-").unwrap();
|
||||||
|
for ch in Path::new(file)
|
||||||
|
.file_stem()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.chars()
|
||||||
|
.chain(['-'])
|
||||||
|
.chain(test_name.chars())
|
||||||
|
{
|
||||||
|
dir.push(match ch {
|
||||||
|
ch if ch.is_alphanumeric() => ch,
|
||||||
|
'_' | '-' | '+' | '.' | ',' | ' ' => ch,
|
||||||
|
_ => '_',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let index = *DIRS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_or_insert_with(HashMap::new)
|
||||||
|
.entry_ref(&dir)
|
||||||
|
.and_modify(|v| *v += 1)
|
||||||
|
.or_insert(0);
|
||||||
|
write!(dir, ".{index}").unwrap();
|
||||||
|
get_cargo_target_dir()
|
||||||
|
.join("fayalite_assert_formal")
|
||||||
|
.join(dir)
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_formal<M>(
|
pub fn assert_formal<M>(
|
||||||
|
test_name: impl std::fmt::Display,
|
||||||
module: M,
|
module: M,
|
||||||
mode: FormalMode,
|
mode: FormalMode,
|
||||||
depth: u64,
|
depth: u64,
|
||||||
|
@ -27,6 +110,7 @@ pub fn assert_formal<M>(
|
||||||
{
|
{
|
||||||
let mut args = assert_formal_helper();
|
let mut args = assert_formal_helper();
|
||||||
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
||||||
|
args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
|
||||||
args.verilog.firrtl.export_options = export_options;
|
args.verilog.firrtl.export_options = export_options;
|
||||||
args.verilog.debug = true;
|
args.verilog.debug = true;
|
||||||
args.mode = mode;
|
args.mode = mode;
|
||||||
|
|
|
@ -23,6 +23,19 @@ impl<T: Type> ReadyValid<T> {
|
||||||
fire
|
fire
|
||||||
}
|
}
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
pub fn fire_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
|
||||||
|
let expr = expr.to_expr();
|
||||||
|
let option_ty = Expr::ty(expr).data;
|
||||||
|
#[hdl]
|
||||||
|
let fire_data = wire(option_ty);
|
||||||
|
connect(fire_data, option_ty.HdlNone());
|
||||||
|
#[hdl]
|
||||||
|
if expr.ready {
|
||||||
|
connect(fire_data, expr.data);
|
||||||
|
}
|
||||||
|
fire_data
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
pub fn map<R: Type>(
|
pub fn map<R: Type>(
|
||||||
expr: Expr<Self>,
|
expr: Expr<Self>,
|
||||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||||
|
@ -163,7 +176,6 @@ pub fn queue<T: Type>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(todo)]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -171,27 +183,39 @@ mod tests {
|
||||||
cli::FormalMode, firrtl::ExportOptions,
|
cli::FormalMode, firrtl::ExportOptions,
|
||||||
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
|
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
|
||||||
};
|
};
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
#[test]
|
#[track_caller]
|
||||||
fn test_queue() {
|
fn test_queue(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
||||||
|
assert_formal(
|
||||||
|
format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"),
|
||||||
|
queue_test(capacity, inp_ready_is_comb, out_valid_is_comb),
|
||||||
|
FormalMode::BMC,
|
||||||
|
20,
|
||||||
|
None,
|
||||||
|
ExportOptions {
|
||||||
|
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
|
||||||
|
..ExportOptions::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
#[hdl_module]
|
#[hdl_module]
|
||||||
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
||||||
#[hdl]
|
|
||||||
let clk: Clock = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let rst: SyncReset = m.input();
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let inp_data: HdlOption<UInt<8>> = m.input();
|
let inp_data: HdlOption<UInt<8>> = m.input();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let out_ready: Bool = m.input();
|
let out_ready: Bool = m.input();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
let start_check: Bool = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let clk: Clock = m.input();
|
||||||
|
#[hdl]
|
||||||
let cd = wire();
|
let cd = wire();
|
||||||
connect(
|
connect(
|
||||||
cd,
|
cd,
|
||||||
#[hdl]
|
#[hdl]
|
||||||
ClockDomain {
|
ClockDomain {
|
||||||
clk,
|
clk,
|
||||||
rst: rst.to_reset(),
|
rst: formal_reset().to_reset(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
@ -216,52 +240,139 @@ mod tests {
|
||||||
} else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) {
|
} else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) {
|
||||||
connect_any(next_count, count - 1u8);
|
connect_any(next_count, count - 1u8);
|
||||||
}
|
}
|
||||||
hdl_assert(clk, count.cmp_eq(dut.count), "");
|
hdl_assert(cd.clk, count.cmp_eq(dut.count), "");
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let index = reg_builder().clock_domain(cd).reset(HdlNone::<UInt<32>>());
|
let started_check = reg_builder().clock_domain(cd).reset(false);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let data = reg_builder().clock_domain(cd).reset(HdlNone());
|
let steps_till_output = reg_builder().clock_domain(cd).reset(0u32);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
match index {
|
let expected_output = reg_builder().clock_domain(cd).reset(HdlNone());
|
||||||
HdlNone =>
|
|
||||||
{
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if ReadyValid::fire(dut.inp) {
|
if start_check & !started_check {
|
||||||
connect(index, HdlSome(0u32));
|
|
||||||
connect(data, dut.inp.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HdlSome(cur_index) =>
|
|
||||||
{
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if cur_index.cmp_ge(next_count) {
|
if let HdlSome(inp) = ReadyValid::fire_data(dut.inp) {
|
||||||
connect(index, HdlNone());
|
connect(started_check, true);
|
||||||
#[hdl]
|
connect_any(
|
||||||
if let HdlSome(data) = data {
|
steps_till_output,
|
||||||
#[hdl]
|
count + (!ReadyValid::fire(dut.out)).cast_to(UInt[1]),
|
||||||
if let HdlSome(out_data) = dut.out.data {
|
|
||||||
hdl_assert(clk, data.cmp_eq(out_data), "");
|
|
||||||
} else {
|
|
||||||
hdl_assert(clk, false.to_expr(), "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hdl_assert(clk, false.to_expr(), "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
connect(index, HdlSome((cur_index + 1u8).cast_to_static()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_formal(
|
|
||||||
queue_test(NonZeroUsize::new(2).unwrap(), false, false),
|
|
||||||
FormalMode::BMC,
|
|
||||||
20,
|
|
||||||
None,
|
|
||||||
ExportOptions {
|
|
||||||
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
|
|
||||||
..ExportOptions::default()
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
connect(expected_output, HdlSome(inp));
|
||||||
|
}
|
||||||
|
} else if started_check & steps_till_output.cmp_ne(0u32) & ReadyValid::fire(dut.out) {
|
||||||
|
connect_any(steps_till_output, steps_till_output - 1u32);
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
let stored_output = reg_builder().clock_domain(cd).reset(HdlNone());
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(out) = ReadyValid::fire_data(dut.out) {
|
||||||
|
#[hdl]
|
||||||
|
if (start_check & !started_check) | (started_check & steps_till_output.cmp_ne(0u32))
|
||||||
|
{
|
||||||
|
connect(stored_output, HdlSome(out));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
if started_check & steps_till_output.cmp_eq(0u32) {
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(expected_output) = expected_output {
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(stored_output) = stored_output {
|
||||||
|
hdl_assert(cd.clk, stored_output.cmp_eq(expected_output), "");
|
||||||
|
} else {
|
||||||
|
hdl_assert(cd.clk, false.to_expr(), "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hdl_assert(cd.clk, false.to_expr(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1_false_false() {
|
||||||
|
test_queue(NonZero::new(1).unwrap(), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1_false_true() {
|
||||||
|
test_queue(NonZero::new(1).unwrap(), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1_true_false() {
|
||||||
|
test_queue(NonZero::new(1).unwrap(), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1_true_true() {
|
||||||
|
test_queue(NonZero::new(1).unwrap(), true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2_false_false() {
|
||||||
|
test_queue(NonZero::new(2).unwrap(), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2_false_true() {
|
||||||
|
test_queue(NonZero::new(2).unwrap(), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2_true_false() {
|
||||||
|
test_queue(NonZero::new(2).unwrap(), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2_true_true() {
|
||||||
|
test_queue(NonZero::new(2).unwrap(), true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_3_false_false() {
|
||||||
|
test_queue(NonZero::new(3).unwrap(), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_3_false_true() {
|
||||||
|
test_queue(NonZero::new(3).unwrap(), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_3_true_false() {
|
||||||
|
test_queue(NonZero::new(3).unwrap(), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_3_true_true() {
|
||||||
|
test_queue(NonZero::new(3).unwrap(), true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_4_false_false() {
|
||||||
|
test_queue(NonZero::new(4).unwrap(), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_4_false_true() {
|
||||||
|
test_queue(NonZero::new(4).unwrap(), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_4_true_false() {
|
||||||
|
test_queue(NonZero::new(4).unwrap(), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(todo)]
|
||||||
|
#[test]
|
||||||
|
fn test_4_true_true() {
|
||||||
|
test_queue(NonZero::new(4).unwrap(), true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3390,7 +3390,15 @@ fn test_formal() {
|
||||||
assert_export_firrtl! {
|
assert_export_firrtl! {
|
||||||
m =>
|
m =>
|
||||||
"/test/check_formal.fir": r#"FIRRTL version 3.2.0
|
"/test/check_formal.fir": r#"FIRRTL version 3.2.0
|
||||||
circuit check_formal:
|
circuit check_formal: %[[
|
||||||
|
{
|
||||||
|
"class": "firrtl.transforms.BlackBoxInlineAnno",
|
||||||
|
"name": "fayalite_formal_reset.v",
|
||||||
|
"text": "module __fayalite_formal_reset(output rst);\n reg rst;\n (* gclk *)\n reg gclk;\n initial rst = 1;\n always @(posedge gclk)\n rst <= 0;\nendmodule\n",
|
||||||
|
"target": "~check_formal|formal_reset"
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
type Ty0 = {rst: UInt<1>}
|
||||||
module check_formal: @[module-XXXXXXXXXX.rs 1:1]
|
module check_formal: @[module-XXXXXXXXXX.rs 1:1]
|
||||||
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
|
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
|
||||||
input en1: UInt<1> @[module-XXXXXXXXXX.rs 3:1]
|
input en1: UInt<1> @[module-XXXXXXXXXX.rs 3:1]
|
||||||
|
@ -3399,12 +3407,21 @@ circuit check_formal:
|
||||||
input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||||
input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1]
|
input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1]
|
||||||
input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
||||||
assert(clk, pred1, en1, "en check 1") @[module-XXXXXXXXXX.rs 9:1]
|
inst formal_reset of formal_reset @[formal.rs 189:24]
|
||||||
assume(clk, pred2, en2, "en check 2") @[module-XXXXXXXXXX.rs 10:1]
|
assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1]
|
||||||
cover(clk, pred3, en3, "en check 3") @[module-XXXXXXXXXX.rs 11:1]
|
inst formal_reset_1 of formal_reset @[formal.rs 189:24]
|
||||||
assert(clk, pred1, UInt<1>(0h1), "check 1") @[module-XXXXXXXXXX.rs 12:1]
|
assume(clk, pred2, and(en2, not(formal_reset_1.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1]
|
||||||
assume(clk, pred2, UInt<1>(0h1), "check 2") @[module-XXXXXXXXXX.rs 13:1]
|
inst formal_reset_2 of formal_reset @[formal.rs 189:24]
|
||||||
cover(clk, pred3, UInt<1>(0h1), "check 3") @[module-XXXXXXXXXX.rs 14:1]
|
cover(clk, pred3, and(en3, not(formal_reset_2.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1]
|
||||||
|
inst formal_reset_3 of formal_reset @[formal.rs 189:24]
|
||||||
|
assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset_3.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1]
|
||||||
|
inst formal_reset_4 of formal_reset @[formal.rs 189:24]
|
||||||
|
assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset_4.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1]
|
||||||
|
inst formal_reset_5 of formal_reset @[formal.rs 189:24]
|
||||||
|
cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset_5.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1]
|
||||||
|
extmodule formal_reset: @[formal.rs 168:5]
|
||||||
|
output rst: UInt<1> @[formal.rs 171:32]
|
||||||
|
defname = __fayalite_formal_reset
|
||||||
"#,
|
"#,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue