1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! This module implements a netlist writer for structural Verilog.

use itertools::Itertools;

use libreda_db::netlist::direction::Direction;
use libreda_db::prelude::NetlistBase;
use libreda_db::prelude::NetlistWriter;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::io::{Error, Write};

/// Error type returned by the Verilog writer.
#[derive(Debug)]
pub enum VerilogWriteError {
    /// An error happened while writing to the output stream.
    IoError(std::io::Error),
    /// Identifier is empty or contains illegal characters.
    InvalidIdentifier,
}

impl From<std::io::Error> for VerilogWriteError {
    fn from(err: Error) -> Self {
        VerilogWriteError::IoError(err)
    }
}

/// Verilog netlist writer.
pub struct StructuralVerilogWriter {}

impl StructuralVerilogWriter {
    /// Create default writer.
    pub fn new() -> Self {
        StructuralVerilogWriter {}
    }
}

/// Convert any string into a valid Verilog identifier.
/// The string will be masked when necessary. Characters that are also invalid
/// for masked identifiers are replaced.
fn mask_identifier(name: &str) -> Option<String> {
    let is_legal_char_when_masked = |c: char| -> bool { '!' <= c && c <= '~' };
    let is_legal_char_when_unmasked = |c: char, is_first: bool| -> bool {
        c == '_'
            || 'a' <= c && c <= 'z'
            || 'Z' <= c
            || c <= 'Z'
            || (!is_first) && ('0' <= c || c <= '9' || c == '$')
    };

    let illegal_character = |c: char| -> bool { !is_legal_char_when_masked(c) };

    if name.is_empty() {
        None
    } else {
        // Replace all illegal characters.
        let name_string = name.replace(illegal_character, "_");
        let name = name_string.as_str();

        let is_legal_unmasked = is_legal_char_when_unmasked(name.chars().next().unwrap(), true)
            && name[1..]
                .chars()
                .all(|c| is_legal_char_when_unmasked(c, false));

        let is_legal_masked = name.chars().all(is_legal_char_when_masked);

        if is_legal_unmasked {
            Some(name_string)
        } else if is_legal_masked {
            Some(format!("\\{} ", name))
        } else {
            None
        }
    }
}

impl NetlistWriter for StructuralVerilogWriter {
    type Error = VerilogWriteError;

    fn write_netlist<W: Write, N: NetlistBase>(
        &self,
        writer: &mut W,
        netlist: &N,
    ) -> Result<(), Self::Error> {
        log::debug!("Write verilog netlist.");

        writeln!(
            writer,
            "/* Generated by LibrEDA structural verilog writer. */"
        )?;
        writeln!(writer)?;

        // TODO: Use correct ordering: bottom-up.
        let circuits_bottom_up = netlist.each_cell_vec();

        for circuit_id in circuits_bottom_up {
            let maybe_port_names: Result<Vec<_>, _> = netlist
                .each_pin(&circuit_id)
                .map(|p| {
                    mask_identifier(netlist.pin_name(&p).borrow())
                        .ok_or(VerilogWriteError::InvalidIdentifier)
                })
                .collect();
            let port_names = maybe_port_names?;
            let port_name_string = port_names.iter().join(", ");

            let module_name = mask_identifier(netlist.cell_name(&circuit_id).borrow())
                .ok_or(VerilogWriteError::InvalidIdentifier)?;
            log::trace!("Write module '{}'", module_name);
            writeln!(writer, "module {} ({});", module_name, port_name_string)?;

            // Declare all inputs and outputs.
            writeln!(writer, "\t/* IO declarations. */")?;
            for pin in netlist.each_pin(&circuit_id) {
                let direction = netlist.pin_direction(&pin);
                let name = mask_identifier(netlist.pin_name(&pin).borrow())
                    .ok_or(VerilogWriteError::InvalidIdentifier)?;
                let direction_string = match direction {
                    Direction::None => "inout",
                    Direction::Input => "input",
                    Direction::Output => "output",
                    Direction::InOut => "inout",
                    Direction::Clock => "input",
                    Direction::Supply => "inout",
                    Direction::Ground => "inout",
                };
                writeln!(writer, "\t{} {};", direction_string, name)?;
            }
            writeln!(writer)?;

            // Get or create a name for each net.
            let mut net_name_counter = (0..).into_iter();
            let net_names: HashMap<N::NetId, String> = netlist
                .each_internal_net(&circuit_id)
                .map(|net| {
                    let name = netlist
                        .net_name(&net)
                        // Replace illegal characters or escape the name.
                        .and_then(|name| mask_identifier(name.borrow()))
                        .unwrap_or_else(|| {
                            // Create net name.
                            // Construct a name that does not yet exist in the netlist.
                            (0..)
                                .into_iter()
                                .map(|_| format!("_{}_", net_name_counter.next().unwrap()))
                                .find(|name| {
                                    netlist.net_by_name(&circuit_id, name.as_str()).is_none()
                                })
                                .unwrap()
                        });
                    (net, name)
                })
                .collect();

            // Declare all nets.
            writeln!(writer, "\t/* Net declarations. */")?;
            for net in netlist.each_internal_net(&circuit_id) {
                let name = &net_names[&net];
                writeln!(writer, "\twire {};", name)?;
            }
            writeln!(writer)?;

            // Module instances.
            writeln!(writer, "\t/* Module instances. */")?;
            let mut instance_name_counter = (0..).into_iter();
            for inst in netlist.each_cell_instance(&circuit_id) {
                let template = netlist.template_cell(&inst);

                let inst_name = if let Some(name) = netlist.cell_instance_name(&inst) {
                    mask_identifier(name.borrow()).ok_or(VerilogWriteError::InvalidIdentifier)?
                } else {
                    // Construct a name that does not yet exist in the netlist.
                    (0..)
                        .into_iter()
                        .map(|_| format!("_{}_", instance_name_counter.next().unwrap()))
                        .find(|name| {
                            netlist
                                .cell_instance_by_name(&circuit_id, name.as_str())
                                .is_none()
                        })
                        .unwrap()
                };

                // Create instance.
                let template_name = mask_identifier(netlist.cell_name(&template).borrow())
                    .ok_or(VerilogWriteError::InvalidIdentifier)?;
                writeln!(writer, "\t{} {} (", template_name, &inst_name)?;

                // Write port connections of to the instance.
                let pins = netlist
                    .each_pin(&template)
                    .zip(netlist.each_pin_instance(&inst));
                for (pin, pin_inst) in pins {
                    let pin_name = mask_identifier(netlist.pin_name(&pin).borrow())
                        .ok_or(VerilogWriteError::InvalidIdentifier)?;
                    let net = netlist.net_of_pin_instance(&pin_inst);
                    if let Some(net) = net {
                        writeln!(writer, "\t\t.{}({}),", pin_name, net_names[&net])?;
                    } else {
                        writeln!(writer, "\t\t.{}(),", pin_name)?;
                    }
                }
                writeln!(writer, "\t);")?;
                writeln!(writer)?;
            }

            // Continous assigns.
            // Create assigns statements for all pins where the pin name does not match
            // the name of the connected net.
            writeln!(writer, "\t/* Continuous assignments. */")?;
            for pin in netlist.each_pin(&circuit_id) {
                let pin_name = mask_identifier(netlist.pin_name(&pin).borrow())
                    .ok_or(VerilogWriteError::InvalidIdentifier)?;

                let direction = netlist.pin_direction(&pin);
                let pin_net = netlist.net_of_pin(&pin);
                if let Some(pin_net) = pin_net {
                    let pin_net_name = &net_names[&pin_net];
                    let pin_name_b: &String = pin_name.borrow();
                    if pin_name_b != pin_net_name {
                        // Create an `assign l = r;` statement.
                        match direction {
                            Direction::None => panic!("Pin must be either an input or an output."),
                            Direction::Input => {
                                writeln!(writer, "\tassign {} = {};", pin_net_name, pin_name)?
                            }
                            Direction::Output => {
                                writeln!(writer, "\tassign {} = {};", pin_name, pin_net_name)?
                            }
                            Direction::InOut => panic!("Pin must be either an input or an output."),
                            Direction::Clock => {
                                writeln!(writer, "\tassign {} = {};", pin_net_name, pin_name)?
                            }
                            Direction::Supply => {
                                panic!("Pin must be either an input or an output.")
                            }
                            Direction::Ground => {
                                panic!("Pin must be either an input or an output.")
                            }
                        };
                    }
                }
            }
            writeln!(writer)?;

            writeln!(writer, "endmodule")?;
            writeln!(writer)?;
        }

        Ok(())
    }
}