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};
#[derive(Debug)]
pub enum VerilogWriteError {
IoError(std::io::Error),
InvalidIdentifier,
}
impl From<std::io::Error> for VerilogWriteError {
fn from(err: Error) -> Self {
VerilogWriteError::IoError(err)
}
}
pub struct StructuralVerilogWriter {}
impl StructuralVerilogWriter {
pub fn new() -> Self {
StructuralVerilogWriter {}
}
}
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 {
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)?;
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)?;
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)?;
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)
.and_then(|name| mask_identifier(name.borrow()))
.unwrap_or_else(|| {
(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();
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)?;
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 {
(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()
};
let template_name = mask_identifier(netlist.cell_name(&template).borrow())
.ok_or(VerilogWriteError::InvalidIdentifier)?;
writeln!(writer, "\t{} {} (", template_name, &inst_name)?;
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)?;
}
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 {
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(())
}
}