diff --git a/fud2/fud-core/src/utils.rs b/fud2/fud-core/src/utils.rs index 0bfcbd67e1..88bea3ed1a 100644 --- a/fud2/fud-core/src/utils.rs +++ b/fud2/fud-core/src/utils.rs @@ -12,3 +12,10 @@ pub fn relative_path(path: &Utf8Path, base: &Utf8Path) -> Utf8PathBuf { .expect("could not get absolute path"), } } + +/// Get the basename of a file as designated in a &str. +pub fn basename(path: &str) -> &str { + Utf8Path::new(path) + .file_stem() + .expect("Failed to get a basename from the path.") +} diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index 860693205d..5bac0cac26 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use fud_core::{ exec::{SetupRef, StateRef}, run::{EmitResult, StreamEmitter}, + utils::basename, DriverBuilder, }; @@ -825,13 +826,7 @@ pub fn build_driver(bld: &mut DriverBuilder) { |e, input, output| { // Generate the YXI file. // no extension - let file_name = input - .rsplit_once('/') - .unwrap() - .1 - .rsplit_once('.') - .unwrap() - .0; + let file_name = basename(input); // Get yxi file from main compute program. let tmp_yxi = format!("{}.yxi", file_name); @@ -873,12 +868,23 @@ pub fn build_driver(bld: &mut DriverBuilder) { let data_path = e.external_path(data_name.as_ref()); e.var("sim_data", data_path.as_str())?; - // Cocotb is wants files relative to the location of the makefile. + // Cocotb wants files relative to the location of the makefile. // This is annoying to calculate on the fly, so we just copy necessary files to the build directory e.rule("copy", "cp $in $out")?; - e.rule("make-cocotb", "make DATA_PATH=$sim_data VERILOG_SOURCE=$in COCOTB_LOG_LEVEL=CRITICAL > $out")?; - // This cleans up the extra `make` cruft, leaving what is in between `{` and `}.` - e.rule("cleanup-cocotb", r"sed -n '/Output:/,/make\[1\]/{/Output:/d;/make\[1\]/d;p}' $in > $out")?; + + let waves = e.config_constrained_or("waves", vec!["true", "false"], "false")?; + let waves = FromStr::from_str(&waves).expect("The 'waves' flag should be either 'true' or 'false'."); + if waves{ + //adds lines based on what is needed for icarus fst output. + e.rule("iverilog-fst-sed", + r#"sed '/\/\/ COMPONENT END: wrapper/c\`ifdef COCOTB_SIM\n initial begin\n \$$dumpfile ("$fst_file_name");\n \$$dumpvars (0, wrapper);\n #1;\n end\n`endif\n\/\/ COMPONENT END: wrapper' $in > $out"#)?; + } + +e.var("cocotb-args", if waves {"WAVES=1"} else {""})?; + + e.rule("make-cocotb", "make DATA_PATH=$sim_data VERILOG_SOURCE=$in COCOTB_LOG_LEVEL=CRITICAL $cocotb-args > $out")?; + // This cleans up the extra `make` and `FST warning` cruft, leaving what is in between `{` and `}.` + e.rule("cleanup-cocotb", r#"sed -n '/Output:/,/make\[1\]/{/Output:/d;/make\[1\]/d;p}' $in | sed -n ':a;N;$$!ba;s/^[^{]*{\(.*\)}[^}]*$$/\1/p' | sed '1d;$$d' > $out"#)?; Ok(()) }); @@ -908,13 +914,27 @@ pub fn build_driver(bld: &mut DriverBuilder) { &["$cocotb-makefile-dir/run_axi_test.py"], &[], )?; + let waves = e.config_constrained_or( + "waves", + vec!["true", "false"], + "false", + )?; + let waves = FromStr::from_str(&waves) + .expect("The 'waves' flag should be either 'true' or 'false'."); + + let vcd_file_name = format!("{}.fst", basename(input)); + let mut make_in = input; + if waves { + make_in = "dumpvars.v"; + e.build_cmd(&[make_in], "iverilog-fst-sed", &[input], &[])?; + e.arg("fst_file_name", &vcd_file_name)?; + } e.build_cmd( &["tmp.dat"], "make-cocotb", - &[input], + &[make_in], &["Makefile", "axi_test.py", "run_axi_test.py"], )?; - e.build_cmd(&[output], "cleanup-cocotb", &["tmp.dat"], &[])?; Ok(())