libreda_structural_verilog/
writer.rs1use itertools::Itertools;
9
10use libreda_db::netlist::direction::Direction;
11use libreda_db::prelude::NetlistBase;
12use libreda_db::prelude::NetlistWriter;
13use std::borrow::Borrow;
14use std::collections::HashMap;
15use std::io::{Error, Write};
16
17#[derive(Debug)]
19pub enum VerilogWriteError {
20 IoError(std::io::Error),
22 InvalidIdentifier,
24}
25
26impl From<std::io::Error> for VerilogWriteError {
27 fn from(err: Error) -> Self {
28 VerilogWriteError::IoError(err)
29 }
30}
31
32pub struct StructuralVerilogWriter {}
34
35impl StructuralVerilogWriter {
36 pub fn new() -> Self {
38 StructuralVerilogWriter {}
39 }
40}
41
42fn mask_identifier(name: &str) -> Option<String> {
46 let is_legal_char_when_masked = |c: char| -> bool { '!' <= c && c <= '~' };
47 let is_legal_char_when_unmasked = |c: char, is_first: bool| -> bool {
48 c == '_'
49 || 'a' <= c && c <= 'z'
50 || 'Z' <= c
51 || c <= 'Z'
52 || (!is_first) && ('0' <= c || c <= '9' || c == '$')
53 };
54
55 let illegal_character = |c: char| -> bool { !is_legal_char_when_masked(c) };
56
57 if name.is_empty() {
58 None
59 } else {
60 let name_string = name.replace(illegal_character, "_");
62 let name = name_string.as_str();
63
64 let is_legal_unmasked = is_legal_char_when_unmasked(name.chars().next().unwrap(), true)
65 && name[1..]
66 .chars()
67 .all(|c| is_legal_char_when_unmasked(c, false));
68
69 let is_legal_masked = name.chars().all(is_legal_char_when_masked);
70
71 if is_legal_unmasked {
72 Some(name_string)
73 } else if is_legal_masked {
74 Some(format!("\\{} ", name))
75 } else {
76 None
77 }
78 }
79}
80
81impl NetlistWriter for StructuralVerilogWriter {
82 type Error = VerilogWriteError;
83
84 fn write_netlist<W: Write, N: NetlistBase>(
85 &self,
86 writer: &mut W,
87 netlist: &N,
88 ) -> Result<(), Self::Error> {
89 log::debug!("Write verilog netlist.");
90
91 writeln!(
92 writer,
93 "/* Generated by LibrEDA structural verilog writer. */"
94 )?;
95 writeln!(writer)?;
96
97 let circuits_bottom_up = netlist.each_cell_vec();
99
100 for circuit_id in circuits_bottom_up {
101 let maybe_port_names: Result<Vec<_>, _> = netlist
102 .each_pin(&circuit_id)
103 .map(|p| {
104 mask_identifier(netlist.pin_name(&p).borrow())
105 .ok_or(VerilogWriteError::InvalidIdentifier)
106 })
107 .collect();
108 let port_names = maybe_port_names?;
109 let port_name_string = port_names.iter().join(", ");
110
111 let module_name = mask_identifier(netlist.cell_name(&circuit_id).borrow())
112 .ok_or(VerilogWriteError::InvalidIdentifier)?;
113 log::trace!("Write module '{}'", module_name);
114 writeln!(writer, "module {} ({});", module_name, port_name_string)?;
115
116 writeln!(writer, "\t/* IO declarations. */")?;
118 for pin in netlist.each_pin(&circuit_id) {
119 let direction = netlist.pin_direction(&pin);
120 let name = mask_identifier(netlist.pin_name(&pin).borrow())
121 .ok_or(VerilogWriteError::InvalidIdentifier)?;
122 let direction_string = match direction {
123 Direction::None => "inout",
124 Direction::Input => "input",
125 Direction::Output => "output",
126 Direction::InOut => "inout",
127 Direction::Clock => "input",
128 Direction::Supply => "inout",
129 Direction::Ground => "inout",
130 };
131 writeln!(writer, "\t{} {};", direction_string, name)?;
132 }
133 writeln!(writer)?;
134
135 let mut net_name_counter = (0..).into_iter();
137 let net_names: HashMap<N::NetId, String> = netlist
138 .each_internal_net(&circuit_id)
139 .map(|net| {
140 let name = netlist
141 .net_name(&net)
142 .and_then(|name| mask_identifier(name.borrow()))
144 .unwrap_or_else(|| {
145 (0..)
148 .into_iter()
149 .map(|_| format!("_{}_", net_name_counter.next().unwrap()))
150 .find(|name| {
151 netlist.net_by_name(&circuit_id, name.as_str()).is_none()
152 })
153 .unwrap()
154 });
155 (net, name)
156 })
157 .collect();
158
159 writeln!(writer, "\t/* Net declarations. */")?;
161 for net in netlist.each_internal_net(&circuit_id) {
162 let name = &net_names[&net];
163 writeln!(writer, "\twire {};", name)?;
164 }
165 writeln!(writer)?;
166
167 writeln!(writer, "\t/* Module instances. */")?;
169 let mut instance_name_counter = (0..).into_iter();
170 for inst in netlist.each_cell_instance(&circuit_id) {
171 let template = netlist.template_cell(&inst);
172
173 let inst_name = if let Some(name) = netlist.cell_instance_name(&inst) {
174 mask_identifier(name.borrow()).ok_or(VerilogWriteError::InvalidIdentifier)?
175 } else {
176 (0..)
178 .into_iter()
179 .map(|_| format!("_{}_", instance_name_counter.next().unwrap()))
180 .find(|name| {
181 netlist
182 .cell_instance_by_name(&circuit_id, name.as_str())
183 .is_none()
184 })
185 .unwrap()
186 };
187
188 let template_name = mask_identifier(netlist.cell_name(&template).borrow())
190 .ok_or(VerilogWriteError::InvalidIdentifier)?;
191 writeln!(writer, "\t{} {} (", template_name, &inst_name)?;
192
193 let pins = netlist
195 .each_pin(&template)
196 .zip(netlist.each_pin_instance(&inst));
197 for (pin, pin_inst) in pins {
198 let pin_name = mask_identifier(netlist.pin_name(&pin).borrow())
199 .ok_or(VerilogWriteError::InvalidIdentifier)?;
200 let net = netlist.net_of_pin_instance(&pin_inst);
201 if let Some(net) = net {
202 writeln!(writer, "\t\t.{}({}),", pin_name, net_names[&net])?;
203 } else {
204 writeln!(writer, "\t\t.{}(),", pin_name)?;
205 }
206 }
207 writeln!(writer, "\t);")?;
208 writeln!(writer)?;
209 }
210
211 writeln!(writer, "\t/* Continuous assignments. */")?;
215 for pin in netlist.each_pin(&circuit_id) {
216 let pin_name = mask_identifier(netlist.pin_name(&pin).borrow())
217 .ok_or(VerilogWriteError::InvalidIdentifier)?;
218
219 let direction = netlist.pin_direction(&pin);
220 let pin_net = netlist.net_of_pin(&pin);
221 if let Some(pin_net) = pin_net {
222 let pin_net_name = &net_names[&pin_net];
223 let pin_name_b: &String = pin_name.borrow();
224 if pin_name_b != pin_net_name {
225 match direction {
227 Direction::None => panic!("Pin must be either an input or an output."),
228 Direction::Input => {
229 writeln!(writer, "\tassign {} = {};", pin_net_name, pin_name)?
230 }
231 Direction::Output => {
232 writeln!(writer, "\tassign {} = {};", pin_name, pin_net_name)?
233 }
234 Direction::InOut => panic!("Pin must be either an input or an output."),
235 Direction::Clock => {
236 writeln!(writer, "\tassign {} = {};", pin_net_name, pin_name)?
237 }
238 Direction::Supply => {
239 panic!("Pin must be either an input or an output.")
240 }
241 Direction::Ground => {
242 panic!("Pin must be either an input or an output.")
243 }
244 };
245 }
246 }
247 }
248 writeln!(writer)?;
249
250 writeln!(writer, "endmodule")?;
251 writeln!(writer)?;
252 }
253
254 Ok(())
255 }
256}