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