rust_hdl_fpga_support/toolchains/
ise.rs1use 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 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}