// SPDX-FileCopyrightText: 2021-2024 Thomas Kramer // SPDX-License-Identifier: AGPL-3.0-or-later use libreda_db::prelude::{traits::*, NetlistWriter}; use libreda_db::prelude::{Chip, HierarchyBase, NetlistBase, TerminalId}; use libreda_sta::models::nldm_cell_delay::{NLDMCellModel, NLDMSignal}; use libreda_sta::traits::{SignalTransitionType, TimingQuery}; use libreda_sta::*; use num_traits::Zero; use std::fs::File; use std::io::BufReader; #[test] fn test_sta() -> Result<(), StaError> { // Read netlist. let netlist = create_test_netlist(); // Load liberty library. let f = File::open("./tests/data/freepdk45/gscl45nm.lib").unwrap(); let mut buf = BufReader::new(f); let library = liberty_io::read_liberty_bytes(&mut buf).expect("Failed to read or parse liberty library."); log::info!("Linking timing library."); let timing_lib = liberty_library::LibertyTimingLibrary::new( &library, |cell_name| netlist.cell_by_name(cell_name), // Link names to netlist IDs. |cell_id, pin_name| netlist.pin_by_name(cell_id, pin_name), // Link names to netlist IDs. ) .map_err(|e| { log::error!("Failed to prepare timing library: {}", e); StaError::Other(format!("Failed to prepare timing library: {}", e)) })?; let nldm_cell_lib = NLDMCellModel::new(&timing_lib, &netlist); let top = netlist.cell_by_name("TOP").expect("TOP not found."); let mut sta = SimpleSTA::new(netlist, top.clone(), nldm_cell_lib).expect("failed to initialize STA"); let clock_input = TerminalId::PinId( sta.pin_by_name(&top, "clock") .expect("Clock pin not found."), ); use uom::si::time::*; let clock_period = Time::new::(100.); let clock_slewrate = Time::new::(100.); sta.create_clock( clock_input, ClockDefinition::new( clock_period, NLDMSignal::new(clock_slewrate, Time::zero()).with_polarity(SignalTransitionType::Rise), NLDMSignal::new(clock_slewrate, clock_period * 0.5) .with_polarity(SignalTransitionType::Fall), ), ) .expect("failed to create clock"); let default_input_signal = InputSignal::new(NLDMSignal::new( Time::new::(1.0), Time::new::(0.0), )); // Set default input signals. for input_pin_name in ["in1", "in2"] { let input_pin = sta.pin_by_name(&top, input_pin_name).unwrap(); sta.set_input_signal(input_pin, default_input_signal) .expect("failed to set input signal"); } // Run many times to detect concurrency problems: //let mut sta = sta; //for _ in 0..100000 { // sta = sta // .update_timing() // .expect("running STA failed") // .to_modifiable(); //} let sta = sta.update_timing().expect("STA failed"); let top_ref = sta.cell_ref(&top); for net in top_ref.each_net() { println!("{}", net.qname(":")); for term in net.each_terminal() { let edge_types = [libreda_sta::RiseFall::Rise, libreda_sta::RiseFall::Fall]; for edge in edge_types { let edge_type_str = match edge { libreda_sta::RiseFall::Rise => "↑", libreda_sta::RiseFall::Fall => "↓", }; println!(" {}{}", term.qname(":"), edge_type_str); println!( " related clock: {:?}", sta.report_clock(term.id().cast(), edge) ); // if let Some(arrival_time) = sta.report_aat(term.id().cast(), edge) { // println!( // " {} {}\t\t{:?}", // edge_type_str, // term.qname(":"), // arrival_time.delay(MinMax::Min), // ); // } else { // println!(" {}\t\t---", term.qname(":")); // } // if let Some(slack) = sta.report_slack(term.id().cast(), edge) { // println!( // " {} {}\t\tslack = {:?}", // term.qname(":"), // edge_type_str, // slack // ); // } } } } Ok(()) } #[test] fn test_simple_incremental_sta() -> Result<(), StaError> { // Read netlist. let netlist = create_simple_test_netlist(); // Load and link liberty library. let f = File::open("./tests/data/freepdk45/gscl45nm.lib").unwrap(); let mut buf = BufReader::new(f); let library = liberty_io::read_liberty_bytes(&mut buf).expect("Failed to read or parse liberty library."); log::info!("Linking timing library."); let timing_lib = liberty_library::LibertyTimingLibrary::new( &library, |cell_name| netlist.cell_by_name(cell_name), // Link names to netlist IDs. |cell_id, pin_name| netlist.pin_by_name(cell_id, pin_name), // Link names to netlist IDs. ) .map_err(|e| { log::error!("Failed to prepare timing library: {}", e); StaError::Other(format!("Failed to prepare timing library: {}", e)) })?; let nldm_cell_lib = NLDMCellModel::new(&timing_lib, &netlist); let top = netlist.cell_by_name("TOP").expect("TOP not found."); let mut sta = SimpleSTA::new(netlist, top.clone(), &nldm_cell_lib).expect("failed to initialize STA"); let clock_input = TerminalId::PinId( sta.pin_by_name(&top, "clock") .expect("Clock pin not found."), ); use uom::si::time::*; let clock_period = Time::new::(100.); let clock_slewrate = Time::new::(1.); sta.create_clock( clock_input, ClockDefinition::new( clock_period, NLDMSignal::new(clock_slewrate, Time::zero()).with_polarity(SignalTransitionType::Rise), NLDMSignal::new(clock_slewrate, clock_period * 0.5) .with_polarity(SignalTransitionType::Fall), ), ) .expect("failed to create clock"); let default_input_signal = InputSignal::new(NLDMSignal::new( Time::new::(1.0), Time::new::(0.0), )); // Set default input signals. for input_pin_name in ["in1"] { let input_pin = sta.pin_by_name(&top, input_pin_name).unwrap(); sta.set_input_signal(input_pin, default_input_signal) .expect("failed to set input signal"); } // Insert more inverters. let invx1 = sta.cell_by_name("INVX1").unwrap(); let pin_inv_a = sta.pin_by_name(&invx1, "A").unwrap(); let pin_inv_y = sta.pin_by_name(&invx1, "Y").unwrap(); // Append an inverter after the last inverter in the chain. for i in 0..2 { let inv_name = format!("i_inv_{}", i); let last_inv_inst = sta.cell_instance_by_name(&top, &inv_name).unwrap(); let new_inv_name = format!("i_inv_{}", i + 1); let new_inv = sta.create_cell_instance(&top, &invx1, Some(new_inv_name.into())); let new_net = sta.create_net(&top, Some(format!("_net_{}", i).into())); let old_y = sta.pin_instance(&last_inv_inst, &pin_inv_y); let new_a = sta.pin_instance(&new_inv, &pin_inv_a); let new_y = sta.pin_instance(&new_inv, &pin_inv_y); let old_net = sta.connect_pin_instance(&old_y, Some(new_net.clone())); sta.connect_pin_instance(&new_a, Some(new_net)); sta.connect_pin_instance(&new_y, old_net); // Run STA. let sta_timed = sta.update_timing().expect("STA failed"); // Get timing information. { let top = sta_timed.cell_ref(&top); let dff2 = top.cell_instance_by_name("dff2").unwrap(); let dff2_d = dff2.pin_instance_by_name("D").unwrap().into_terminal(); let aat = sta_timed.report_aat(dff2_d.id().cast(), libreda_sta::RiseFall::Rise); dbg!(aat); let rat = sta_timed.report_rat(dff2_d.id().cast(), libreda_sta::RiseFall::Rise); dbg!(rat); } } // Dump netlist. if false { let w = libreda_structural_verilog::StructuralVerilogWriter::new(); let mut buf = vec![]; w.write_netlist(&mut buf, &sta) .expect("writing to verilog failed"); let verilog = String::from_utf8(buf).expect("not utf-8 encoded"); println!("{}", verilog); } Ok(()) } pub fn create_simple_test_netlist() -> impl NetlistEdit + NetlistBaseMT + std::fmt::Debug { use libreda_db::prelude::NetlistReader; use libreda_structural_verilog::StructuralVerilogReader; let mut chip = Chip::new(); let data = r#" module TOP(clock, in1, out1); input clock; input in1; output out1; //wire clock_buf; wire inv; wire dffout1; wire dffin1; //CLKBUF1 clkbuf ( // .A(clock), // .Y(clock_buf) //); DFFPOSX1 dff1 ( .D(in1), .CLK(clock), .Q(dffout1) ); INVX1 i_inv_0 ( .A(dffout1), .Y(dffin1) ); DFFPOSX1 dff2 ( .D(dffin1), .CLK(clock), .Q(out1) ); endmodule module CLKBUF1(A, Y); input A; output Y; endmodule module INVX1(A, Y); input A; output Y; endmodule module NAND2X1(A, B, Y); input A; input B; output Y; endmodule module DFFPOSX1(D, Q, CLK); input D; input CLK; output Q; endmodule "#; let mut bytes = data.as_bytes(); let reader = StructuralVerilogReader::new(); reader .read_into_netlist(&mut bytes, &mut chip) .expect("Failed to read verilog netlist."); chip } /// Create a test netlist. pub fn create_test_netlist() -> impl NetlistEdit + NetlistBaseMT + std::fmt::Debug { use libreda_db::prelude::NetlistReader; use libreda_structural_verilog::StructuralVerilogReader; let mut chip = Chip::new(); let data = r#" module TOP(clock, in1, in2, out1, out2); input clock; input in1; input in2; output out1; output out2; wire a; wire y; wire nand; wire and; wire clock_buf; wire dffout1; wire dffout2; wire dffin1; wire dffin2; CLKBUF1 clkbuf1 ( .A(clock), .Y(clock_buf) ); NAND2X1 nand_inst1 ( .A(in1), .B(in2), .Y(nand) ); NAND2X1 nand_inst2 ( .A(dffout2), .B(dffout1), .Y(dffin1) ); INVX1 inv_inst1 ( .A(nand), .Y(and) ); INVX1 inv_inst2 ( .A(dffout2), .Y(dffin2) ); DFFPOSX1 dff_inst1 ( .D(dffin1), .CLK(clock), .Q(dffout1) ); DFFPOSX1 dff_inst2 ( .D(dffin2), .CLK(clock_buf), .Q(dffout2) ); assign out1 = dffout1; assign out2 = and; endmodule module CLKBUF1(A, Y); input A; output Y; endmodule module INVX1(A, Y); input A; output Y; endmodule module NAND2X1(A, B, Y); input A; input B; output Y; endmodule module DFFPOSX1(D, Q, CLK); input D; input CLK; output Q; endmodule "#; let mut bytes = data.as_bytes(); let reader = StructuralVerilogReader::new(); reader .read_into_netlist(&mut bytes, &mut chip) .expect("Failed to read verilog netlist."); chip }