ASIC place & route framework. This crate contains interface definitions of the core parts of the place & route flow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
4.4 KiB

/*
* Copyright (c) 2020-2021 Thomas Kramer.
*
* This file is part of LibrEDA
* (see https://codeberg.org/libreda).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//! Provides sanity checks for netlists such as finding driving conflicts or non-driven nets.
use crate::db::{NetlistBase, NetlistUtil, TerminalId, Direction};
/// For each net in the `cell` check that there's exactly one cell driving it.
/// The check is done only based on the pin direction as annotated in the netlist.
/// The internals of child cells are ignored and hence the check is not perfect: If a child cell
/// has an inout pin then it could be resolved by looking at the internals.
///
/// Errors are logged with `log::error!()`.
///
/// Return `Ok` when no problem was found,
/// return `Err((drive_conflicts, nets_without_drivers))` when a problem was found.
/// Where `drive_conflicts` is a list of the form `[(net, [driver1 terminal, driver2 terminal, ...], ...]`.
/// `nets_without_drivers` is a list of nets without driver.
pub fn check_drivers_in_cell<N: NetlistBase>(netlist: &N, cell: &N::CellId)
-> Result<(), (Vec<(N::NetId, Vec<TerminalId<N>>)>, Vec<N::NetId>)> {
let cell_name = netlist.cell_name(cell);
log::debug!("Check for drive conflicts and nets without driver in cell '{}'.", cell_name);
let mut drive_conflicts = vec![];
let mut nets_without_driver = vec![];
let mut unconnected_nets = vec![]; // Nets without any terminals attached.
for net in netlist.each_internal_net(cell) {
let net_name = netlist.net_name(&net);
log::debug!("Checking net '{:?}'", net_name);
let mut drivers = vec![];
let mut inouts = vec![];
for t in netlist.each_terminal_of_net(&net) {
// A pin of the parent cell is considered to be a source when it's direction is 'INPUT',
// however a pin instance that connects to a child instance is considered a sink when it's direction is 'INPUT'.
match &t {
TerminalId::PinId(p) => {
match netlist.pin_direction(p) {
Direction::Input => drivers.push(t),
Direction::InOut => inouts.push(t),
_ => {}
}
}
TerminalId::PinInstId(p) => {
match netlist.pin_direction(&netlist.template_pin(p)) {
Direction::Output => drivers.push(t),
Direction::InOut => inouts.push(t),
_ => {}
}
}
}
}
log::debug!("Number of drivers: {}", drivers.len());
log::debug!("Number of inouts: {}", inouts.len());
if drivers.is_empty() {
if netlist.num_net_terminals(&net) == 0 {
if !netlist.is_constant_net(&net) {
// Constant nets are ignored for this warning.
log::warn!("Unconnected net '{:?}' in cell '{}'.", net_name, cell_name);
unconnected_nets.push(net);
}
} else {
log::error!("No driver found on net '{:?}' in cell '{}'.", net_name, cell_name);
if netlist.is_constant_net(&net) {
log::warn!("Constant net '{:?}' in cell '{}' should be connected to a tie cell.", net_name, cell_name);
}
nets_without_driver.push(net);
}
} else if drivers.len() > 1 {
log::error!("Drive conflict. {} drivers found on net '{:?}' in cell '{}'.", drivers.len(), net_name, cell_name);
drive_conflicts.push((net, drivers));
}
}
if drive_conflicts.is_empty() && nets_without_driver.is_empty() {
Ok(())
} else {
Err((drive_conflicts, nets_without_driver))
}
}