Skip to content

Commit

Permalink
support bus style in verilog
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric committed May 15, 2021
1 parent 60a2f30 commit b743664
Show file tree
Hide file tree
Showing 8 changed files with 4,129 additions and 467 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# netlist
Low level library-independent data structure for VLSI design


## Purpose

netlist is a common structure in VLSI design, especially in logical synthesis, P&R, formal verification, STA.

This crate wants to abstract netlist to a generic style for more common use.



## Feature
**1. graph-like data structure**


**2. verilog parser**
The verilog parser in this crate is a minimal subset of verilog-2001, which can parse structural verilog syntax into netlist.

**3. verilog saver**
Save netlist as verilog.

## Limitation
* Not full verilog2001 featured verilog parser. Currently only support single module design and bus-based wire/port declare not supported
*
2,070 changes: 2,070 additions & 0 deletions examples/Intro_TopNetlist.v

Large diffs are not rendered by default.

2,250 changes: 1,854 additions & 396 deletions examples/exported.v

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ pub struct Node<N> {
pub struct Pin<P> {
pub name: String,
pub direction: PinDirection,
pub node: NodeIndex,
pub bitwidth: u32,
pub node: Vec<NodeIndex>,
pub data: P,
}

Expand Down
11 changes: 8 additions & 3 deletions src/parser/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use nom::branch::alt;

use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, alphanumeric1, digit1, multispace0};
use nom::combinator::{recognize,map_res};
use nom::combinator::{map_res, opt, recognize};

use nom::multi::{many0, many1};

use super::ParseRes;
use nom::sequence::{delimited, pair, tuple};
use std::str::{self,FromStr};
use std::str::{self, FromStr};

// basic parse.

Expand All @@ -34,7 +34,7 @@ pub fn tstring(s: &str) -> ParseRes<&str, &str> {
/// this parser allow hierachical representation of module name
pub fn identifier(s: &str) -> ParseRes<&str, &str> {
ws(recognize(pair(
tstring,
pair(opt(tag("\\")), tstring),
many0(alt((
recognize(many1(tuple((tag("\\\\["), digit1, tag("\\\\]"))))),
recognize(many1(tuple((tag("\\["), digit1, tag("\\]"))))),
Expand Down Expand Up @@ -79,4 +79,9 @@ mod test {
let input = "abc/def/net[1][2]";
let (_, _) = identifier(input).unwrap();
}
#[test]
fn test_identifier_6() {
let input = "\\abc/def/net[1][2]";
let (_, _) = identifier(input).unwrap();
}
}
81 changes: 61 additions & 20 deletions src/parser/subparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use super::base::{identifier, number, tstring, ws};
use crate::model::PinDirection;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag};
use nom::character::complete::alphanumeric1;
use nom::combinator::{map, value};
use nom::combinator::{map, opt, value};
use nom::error::context;
use nom::multi::separated_list1;
use nom::sequence::{delimited, pair, preceded, separated_pair, terminated, tuple};
Expand Down Expand Up @@ -40,32 +39,55 @@ pub fn port_map_stmt(s: &str) -> ParseRes<&str, Vec<&str>> {
)(s)
}

pub fn port_direction_declare_stmt(s: &str) -> ParseRes<&str, (PinDirection, &str)> {
pub fn port_direction_declare_stmt(
s: &str,
) -> ParseRes<&str, (PinDirection, Option<(u32, u32)>, Vec<&str>)> {
context(
"Port Direction Declare Statement",
terminated(
alt((
map(preceded(ws(tag("input")), identifier), |d| {
(PinDirection::Input, d)
}),
map(preceded(ws(tag("output")), identifier), |d| {
(PinDirection::Output, d)
}),
)),
ws(tag(";")),
),
alt((
map(
delimited(
ws(tag("input")),
tuple((opt(bitwidth), separated_list1(tag(","), identifier))),
ws(tag(";")),
),
|d| (PinDirection::Input, d.0, d.1),
),
map(
delimited(
ws(tag("output")),
tuple((opt(bitwidth), separated_list1(tag(","), identifier))),
ws(tag(";")),
),
|d| (PinDirection::Output, d.0, d.1),
),
)),
)(s)
}

pub fn wire_declare_stmt(s: &str) -> ParseRes<&str, &str> {
// wire declare

// examples
// wire a1;
// wire [2:0] a2;
// wire a1, a2, a3;
pub fn wire_declare_stmt(s: &str) -> ParseRes<&str, (Option<(u32, u32)>, Vec<&str>)> {
context(
"Wire Declare Statement",
terminated(preceded(ws(tag("wire")), identifier), ws(tag(";"))),
delimited(
ws(tag("wire")),
tuple((opt(bitwidth), separated_list1(tag(","), identifier))),
ws(tag(";")),
),
)(s)
}

// return msb and lsb
pub fn port_bitwidth(s: &str) -> ParseRes<&str, (u32, u32)> {
// bus bit in wire declare or port declare

// examples
// wire [11:0] a;
// input [2:0] b;
pub fn bitwidth(s: &str) -> ParseRes<&str, (u32, u32)> {
delimited(tag("["), separated_pair(number, tag(":"), number), tag("]"))(s)
}

Expand All @@ -77,9 +99,28 @@ pub fn comment(s: &str) -> ParseRes<&str, ()> {
)(s)
}

// pin2net binding

// examples
// .A(n1)
fn binding_parser(s: &str) -> ParseRes<&str, BindingT> {
tuple((
preceded(tag("."), alphanumeric1),
delimited(ws(tag("(")), tstring, ws(tag(")"))),
preceded(ws(tag(".")), tstring),
delimited(ws(tag("(")), identifier, ws(tag(")"))),
))(s)
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_comment_1() {
let input = "// Date : Mon Jul 20 00:19:01 2020";
let (_, _) = comment(input).unwrap();
}
#[test]
fn test_comment_2() {
let input = "///////////\n\r";
let (_, _) = comment(input).unwrap();
}
}
151 changes: 107 additions & 44 deletions src/parser/verilog_parser.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
use super::{
base::{tstring, ws},
subparser::{instantiate_stmt, port_direction_declare_stmt, port_map_stmt, wire_declare_stmt},
subparser::{
comment, instantiate_stmt, port_direction_declare_stmt, port_map_stmt, wire_declare_stmt,
},
ParseRes,
};
use crate::model::{DriveLoad, Gate, Net, NetList, Node, Pin, PinDirection};
use nom::{
branch::permutation,
bytes::complete::tag,
multi::{many0, many1},
sequence::{delimited, tuple},
sequence::{delimited, preceded, tuple},
};

pub fn verilog_parser<W: Default, N: Default, G: Default, P: Default>(
s: &str,
) -> ParseRes<&str, NetList<W, N, G, P>> {
preceded(many0(comment), module_parser)(s)
}

pub fn module_parser<W: Default, N: Default, G: Default, P: Default>(
s: &str,
) -> ParseRes<&str, NetList<W, N, G, P>> {
delimited(
ws(tag("module")),
Expand All @@ -23,71 +31,126 @@ pub fn verilog_parser<W: Default, N: Default, G: Default, P: Default>(
many1(port_direction_declare_stmt),
many1(instantiate_stmt),
many0(wire_declare_stmt),
many0(comment),
)),
)),
ws(tag("endmodule")),
)(s)
.map(|(s, d)| {
let mut netlist = NetList::default();
netlist.name = d.0.to_string();

// create new pin
for p in (d.2).0 {
let pname: &str = p.1;
// node,pin,net index
let node_id = netlist.nodes.len();
let pin_id = netlist.pins.len();
let net_id = netlist.nets.len();
// create new net,pin,node record in hashmap
// pin net name eq pin name
netlist.net_map.insert(pname.to_string(), net_id);
netlist.pin_map.insert(pname.to_string(), pin_id);
let mut node_id = netlist.nodes.len();
let mut pin_id = netlist.pins.len();
let mut net_id = netlist.nets.len();

match p.0 {
// Input pin means that
// (1) new node with net as load will be created
// (2) new input pin wih created node will be created
// (3) new net with created node will be created
PinDirection::Input => {
netlist.nodes.push(Node {
name: pname.to_string(),
from: DriveLoad::Pin(pin_id),
to: DriveLoad::Net(net_id),
data: N::default(),
});
netlist.pins.push(Pin {
name: pname.to_string(),
direction: p.0,
node: node_id,
..Default::default()
});
netlist.nets.push(Net {
name: pname.to_string(), // pname is also net name, according to verilog standard
nodes: vec![node_id],
..Default::default()
});
for pname in &p.2 {
let mut new_pin = Pin {
name: pname.to_string(),
bitwidth: 1,
..Default::default() // default as Input direction
};
if let Some((msb, lsb)) = p.1 {
new_pin.bitwidth = msb - lsb + 1;
for bit in lsb..=msb {
let net_name = &format!("{}[{}]", pname, bit);
netlist.nets.push(Net {
name: net_name.to_string(),
nodes: vec![node_id],
..Default::default()
});
netlist.net_map.insert(net_name.to_string(), net_id);
netlist.nodes.push(Node {
name: net_name.to_string(),
from: DriveLoad::Pin(pin_id),
to: DriveLoad::Net(net_id),
data: N::default(),
});
new_pin.node.push(node_id);
node_id += 1;
pin_id += 1;
net_id += 1;
}
} else {
new_pin.node.push(node_id);
netlist.nets.push(Net {
name: pname.to_string(), // pname is also net name, according to verilog standard
nodes: vec![node_id],
..Default::default()
});
netlist.net_map.insert(pname.to_string(), net_id);
netlist.nodes.push(Node {
name: pname.to_string(),
from: DriveLoad::Pin(pin_id),
to: DriveLoad::Net(net_id),
data: N::default(),
});
}
netlist.pins.push(new_pin);
netlist.pin_map.insert(pname.to_string(), pin_id);
}
}
// Output pin means that
// (1) new node with pin as load will be created
// (2) new output pin with created node will be created
// (3) new net that same name with pin name will be created, with created node
PinDirection::Output => {
netlist.nodes.push(Node {
name: pname.to_string(),
from: DriveLoad::Net(net_id),
to: DriveLoad::Pin(pin_id),
data: N::default(),
});
netlist.pins.push(Pin {
name: pname.to_string(),
direction: p.0,
node: node_id,
..Default::default()
});
netlist.nets.push(Net {
name: pname.to_string(), // pname is also net name, according to verilog standard
nodes: vec![node_id],
..Default::default()
});
for pname in &p.2 {
let mut new_pin = Pin {
name: pname.to_string(),
bitwidth: 1,
direction: PinDirection::Output,
..Default::default()
};
if let Some((msb, lsb)) = p.1 {
new_pin.bitwidth = msb - lsb + 1;
for bit in lsb..=msb {
let net_name = &format!("{}[{}]", pname, bit);
netlist.nets.push(Net {
name: net_name.to_string(),
nodes: vec![node_id],
..Default::default()
});
netlist.net_map.insert(net_name.to_string(), net_id);
netlist.nodes.push(Node {
name: net_name.to_string(),
from: DriveLoad::Net(net_id),
to: DriveLoad::Pin(pin_id),
data: N::default(),
});
new_pin.node.push(node_id);
node_id += 1;
pin_id += 1;
net_id += 1;
}
} else {
// when pin bitwidth == 1, pin_name = net_name = node_name
new_pin.node.push(node_id);
netlist.nets.push(Net {
name: pname.to_string(),
nodes: vec![node_id],
..Default::default()
});
netlist.net_map.insert(pname.to_string(), net_id);
netlist.nodes.push(Node {
name: pname.to_string(),
from: DriveLoad::Net(net_id),
to: DriveLoad::Pin(pin_id),
data: N::default(),
});
}
netlist.pins.push(new_pin);
netlist.pin_map.insert(pname.to_string(), pin_id);
}
}
}
}
Expand Down
Loading

0 comments on commit b743664

Please sign in to comment.