libreda_structural_verilog/
writer.rs

1// Copyright (c) 2020-2021 Thomas Kramer.
2// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! This module implements a netlist writer for structural Verilog.
7
8use 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/// Error type returned by the Verilog writer.
18#[derive(Debug)]
19pub enum VerilogWriteError {
20    /// An error happened while writing to the output stream.
21    IoError(std::io::Error),
22    /// Identifier is empty or contains illegal characters.
23    InvalidIdentifier,
24}
25
26impl From<std::io::Error> for VerilogWriteError {
27    fn from(err: Error) -> Self {
28        VerilogWriteError::IoError(err)
29    }
30}
31
32/// Verilog netlist writer.
33pub struct StructuralVerilogWriter {}
34
35impl StructuralVerilogWriter {
36    /// Create default writer.
37    pub fn new() -> Self {
38        StructuralVerilogWriter {}
39    }
40}
41
42/// Convert any string into a valid Verilog identifier.
43/// The string will be masked when necessary. Characters that are also invalid
44/// for masked identifiers are replaced.
45fn 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        // Replace all illegal characters.
61        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        // TODO: Use correct ordering: bottom-up.
98        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            // Declare all inputs and outputs.
117            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            // Get or create a name for each net.
136            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                        // Replace illegal characters or escape the name.
143                        .and_then(|name| mask_identifier(name.borrow()))
144                        .unwrap_or_else(|| {
145                            // Create net name.
146                            // Construct a name that does not yet exist in the netlist.
147                            (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            // Declare all nets.
160            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            // Module instances.
168            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                    // Construct a name that does not yet exist in the netlist.
177                    (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                // Create instance.
189                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                // Write port connections of to the instance.
194                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            // Continous assigns.
212            // Create assigns statements for all pins where the pin name does not match
213            // the name of the connected net.
214            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                        // Create an `assign l = r;` statement.
226                        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}