rust_hdl_fpga_support/toolchains/
vivado.rs

1use crate::toolchains::map_signal_type_to_xilinx_string;
2use rust_hdl_core::prelude::*;
3
4#[derive(Default)]
5struct XDCGenerator {
6    path: NamedPath,
7    namespace: NamedPath,
8    xdc: Vec<String>,
9}
10
11impl Probe for XDCGenerator {
12    fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
13        let _top_level = self.path.to_string();
14        self.path.push(name);
15        self.namespace.reset();
16    }
17    fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
18        self.namespace.push(name);
19    }
20    fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
21        let namespace = self.namespace.flat("$");
22        let name = if namespace.is_empty() {
23            name.to_owned()
24        } else {
25            format!("{}${}", namespace, name)
26        };
27        for pin in &signal.constraints() {
28            let prefix = if signal.bits() == 1 {
29                format!("{}", name)
30            } else {
31                format!("{}[{}]", name, pin.index)
32            };
33            match &pin.constraint {
34                Constraint::Location(l) => self.xdc.push(format!(
35                    "set_property PACKAGE_PIN {location} [get_ports {{ {prefix} }}]",
36                    location = l,
37                    prefix = prefix
38                )),
39                Constraint::Slew(k) => {
40                    if let SlewType::Fast = k {
41                        self.xdc.push(format!(
42                            "set_property SLEW FAST [get_ports {{ {prefix} }}]",
43                            prefix = prefix
44                        ))
45                    }
46                }
47                Constraint::Kind(k) => {
48                    let name = map_signal_type_to_xilinx_string(k);
49                    self.xdc.push(format!(
50                        "set_property IOSTANDARD {name} [get_ports {{ {prefix} }}]",
51                        prefix = prefix,
52                        name = name
53                    ))
54                }
55                Constraint::Timing(t) => {
56                    let timing = match t {
57                        Timing::Periodic(p) => {
58                            if p.duty_cycle != 50.0 {
59                                unimplemented!("Only 50 % duty cycle clocks currently implemented");
60                            }
61                            format!("create_clock -name {net} -period {period} [get_ports {{ {prefix} }}]",
62                                    net=p.net,
63                                    period=p.period_nanoseconds,
64                                    prefix=prefix)
65                        }
66                        Timing::Custom(c) => c.to_string(),
67                        Timing::VivadoOutputTiming(o) => {
68                            format!("set_output_delay -add_delay -clock [get_clocks {{ {clock} }}] {delay} [get_ports {{ {prefix} }}]",
69                                    clock = o.clock, delay = o.delay_nanoseconds, prefix=prefix)
70                        }
71                        Timing::VivadoInputTiming(i) => {
72                            format!(
73                                "set_input_delay -add_delay -max -clock [get_clocks {{ {clock} }}] {max} [get_ports {{ {prefix} }}]
74set_input_delay -add_delay -min -clock [get_clocks {{ {clock} }}] {min} [get_ports {{ {prefix} }}]
75set_multicycle_path -setup -from [get_ports {{ {prefix} }}] {cycles}",
76                                clock = i.clock,
77                                max = i.max_nanoseconds,
78                                min = i.min_nanoseconds,
79                                prefix = prefix,
80                                cycles = i.multicycle
81                            )
82                        }
83                        VivadoClockGroup(c) => {
84                            format!(
85                                "set_clock_groups -asynchronous {}",
86                                c.iter()
87                                    .map(|g| format!("-group {{ {} }}", g.join(" ")))
88                                    .collect::<Vec<_>>()
89                                    .join(" ")
90                            )
91                        }
92                        VivadoFalsePath(p) => {
93                            format!(
94                                "set_false_path -from [get_pins -hierarchical -regexp {from}] -to [get_pins -hierarchical -regexp {to}]",
95                                from = p.from_regexp,
96                                to = p.to_regexp
97                            )
98                        }
99                        _ => {
100                            unimplemented!("Constraint type {:?} is not implemented for Vivado", t)
101                        }
102                    };
103                    self.xdc.push(timing);
104                }
105                Constraint::Custom(s) => self.xdc.push(s.clone()),
106            }
107        }
108    }
109    fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
110        self.namespace.pop();
111    }
112    fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
113        self.path.pop();
114    }
115}
116
117pub fn generate_xdc<U: Block>(uut: &U) -> String {
118    let mut xdc = XDCGenerator::default();
119    uut.accept("top", &mut xdc);
120    let mut xdc_uniq = vec![];
121    for line in xdc.xdc {
122        if !xdc_uniq.contains(&line) {
123            xdc_uniq.push(line);
124        }
125    }
126    xdc_uniq.join("\n")
127        + "
128set_property CFGBVS VCCO [current_design]
129set_property CONFIG_VOLTAGE 3.3 [current_design]
130set_property BITSTREAM.GENERAL.COMPRESS True [current_design]
131    "
132}