rust_hdl_core/
yosys.rs

1use std::env::temp_dir;
2use std::fs::{create_dir_all, remove_dir_all, File};
3use std::io::{Error, Write};
4use std::process::Command;
5
6#[derive(Debug)]
7pub enum SynthError {
8    SynthesisFailed { stdout: String, stderr: String },
9    LatchingWriteToSignal(Vec<String>),
10    ImplicitlyDeclared(Vec<String>),
11    DuplicateModule(Vec<String>),
12    IOError(std::io::Error),
13    WireHasNoDriver(Vec<String>),
14    MissingModule(Vec<String>),
15}
16
17impl From<std::io::Error> for SynthError {
18    fn from(x: Error) -> Self {
19        SynthError::IOError(x)
20    }
21}
22
23pub fn yosys_validate(prefix: &str, translation: &str) -> Result<(), SynthError> {
24    let dir = temp_dir().as_path().join(prefix);
25    let _ = remove_dir_all(&dir);
26    let _ = create_dir_all(&dir);
27    let mut v_file = File::create(dir.clone().join("top.v")).unwrap();
28    write!(v_file, "{}", translation).unwrap();
29    let output = Command::new("yosys")
30        .current_dir(dir.clone())
31        .arg(format!(
32            "-p read -vlog95 top.v; hierarchy -check -top top; proc",
33        ))
34        .output()
35        .unwrap();
36    let stdout = String::from_utf8(output.stdout).unwrap();
37    let stderr = String::from_utf8(output.stderr).unwrap();
38    {
39        let mut debug = File::create(dir.join("yosys.stdout"))?;
40        write!(debug, "{}", stdout).unwrap();
41        write!(debug, "{}", stderr).unwrap();
42        let mut dump = File::create(dir.join("yosys.v"))?;
43        write!(dump, "{}", translation).unwrap();
44    }
45    fn capture(stdout: &str, reg_exp: &str) -> Vec<String> {
46        let regex = regex::Regex::new(reg_exp).unwrap();
47        let mut signal_name = vec![];
48        if regex.is_match(&stdout) {
49            for capture in regex.captures(&stdout).unwrap().iter() {
50                signal_name.push(capture.unwrap().as_str().to_string());
51            }
52        }
53        signal_name
54    }
55    if stdout.contains("Re-definition of") {
56        return Err(SynthError::DuplicateModule(capture(
57            &stdout,
58            r#"Re-definition of module (\S*)"#,
59        )));
60    }
61    if stdout.contains("implicitly declared.") {
62        return Err(SynthError::ImplicitlyDeclared(capture(
63            &stdout,
64            r#"Identifier (\S*) is implicitly declared"#,
65        )));
66    }
67    if stdout.contains("Latch inferred for") {
68        return Err(SynthError::LatchingWriteToSignal(capture(
69            &stdout,
70            r#"Latch inferred for signal (\S*)"#,
71        )));
72    }
73    if stdout.contains("is used but has no driver") {
74        return Err(SynthError::WireHasNoDriver(capture(
75            &stdout,
76            r#"Wire (\S*) .*? is used but has no driver."#,
77        )));
78    }
79    if stderr.contains("is not part of the design") {
80        return Err(SynthError::MissingModule(capture(
81            &stderr,
82            r#"Module (\S*) .*? is not part of the design"#,
83        )));
84    }
85    if !stdout.contains("End of script.") {
86        return Err(SynthError::SynthesisFailed { stdout, stderr });
87    }
88    Ok(())
89}