Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Churchroad binary #72

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/from_verilog/from_verilog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generates Churchroad source code from Verilog using Yosys.
#
# Usage: python3 from_verilog.py -verilog=<verilog_file> -output_path=<output_file>

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}")
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand All @@ -73,7 +73,7 @@ pub fn interpret(
egraph: &egraph_serialize::EGraph,
class_id: &ClassId,
time: usize,
env: &HashMap<&str, Vec<u64>>,
env: &HashMap<String, Vec<u64>>,
) -> Result<InterpreterResult, String> {
let result = match egraph.classes().iter().find(|(id, _)| *id == class_id) {
Some((id, _)) => interpret_helper(egraph, id, time, env),
Expand All @@ -87,7 +87,7 @@ fn interpret_helper(
egraph: &egraph_serialize::EGraph,
id: &ClassId,
time: usize,
env: &HashMap<&str, Vec<u64>>,
env: &HashMap<String, Vec<u64>>,
) -> Result<InterpreterResult, String> {
let node_ids = &egraph.classes().get(id).unwrap().nodes;
if node_ids.len() != 1 {
Expand Down
106 changes: 106 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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 <filename>
let args: Vec<String> = std::env::args().collect();
if args.len() != 2 {
eprintln!("Usage: cargo run <filename>");
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<String, Vec<u64>> = 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::<Vec<&str>>();
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!(),
}
}
}
}
46 changes: 23 additions & 23 deletions tests/interpreter_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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<PathBuf>,
test_output_dir: PathBuf,
num_test_cases: usize,
Expand Down Expand Up @@ -281,7 +281,7 @@ fn verilator_intepreter_fuzz_test(
let mut rng = StdRng::seed_from_u64(0xb0bacafe);
let mut interpreter_results: Vec<InterpreterResult> = Vec::new();
for _ in 0..num_test_cases {
let mut env: HashMap<&str, Vec<u64>> = HashMap::new();
let mut env: HashMap<String, Vec<u64>> = HashMap::new();
let input_values: Vec<Vec<u64>> = inputs
.iter()
.map(|(name, bw)| {
Expand All @@ -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();
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand Down
Loading