rust_hdl_fpga_support/toolchains/
ise.rs

1use std::collections::HashMap;
2
3use crate::toolchains::map_signal_type_to_xilinx_string;
4use rust_hdl_core::prelude::*;
5
6#[derive(Default)]
7struct UCFGenerator {
8    path: NamedPath,
9    namespace: NamedPath,
10    ucf: Vec<String>,
11    names: HashMap<usize, String>,
12}
13
14pub fn collect_xrefs(txt: &[String]) -> Vec<(String, String)> {
15    let xref = regex::Regex::new(r#"!(\d*)!"#).unwrap();
16    let mut ret = vec![];
17    for line in txt {
18        for capture in xref.captures_iter(line) {
19            if let Some(t) = capture.get(0) {
20                if let Some(p) = capture.get(1) {
21                    let capture = (t.as_str().to_string(), p.as_str().to_string());
22                    if !ret.contains(&capture) {
23                        ret.push(capture);
24                    }
25                }
26            }
27        }
28    }
29    ret
30}
31
32pub fn substitute_refs(
33    txt: &[String],
34    xrefs: &[(String, String)],
35    name_map: &HashMap<usize, String>,
36) -> Vec<String> {
37    let mut ret = vec![];
38    for line in txt {
39        let mut line = line.clone();
40        for (from, to) in xrefs {
41            let index = to.parse::<usize>().unwrap();
42            if let Some(name) = name_map.get(&index) {
43                line = line.replace(from, name);
44            }
45        }
46        ret.push(line);
47    }
48    ret
49}
50
51impl Probe for UCFGenerator {
52    fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
53        let _top_level = self.path.to_string();
54        self.path.push(name);
55        self.namespace.reset();
56    }
57    fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
58        self.namespace.push(name);
59    }
60    fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
61        let namespace = self.namespace.flat("$");
62        let name = if namespace.is_empty() {
63            name.to_owned()
64        } else {
65            format!("{}${}", namespace, name)
66        };
67        for pin in &signal.constraints() {
68            self.names.insert(signal.id(), name.clone());
69            let prefix = if signal.bits() == 1 {
70                format!("NET {}", name)
71            } else {
72                format!("NET {}<{}>", name, pin.index)
73            };
74            match &pin.constraint {
75                Constraint::Location(l) => {
76                    self.ucf.push(format!("{} LOC={}", prefix, l));
77                }
78                Constraint::Kind(k) => {
79                    let name = map_signal_type_to_xilinx_string(k);
80                    self.ucf.push(format!("{} IOSTANDARD={}", prefix, name))
81                }
82                Constraint::Timing(t) => {
83                    let timing = match t {
84                        Timing::Periodic(p) => {
85                            format!("{prefix} TNM_NET={net}; TIMESPEC {ts} = PERIOD {net} {period} ns HIGH {duty}%",
86                                    prefix=prefix,
87                                    net=p.net,
88                                    ts=format!("TS_{}", p.net),
89                                    period = p.period_nanoseconds,
90                                    duty = p.duty_cycle)
91                        }
92                        Timing::InputTiming(i) => {
93                            format!("{prefix} OFFSET = IN {offset} ns VALID {valid} {relative} !{id}!{bit} {edge}",
94                                    prefix = prefix,
95                                    offset = i.offset_nanoseconds,
96                                    valid = i.valid_duration_nanoseconds,
97                                    relative = i.relative.to_string(),
98                                    id = i.to_signal_id,
99                                    bit = if let Some(n) = i.to_signal_bit {
100                                        format!("<{}>", n)
101                                    } else {
102                                        "".into()
103                                    },
104                                    edge = i.edge_sense.to_string()
105                            )
106                        }
107                        Timing::OutputTiming(o) => {
108                            format!(
109                                "{prefix} OFFSET = OUT {offset} ns {relative} !{id}!{bit} {edge}",
110                                prefix = prefix,
111                                offset = o.offset_nanoseconds,
112                                relative = o.relative.to_string(),
113                                id = o.to_signal_id,
114                                bit = if let Some(n) = o.to_signal_bit {
115                                    format!("<{}>", n)
116                                } else {
117                                    "".into()
118                                },
119                                edge = o.edge_sense.to_string()
120                            )
121                        }
122                        Timing::Custom(c) => c.to_string(),
123                        Timing::VivadoFalsePath(_) => "".to_string(),
124                        _ => unimplemented!("Unknown timing constraint for ISE/UCF generation"),
125                    };
126                    if !timing.is_empty() {
127                        self.ucf.push(timing);
128                    }
129                }
130                Constraint::Custom(s) => self.ucf.push(s.clone()),
131                _ => {
132                    unimplemented!("Unsupported constraint type for UCF files")
133                }
134            }
135        }
136    }
137    fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
138        self.namespace.pop();
139    }
140    fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
141        self.path.pop();
142    }
143}
144
145pub fn generate_ucf<U: Block>(uut: &U) -> String {
146    let mut ucf = UCFGenerator::default();
147    uut.accept("top", &mut ucf);
148    // Substitute the signal references
149    let xrefs = collect_xrefs(&ucf.ucf);
150    let ucf_lines = substitute_refs(&ucf.ucf, &xrefs, &ucf.names);
151    let mut ucf_uniq = vec![];
152    for line in ucf_lines {
153        if !ucf_uniq.contains(&line) {
154            ucf_uniq.push(line);
155        }
156    }
157    ucf_uniq.join(";\n")
158}