docs.rs failed to build nl-compiler-0.1.28
Please check the
build logs for more information.
See
Builds for ideas on how to fix a failed build,
or
Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault,
open an issue.

nl-compiler: Verilog Frontend Compilation
nl-compiler provides frontend compilation for Verilog netlists. The output format is a memory-safe netlist data structure called safety-net.
Getting Started
Getting a compilation flow working quickly is easy with nl-compiler. Here are the basic steps to follow:
1. Produce a Verilog AST with sv-parser
Use the sv-parser crate to generate an AST for your netlist:
fn sv_parse_wrapper(
s: &str,
path: Option<PathBuf>,
) -> Result<sv_parser::SyntaxTree, sv_parser::Error> {
let incl: Vec<std::path::PathBuf> = vec![];
let path = path.unwrap_or(Path::new("top.v").to_path_buf());
match sv_parser::parse_sv_str(s, path, &HashMap::new(), &incl, true, false) {
Ok((ast, _defs)) => Ok(ast),
Err(e) => Err(e),
}
}
2. Define a Cell Library
safety-net netlists are generic to the cell library used via an Instantiable interface. Simply create a cell library that implements this interface. This is where the most of the boiler-plate comes from.
use safety_net::{Net, Identifier, Instantiable}
#[derive(Debug, Clone)]
struct Gate {
name: Identifier,
inputs: Vec<Net>,
outputs: Vec<Net>,
params: HashMap<Identifier, Parameter>,
}
impl FromId for Gate {
fn from_id(s: &Identifier) -> Result<Self, safety_net::Error> {
match s.to_string().as_str() {
"AND" => Ok(Gate {
name: s.clone(),
inputs: vec!["A".into(), "B".into()],
outputs: vec!["Y".into()],
params: HashMap::new(),
}),
"INV" => Ok(Gate {
name: s.clone(),
inputs: vec!["A".into()],
outputs: vec!["ZN".into()],
params: HashMap::new(),
}),
_ => Err(safety_net::Error::ParseError(format!(
"Unknown primitive gate: {}",
s
))),
}
}
}
impl Instantiable for Gate {
fn get_name(&self) -> &Identifier {
&self.name
}
fn get_input_ports(&self) -> impl IntoIterator<Item = &Net> {
&self.inputs
}
fn get_output_ports(&self) -> impl IntoIterator<Item = &Net> {
&self.outputs
}
fn has_parameter(&self, id: &Identifier) -> bool {
self.params.contains_key(id)
}
fn get_parameter(&self, id: &Identifier) -> Option<Parameter> {
self.params.get(id).cloned()
}
fn set_parameter(&mut self, id: &Identifier, val: Parameter) -> Option<Parameter> {
self.params.insert(id.clone(), val)
}
fn parameters(&self) -> impl Iterator<Item = (Identifier, Parameter)> {
self.params.clone().into_iter()
}
fn from_constant(val: Logic) -> Option<Self> {
match val {
Logic::False => Some(Gate {
name: "GND".into(),
inputs: vec![],
outputs: vec!["Y".into()],
params: HashMap::new(),
}),
Logic::True => Some(Gate {
name: "VDD".into(),
inputs: vec![],
outputs: vec!["Y".into()],
params: HashMap::new(),
}),
_ => None,
}
}
fn get_constant(&self) -> Option<Logic> {
match self.name.to_string().as_str() {
"GND" => Some(Logic::False),
"VDD" => Some(Logic::True),
_ => None,
}
}
fn is_seq(&self) -> bool {
false
}
}
3. Compile!
fn main() -> std::io::Result<()> {
let mut buf = String::new();
let ast = sv_parse_wrapper(&buf, path).map_err(std::io::Error::other)?;
let netlist = match from_vast::<Gate>(&ast) {
Ok(nl) => nl,
Err(e) => {
eprintln!("{e}");
return Err(std::io::Error::other(e));
}
};
netlist.verify().map_err(std::io::Error::other)?;
println!("{netlist}");
Ok(())
}
Example Netlist
Here is an example netlist to get you started:
module and_test (
input wire a,
input wire b,
output wire y
);
AND _0_ (
.A(a),
.B(b),
.Y(y)
);
endmodule
Save the above file to and.v and try running it on the baseline implementation.
cargo run --example roundtrip -- and.v
Testing
Also, take a look at some of the tests:
#[test]
fn mux_lut() {
let src = "module lut_test (
a,
b,
c,
y
);
input a;
wire a;
input b;
wire b;
input c;
wire c;
output y;
wire y;
LUT3 #(
.INIT(8'b11001010)
) _0_ (
.I0(a),
.I1(b),
.I2(c),
.O(y)
);
endmodule
"
.to_string();
assert_verilog_eq!(src, roundtrip(&src).unwrap());
}