use super::*;
use std::collections::HashSet;
use either::Either;
#[must_use]
#[inline]
pub(crate) fn find_top_module<'i>(
modules: &'i HashMap<Substr, (SVerilogModule, ModuleMap)>,
top: Option<&'_ str>
) -> Option<(&'i Substr, &'i SVerilogModule, &'i ModuleMap)> {
if modules.len() == 0 {
clilog::error!(NL_SV_PARSE, "Empty verilog netlist.");
return None;
}
if let Some(top) = top {
if let Some((k, (v1, v2))) = modules.get_key_value(top) {
Some((k, v1, v2))
}
else {
clilog::error!(
NL_SV_TOPMODULE_NF,
"Top module {} not found in the verilog code", top);
return None;
}
}
else if modules.len() != 1 {
let mut referenced = HashSet::<&Substr>::new();
for (_, (m, _)) in modules {
for cell in &m.cells {
if modules.contains_key(&cell.macro_name) {
referenced.insert(&cell.macro_name);
}
}
}
let unrefs: Vec<_> = modules.iter()
.filter(|(s, _)| !referenced.contains(s)).collect();
if unrefs.len() == 1 {
let (s, (m, mm)) = unrefs[0];
clilog::info!(
NL_SV_GUESSTOP,
"The top module is guessed to be {}.", s);
Some((s, m, mm))
}
else if unrefs.len() == 0 {
clilog::error!(
NL_SV_CANTGUESSTOP,
"There are cyclic references in netlist, cannot guess top module.");
return None;
}
else {
clilog::error!(
NL_SV_CANTGUESSTOP,
"There are {} potential top modules: {:?}. Please explicitly specify one of them as the top module.",
unrefs.len(), unrefs.iter().map(|(s, _)| s).collect::<Vec<_>>());
return None;
}
}
else {
let (s, (m, mm)) = modules.iter().next().unwrap();
Some((s, m, mm))
}
}
pub fn enum_in_width(
w: Option<SVerilogRange>
) -> impl Iterator<Item = Option<isize>> {
match w {
None => Either::Left(Some(None).into_iter()),
Some(r) => Either::Right(r.map(|c| Some(c)))
}
}
#[readonly::make]
pub struct ModuleMap {
pub def_widths: HashMap<Substr, SVerilogRange>,
pub def_types: HashMap<Substr, WireDefType>,
pub port_widths: HashMap<Substr, SVerilogRange>,
}
pub enum ExprBit<'i> {
Const(u8 ),
Var(&'i Substr, Option<isize>)
}
fn eval_expr_len(
def_widths: &HashMap<Substr, SVerilogRange>,
expr: &Wirexpr
) -> usize {
#[inline]
fn len_basic(
def_widths: &HashMap<Substr, SVerilogRange>,
exprbasic: &WirexprBasic
) -> usize {
use WirexprBasic::*;
match exprbasic {
Full(s) => {
match def_widths.get(s.as_str()) {
Some(range) => range.len(),
None => 1
}
},
SingleBit(_, _) => 1,
Slice(_, range) => range.len(),
Literal(size, _, _) => *size
}
}
use Wirexpr::*;
match expr {
Basic(basic) => len_basic(def_widths, basic),
Concat(v) => v.iter().map(|b| len_basic(def_widths, b)).sum()
}
}
impl ModuleMap {
pub fn from(m: &SVerilogModule) -> ModuleMap {
let def_widths: HashMap<Substr, SVerilogRange> = m.defs.iter()
.filter_map(|SVerilogWireDef{name, width, ..}| {
let w = width.as_ref()?.clone();
Some((name.clone(), w))
})
.collect();
let mut def_types = HashMap::with_capacity(m.defs.len());
for SVerilogWireDef{name, typ, ..} in &m.defs {
use WireDefType::*;
match def_types.get_mut(name) {
Some(v) => {
match (&v, typ) {
(Wire, Input | Output | InOut) => { *v = *typ; }
(_, Wire) => {}
_ => { assert_eq!(v, typ, "conflicting def"); }
}
}
None => { def_types.insert(name.clone(), *typ); }
}
}
let port_widths = m.ports.iter().filter_map(|def| {
use SVerilogPortDef::*;
match def {
Basic(name) => match def_widths.get(name.as_str()) {
Some(w) => Some((name.clone(), *w)),
None => None
},
Conn(name, expr) => {
let width = eval_expr_len(&def_widths, expr);
if width > 1 {
return Some((name.clone(),
SVerilogRange(0, width as isize - 1)))
}
let expr_basic_has_vector = |eb: &WirexprBasic| -> bool {
use WirexprBasic::*;
match eb {
Full(name) => def_widths.contains_key(name),
SingleBit(_, _) => false,
Slice(_, _) => true,
Literal(_, _, _) => false
}
};
use Wirexpr::*;
let has_vector = match expr {
Basic(eb) => expr_basic_has_vector(eb),
Concat(v) => v.iter().any(expr_basic_has_vector)
};
match has_vector {
true => Some((name.clone(), SVerilogRange(0, 0))),
false => None
}
}
}
}).collect();
ModuleMap { def_widths, def_types, port_widths }
}
pub fn eval_expr<'a>(
&'a self, expr: &'a Wirexpr
) -> impl Iterator<Item = ExprBit<'a>> + 'a {
use Either::*;
#[inline]
fn eval_basic<'a>(
mm: &'a ModuleMap, exprbasic: &'a WirexprBasic,
) -> impl Iterator<Item = ExprBit<'a>> + 'a {
use WirexprBasic::*;
use ExprBit::*;
let index_map = |s: &'a Substr| move |i| Var(s, Some(i));
match exprbasic {
Full(s) => match mm.def_widths.get(s.as_str()) {
Some(range) => Right(Left(range.map(index_map(s)))),
None => Left(Some(Var(s, None)).into_iter())
},
SingleBit(s, i) => Left(Some(Var(s, Some(*i))).into_iter()),
Slice(s, range) => Right(Left(range.map(index_map(s)))),
Literal(size, value, is_xz) => Right(Right({
let (value, is_xz) = (*value, *is_xz);
(0..*size).rev()
.map(move |i| Const((((is_xz >> i & 1) << 1) |
((value >> i & 1))) as u8))
}))
}
}
use Wirexpr::*;
match expr {
Basic(basic) => Left(eval_basic(self, basic)),
Concat(v) => Right(v.iter().map(|b| eval_basic(self, b)).flatten())
}
}
pub fn eval_expr_len(&self, expr: &Wirexpr) -> usize {
eval_expr_len(&self.def_widths, expr)
}
}
#[must_use]
pub(crate) fn estimate_size<'i>(
modules: &'i HashMap<Substr, (SVerilogModule, ModuleMap)>,
parent_modules: &mut HashSet<&'i Substr>,
(cur_name, cur_m, cur_mm): (&'i Substr, &'i SVerilogModule, &'i ModuleMap),
cache: &mut HashMap<&'i Substr, (usize, usize)>
) -> Option<(usize, usize)> {
if let Some((x, y)) = cache.get(cur_name) {
return Some((*x, *y));
}
if !parent_modules.insert(cur_name) {
clilog::error!(
NL_SV_RECUR, "module {} has recursion which is NOT allowed",
cur_name);
return None;
}
let mut parent_modules = scopeguard::guard(parent_modules, |parent_modules| {
parent_modules.remove(cur_name);
});
let mut num_cells = 0;
let mut num_logic_pins = cur_m.defs.iter()
.map(|def| def.width.map(|r| r.len()).unwrap_or(1))
.sum::<usize>();
num_logic_pins += cur_m.ports.iter()
.map(|port| match port {
SVerilogPortDef::Basic(_) => 0,
SVerilogPortDef::Conn(_, e) => cur_mm.eval_expr_len(e)
})
.sum::<usize>();
for cell in &cur_m.cells {
if let Some((m, mm)) = modules.get(&cell.macro_name) {
let (c, lp) = estimate_size(
modules, &mut parent_modules,
(&cell.macro_name, m, mm), cache)?;
num_cells += c;
num_logic_pins += lp;
}
else {
num_cells += 1;
num_logic_pins += cell.ioports.iter()
.map(|(_, expr)| cur_mm.eval_expr_len(expr))
.sum::<usize>();
}
}
cache.insert(cur_name, (num_cells, num_logic_pins));
Some((num_cells, num_logic_pins))
}