diff --git a/src/from_verilog/from_verilog.py b/src/from_verilog/from_verilog.py new file mode 100644 index 0000000..2ff09cc --- /dev/null +++ b/src/from_verilog/from_verilog.py @@ -0,0 +1,53 @@ +# Generates Churchroad source code from Verilog using Yosys. +# +# Usage: python3 from_verilog.py -verilog= -output_path= + +import argparse +import subprocess +import os + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # Add arguments + parser.add_argument("-verilog", help="Path of Verilog file to convert") + parser.add_argument("-output_path", help="Path to output file") + parser.add_argument("-top_level_module", help="Path to output file") + + args = parser.parse_args() + + if args.verilog is None: + print("Error: Verilog file not specified") + exit(1) + + if args.output_path is None: + print("Error: Output file not specified") + exit(1) + + if args.top_level_module is None: + print("Error: Top level module not specified") + exit(1) + + # get CHURCHROAD_DIR from environment variable + CHURCHROAD_DIR = os.getenv("CHURCHROAD_DIR") + + if CHURCHROAD_DIR is None: + print("Error: CHURCHROAD_DIR environment variable not set") + exit(1) + + # Generate Churchroad source code from Verilog + churchroad_code = subprocess.check_output( + [ + "yosys", + "-m", + f"{CHURCHROAD_DIR}/yosys-plugin/churchroad.so", + "-q", + "-p", + f"read_verilog -sv {args.verilog}; prep -top {args.top_level_module}; pmuxtree; write_lakeroad", + ] + ) + + with open(args.output_path, "w") as f: + f.write(str(churchroad_code.decode("utf-8"))) + + print(f"Generated Churchroad source code at {args.output_path}") diff --git a/src/lib.rs b/src/lib.rs index a2d3157..6f76d10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ pub enum InterpreterResult { /// .unwrap(); /// /// let result = interpret(&serialized, &output_node.eclass, 0, -/// &[("a", vec![1]), ("b", vec![1])].into() +/// &[("a".to_string(), vec![1]), ("b".to_string(), vec![1])].into() /// ); /// /// assert_eq!(result, Ok(InterpreterResult::Bitvector(1, 1))); @@ -73,7 +73,7 @@ pub fn interpret( egraph: &egraph_serialize::EGraph, class_id: &ClassId, time: usize, - env: &HashMap<&str, Vec>, + env: &HashMap>, ) -> Result { let result = match egraph.classes().iter().find(|(id, _)| *id == class_id) { Some((id, _)) => interpret_helper(egraph, id, time, env), @@ -87,7 +87,7 @@ fn interpret_helper( egraph: &egraph_serialize::EGraph, id: &ClassId, time: usize, - env: &HashMap<&str, Vec>, + env: &HashMap>, ) -> Result { let node_ids = &egraph.classes().get(id).unwrap().nodes; if node_ids.len() != 1 { diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ee6e6e4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,106 @@ +// Runs the Churchroad interpreter on a given .egg file. + +use std::collections::HashMap; + +use egglog::{EGraph, SerializeConfig}; + +use egraph_serialize::NodeId; + +use churchroad::{import_churchroad, interpret}; + +fn main() { + // Usage: cargo run + let args: Vec = std::env::args().collect(); + if args.len() != 2 { + eprintln!("Usage: cargo run "); + std::process::exit(1); + } + + let churchroad_src_path = &args[1]; + + let mut egraph: EGraph = EGraph::default(); + import_churchroad(&mut egraph); + egraph + .parse_and_run_program(&std::fs::read_to_string(churchroad_src_path).unwrap()) + .unwrap(); + let serialized = egraph.serialize(SerializeConfig::default()); + let (_, is_output_node) = serialized + .nodes + .iter() + .find(|(_, n)| n.op == "IsPort" && n.children[2] == NodeId::from("Output-0")) + .unwrap(); + let output_id = is_output_node.children.last().unwrap(); + let (_, output_node) = serialized + .nodes + .iter() + .find(|(node_id, _)| **node_id == *output_id) + .unwrap(); + + // get user input: # of test cases, # of time steps + let mut test_cases = String::new(); + println!("num test cases:"); + std::io::stdin().read_line(&mut test_cases).unwrap(); + let test_cases: usize = test_cases.trim().parse().unwrap(); + + let mut time_steps = String::new(); + println!("num time steps:"); + std::io::stdin().read_line(&mut time_steps).unwrap(); + let time_steps: usize = time_steps.trim().parse().unwrap(); + + let mut num_inputs = String::new(); + println!("num inputs:"); + std::io::stdin().read_line(&mut num_inputs).unwrap(); + let num_inputs: usize = num_inputs.trim().parse().unwrap(); + + println!( + "Ok, {} test cases, {} time steps, {} inputs.", + test_cases, time_steps, num_inputs + ); + + for _ in 0..test_cases { + let mut input_map: HashMap> = HashMap::new(); + for time in 0..time_steps { + for _ in 0..num_inputs { + // input will be of form "variable_name: value" + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + + let inputs = input.trim().split(":").collect::>(); + assert!( + inputs.len() == 2, + "input must be of form 'variable_name:value'" + ); + + let var_name = inputs[0].to_string(); + let value: u64 = inputs[1].trim().parse().unwrap(); + + let mut old_inputs = input_map.get(&var_name).unwrap_or(&vec![]).clone(); + + if old_inputs.len() > time { + panic!( + "You've added an input for {} twice in clock cycle {}.", + var_name, time + ); + } + + old_inputs.push(value); + + input_map.insert(var_name, old_inputs); + } + } + + for (_, values) in input_map.iter() { + assert!(values.len() == time_steps); + } + + // now, run the interpreter + for time in 0..time_steps { + match interpret(&serialized, &output_node.eclass, time, &input_map) { + Ok(churchroad::InterpreterResult::Bitvector(val, bw)) => { + println!("({}, {})", val, bw); + } + _ => panic!(), + } + } + } +} diff --git a/tests/interpreter_tests.rs b/tests/interpreter_tests.rs index 5e43996..e939864 100644 --- a/tests/interpreter_tests.rs +++ b/tests/interpreter_tests.rs @@ -89,19 +89,19 @@ fn test_lut6_0() { let makefile_template_path = churchroad_dir.join("tests/interpreter_tests/Makefile.template"); let inputs = vec![ - ("INIT", 64), - ("I0", 1), - ("I1", 1), - ("I2", 1), - ("I3", 1), - ("I4", 1), - ("I5", 1), + ("INIT".to_string(), 64), + ("I0".to_string(), 1), + ("I1".to_string(), 1), + ("I2".to_string(), 1), + ("I3".to_string(), 1), + ("I4".to_string(), 1), + ("I5".to_string(), 1), ]; - let outputs: Vec<(&str, i32)> = vec![("O", 1)]; + let outputs: Vec<(String, i32)> = vec![("O".to_string(), 1)]; let include_dirs = vec![churchroad_dir.join("tests/interpreter_tests/verilog/")]; - verilator_intepreter_fuzz_test( + verilator_interpreter_fuzz_test( testbench_template_path, makefile_template_path, "LUT6", @@ -128,12 +128,12 @@ fn test_lut6_0() { // num_test_cases: number of test cases to run // num_clock_cycles: number of clock cycles to run each test case for // filename: name of the file to run Verilator on. For example, if the file is `adder.v`, this should be `"adder.v"` -fn verilator_intepreter_fuzz_test( +fn verilator_interpreter_fuzz_test( testbench_template_path: PathBuf, makefile_template_path: PathBuf, top_module_name: &str, - inputs: Vec<(&str, i32)>, - outputs: Vec<(&str, i32)>, + inputs: Vec<(String, i32)>, + outputs: Vec<(String, i32)>, include_dirs: Vec, test_output_dir: PathBuf, num_test_cases: usize, @@ -281,7 +281,7 @@ fn verilator_intepreter_fuzz_test( let mut rng = StdRng::seed_from_u64(0xb0bacafe); let mut interpreter_results: Vec = Vec::new(); for _ in 0..num_test_cases { - let mut env: HashMap<&str, Vec> = HashMap::new(); + let mut env: HashMap> = HashMap::new(); let input_values: Vec> = inputs .iter() .map(|(name, bw)| { @@ -295,7 +295,7 @@ fn verilator_intepreter_fuzz_test( val }) .collect(); - env.insert(name, vals.clone()); + env.insert(String::from(name), vals.clone()); vals }) .collect(); @@ -362,9 +362,9 @@ interpreter_test!( "ALU", 0, &[ - ("a", vec![0b01010101]), - ("b", vec![0b11111111]), - ("op", vec![1]) + ("a".to_string(), vec![0b01010101]), + ("b".to_string(), vec![0b11111111]), + ("op".to_string(), vec![1]) ] .into(), "out" @@ -377,9 +377,9 @@ interpreter_test!( "ALU", 0, &[ - ("a", vec![0b01010101, 0b10101010]), - ("b", vec![0b11111111, 0b00000000]), - ("op", vec![1, 0]) + ("a".to_string(), vec![0b01010101, 0b10101010]), + ("b".to_string(), vec![0b11111111, 0b00000000]), + ("op".to_string(), vec![1, 0]) ] .into(), "out" @@ -392,9 +392,9 @@ interpreter_test!( "ALU", 1, &[ - ("a", vec![0b01010101, 0b10101010]), - ("b", vec![0b11111111, 0b00000000]), - ("op", vec![1, 0]) + ("a".to_string(), vec![0b01010101, 0b10101010]), + ("b".to_string(), vec![0b11111111, 0b00000000]), + ("op".to_string(), vec![1, 0]) ] .into(), "out"