rust_hdl_yosys_synth/
lib.rs

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