rust_hdl_yosys_synth/
lib.rs1use 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}