rust_hdl_fpga_support/toolchains/
ecp5.rs1use rust_hdl_core::prelude::*;
3use std::collections::HashMap;
4
5use super::map_signal_type_to_lattice_string;
6
7#[derive(Default)]
8struct LPFGenerator {
9 path: NamedPath,
10 namespace: NamedPath,
11 lpf: Vec<String>,
12 names: HashMap<usize, String>,
13}
14
15impl Probe for LPFGenerator {
16 fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
17 let _top_level = self.path.to_string();
18 self.path.push(name);
19 self.namespace.reset();
20 }
21 fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
22 self.namespace.push(name);
23 }
24 fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
25 let namespace = self.namespace.flat("$");
26 let name = if namespace.is_empty() {
27 name.to_owned()
28 } else {
29 format!("{}${}", namespace, name)
30 };
31 for pin in &signal.constraints() {
32 self.names.insert(signal.id(), name.clone());
33 let prefix = if signal.bits() == 1 {
34 format!("{}", name)
35 } else {
36 format!("{}[{}]", name, pin.index)
37 };
38 match &pin.constraint {
39 Constraint::Location(l) => {
40 self.lpf
41 .push(format!("LOCATE COMP \"{}\" SITE \"{}\"", prefix, l));
42 }
43 Constraint::Kind(k) => {
44 let name = map_signal_type_to_lattice_string(k);
45 self.lpf
46 .push(format!("IOBUF PORT \"{}\" IO_TYPE={}", prefix, name))
47 }
48 Constraint::Timing(t) => {
49 let timing = match t {
50 Timing::Periodic(p) => {
51 format!(
52 "FREQUENCY PORT \"{prefix}\" {freq} MHz",
53 prefix = prefix,
54 freq =
55 ((1000.0 / p.period_nanoseconds) * 10000.0).round() / 10000.0
56 )
57 }
58 Timing::Custom(c) => c.to_string(),
59 _ => unimplemented!("Unknown timing constraint for ECP5 generation"),
60 };
61 if !timing.is_empty() {
62 self.lpf.push(timing);
63 }
64 }
65 Constraint::Custom(s) => self.lpf.push(s.clone()),
66 Constraint::Slew(k) => {
67 let tag = match k {
68 SlewType::Fast => "FAST",
69 SlewType::Normal => "SLOW",
70 };
71 self.lpf
72 .push(format!("IOBUF PORT \"{}\" SLEWRATE={}", prefix, tag));
73 }
74 }
75 }
76 }
77 fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
78 self.namespace.pop();
79 }
80 fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
81 self.path.pop();
82 }
83}
84
85pub fn generate_lpf<U: Block>(uut: &U) -> String {
86 let mut lpf = LPFGenerator::default();
87 uut.accept("top", &mut lpf);
88 let mut lpf_uniq = vec![];
89 lpf_uniq.push("BLOCK RESETPATHS".to_string());
90 lpf_uniq.push("BLOCK ASYNCPATHS".to_string());
91 for line in lpf.lpf {
92 if !lpf_uniq.contains(&line) {
93 lpf_uniq.push(line);
94 }
95 }
96 lpf_uniq.join(";\n") + ";\n"
97}