diff --git a/Cargo.lock b/Cargo.lock index 51fb8cc..bdca490 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -688,6 +688,15 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -807,6 +816,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "paste" version = "1.0.15" @@ -1120,6 +1152,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semver" version = "1.0.24" @@ -1384,7 +1422,7 @@ dependencies = [ [[package]] name = "topstitch" -version = "0.93.0" +version = "0.94.0" dependencies = [ "cargo_metadata", "curl", @@ -1396,6 +1434,7 @@ dependencies = [ "log", "nalgebra", "num-bigint", + "parking_lot", "paste", "regex", "rstar", @@ -1531,6 +1570,12 @@ dependencies = [ "winsafe", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 47f26aa..3492164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "topstitch" -version = "0.93.0" +version = "0.94.0" edition = "2024" license = "Apache-2.0" description = "Stitch together Verilog modules with Rust" @@ -20,6 +20,7 @@ fixedbitset = "0.5.7" paste = "1.0.15" geo = "0.31.0" rstar = "0.12.2" +parking_lot = "0.12" [dev-dependencies] cargo_metadata = "0.18" diff --git a/src/connection/port_slice.rs b/src/connection/port_slice.rs index fa82856..ca89dff 100644 --- a/src/connection/port_slice.rs +++ b/src/connection/port_slice.rs @@ -204,7 +204,7 @@ impl PortSliceConnection { None => return result, }; for next in &port_connections - .borrow() + .read() .slice(port_slice.msb, port_slice.lsb) { if let ConnectedItem::PortSlice(next_other) = &next.other @@ -259,7 +259,7 @@ mod tests { a.slice(3, 0).connect(&b.slice(7, 4)); a.slice(11, 8).connect(&b.slice(3, 0)); - let mut overlaps = a.get_port_connections().unwrap().borrow().slice(5, 2); + let mut overlaps = a.get_port_connections().unwrap().read().slice(5, 2); sort_for_test(&mut overlaps); assert_eq!(overlaps.len(), 2); @@ -268,10 +268,10 @@ mod tests { assert_eq!(overlaps[1].this, a.slice(5, 4)); assert_eq!(overlaps[1].other, b.slice(13, 12)); - let empty = a.get_port_connections().unwrap().borrow().slice(13, 12); + let empty = a.get_port_connections().unwrap().read().slice(13, 12); assert_eq!(empty.len(), 0); - let edge = a.get_port_connections().unwrap().borrow().slice(8, 8); + let edge = a.get_port_connections().unwrap().read().slice(8, 8); assert_eq!(edge.len(), 1); assert_eq!(edge[0].this, a.bit(8)); assert_eq!(edge[0].other, b.bit(0)); @@ -294,7 +294,7 @@ mod tests { .get_port("x") .get_port_connections() .unwrap() - .borrow() + .read() .slice(5, 2) .trace(); sort_for_test(&mut traced); @@ -337,7 +337,7 @@ mod tests { .get_port("i") .get_port_connections() .unwrap() - .borrow() + .read() .slice(3, 2) .trace(); sort_for_test(&mut traced); @@ -360,7 +360,7 @@ mod tests { let p = m.add_port("p", IO::Input(8)); p.tieoff(0xaau32); - let overlaps = p.get_port_connections().unwrap().borrow().slice(6, 1); + let overlaps = p.get_port_connections().unwrap().read().slice(6, 1); assert_eq!(overlaps.len(), 1); assert_eq!(overlaps[0].this, p.slice(6, 1)); assert_eq!( @@ -378,7 +378,7 @@ mod tests { let segments = q .get_port_connections() .unwrap() - .borrow() + .read() .make_non_overlapping(); // resolve should panic for Output with Unused let _ = segments[0].to_expression_source(); @@ -397,7 +397,7 @@ mod tests { .get_port("i") .get_port_connections() .unwrap() - .borrow() + .read() .make_non_overlapping(); assert_eq!(segments.len(), 1); assert_eq!(segments[0][0].this, ai.get_port("i").slice(1, 0)); @@ -415,7 +415,7 @@ mod tests { let segments = y .get_port_connections() .unwrap() - .borrow() + .read() .make_non_overlapping(); assert_eq!(segments.len(), 1); assert_eq!(segments[0][0].this, y.slice(1, 0)); @@ -441,7 +441,7 @@ mod tests { .get_port("b_io") .get_port_connections() .unwrap() - .borrow() + .read() .trace() .make_non_overlapping(); assert_eq!(segments.len(), 1); @@ -548,7 +548,7 @@ mod tests { let mut non_overlapping = port .get_port_connections() .unwrap() - .borrow() + .read() .trace() .make_non_overlapping(); assert_eq!(non_overlapping.len(), segments.len()); diff --git a/src/intf.rs b/src/intf.rs index db4d14d..fe4feb5 100644 --- a/src/intf.rs +++ b/src/intf.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; -use std::rc::{Rc, Weak}; +use parking_lot::RwLock; +use std::sync::{Arc, Weak}; use indexmap::IndexMap; @@ -25,7 +25,7 @@ mod width; pub enum Intf { ModDef { name: String, - mod_def_core: Weak>, + mod_def_core: Weak>, }, ModInst { intf_name: String, @@ -42,7 +42,7 @@ impl Intf { } } - pub(crate) fn get_mod_def_core(&self) -> Rc> { + pub(crate) fn get_mod_def_core(&self) -> Arc> { match self { Intf::ModDef { mod_def_core, .. } => mod_def_core.upgrade().unwrap(), Intf::ModInst { hierarchy, .. } => hierarchy @@ -60,7 +60,7 @@ impl Intf { mod_def_core, name, .. } => { let core = mod_def_core.upgrade().unwrap(); - let binding = core.borrow(); + let binding = core.read(); let mod_def = ModDef { core: core.clone() }; let mapping = binding.interfaces.get(name).unwrap(); mapping @@ -82,7 +82,7 @@ impl Intf { hierarchy: hierarchy.clone(), }; let inst_core = inst.mod_def_core_of_instance(); - let inst_binding = inst_core.borrow(); + let inst_binding = inst_core.read(); let inst_mapping = inst_binding.interfaces.get(intf_name).unwrap(); inst_mapping .iter() @@ -111,7 +111,7 @@ impl Intf { mod_def_core, name, .. } => { let core = mod_def_core.upgrade().unwrap(); - let binding = core.borrow(); + let binding = core.read(); let mod_def = ModDef { core: core.clone() }; let mapping = binding.interfaces.get(name)?; mapping @@ -127,7 +127,7 @@ impl Intf { hierarchy: hierarchy.clone(), }; let inst_core = inst.mod_def_core_of_instance(); - let inst_binding = inst_core.borrow(); + let inst_binding = inst_core.read(); let inst_mapping = inst_binding.interfaces.get(intf_name)?; inst_mapping .get(func_name) @@ -146,7 +146,7 @@ impl Intf { let core = mod_def_core.upgrade().unwrap(); let mod_def = ModDef { core: core.clone() }; let removed = { - let mut binding = core.borrow_mut(); + let mut binding = core.write(); let mapping = binding.interfaces.get_mut(name)?; mapping.shift_remove(func_name) }; @@ -162,7 +162,7 @@ impl Intf { }; let inst_core = inst.mod_def_core_of_instance(); let removed = { - let mut inst_binding = inst_core.borrow_mut(); + let mut inst_binding = inst_core.write(); let inst_mapping = inst_binding.interfaces.get_mut(intf_name)?; inst_mapping.shift_remove(func_name) }; @@ -203,7 +203,7 @@ impl Intf { match self { Intf::ModDef { .. } => { let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); core.mod_def_intf_metadata .entry(self.get_intf_name()) .or_default() @@ -216,7 +216,7 @@ impl Intf { .inst_name .to_string(); let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); core.mod_inst_intf_metadata .entry(inst_name) .or_default() @@ -232,7 +232,7 @@ impl Intf { match self { Intf::ModDef { .. } => { let core_rc = self.get_mod_def_core(); - let core = core_rc.borrow(); + let core = core_rc.read(); core.mod_def_intf_metadata .get(&self.get_intf_name()) .and_then(|metadata| metadata.get(key.as_ref()).cloned()) @@ -244,7 +244,7 @@ impl Intf { .inst_name .as_str(); let core_rc = self.get_mod_def_core(); - let core = core_rc.borrow(); + let core = core_rc.read(); core.mod_inst_intf_metadata .get(inst_name) .and_then(|intfs| intfs.get(&self.get_intf_name())) @@ -257,7 +257,7 @@ impl Intf { match self { Intf::ModDef { .. } => { let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if let Some(metadata) = core.mod_def_intf_metadata.get_mut(&self.get_intf_name()) { metadata.remove(key.as_ref()); if metadata.is_empty() { @@ -272,7 +272,7 @@ impl Intf { .inst_name .as_str(); let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if let Some(intfs) = core.mod_inst_intf_metadata.get_mut(inst_name) { let intf_name = self.get_intf_name(); if let Some(metadata) = intfs.get_mut(&intf_name) { diff --git a/src/intf/debug.rs b/src/intf/debug.rs index 31f007d..05cc90a 100644 --- a/src/intf/debug.rs +++ b/src/intf/debug.rs @@ -5,7 +5,7 @@ use crate::Intf; impl std::fmt::Debug for Intf { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mod_def_core = self.get_mod_def_core(); - let core = mod_def_core.borrow(); + let core = mod_def_core.read(); match self { Intf::ModDef { name, .. } => { writeln!(f, "Interface Mapping:")?; @@ -27,7 +27,7 @@ impl std::fmt::Debug for Intf { .inst_name .as_str(); let inst_core = core.instances.get(inst_name).unwrap(); - let inst_binding = inst_core.borrow(); + let inst_binding = inst_core.read(); writeln!(f, "Interface Mapping:")?; for (func_name, (port_name, msb, lsb)) in inst_binding.interfaces.get(intf_name).unwrap() @@ -48,7 +48,7 @@ impl Intf { pub(crate) fn debug_string(&self) -> String { match self { Intf::ModDef { name, .. } => { - format!("{}.{}", self.get_mod_def_core().borrow().name, name) + format!("{}.{}", self.get_mod_def_core().read().name, name) } Intf::ModInst { intf_name, diff --git a/src/mod_def.rs b/src/mod_def.rs index 5f15bbe..885c289 100644 --- a/src/mod_def.rs +++ b/src/mod_def.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 pub use self::pins::SpreadPinsOptions; +use parking_lot::{ + MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, +}; use indexmap::IndexMap; -use std::cell::{Ref, RefCell, RefMut}; use std::collections::{HashMap, HashSet}; -use std::rc::Rc; +use std::sync::Arc; use crate::{Intf, MetadataKey, MetadataValue, Port, Usage}; @@ -52,7 +54,7 @@ pub use edges::{ /// in Verilog. #[derive(Clone)] pub struct ModDef { - pub(crate) core: Rc>, + pub(crate) core: Arc>, } #[macro_export] @@ -93,7 +95,7 @@ impl ModDef { /// Creates a new module definition with the given name. pub fn new(name: impl AsRef) -> ModDef { ModDef { - core: Rc::new(RefCell::new(ModDefCore { + core: Arc::new(RwLock::new(ModDefCore { name: name.as_ref().to_string(), ports: IndexMap::new(), enum_ports: IndexMap::new(), @@ -125,31 +127,31 @@ impl ModDef { } fn frozen(&self) -> bool { - self.core.borrow().verilog_import.is_some() + self.core.read().verilog_import.is_some() } /// Returns the name of this module definition. pub fn get_name(&self) -> String { - self.core.borrow().name.clone() + self.core.read().name.clone() } /// Configures how this module definition should be used when validating /// and/or emitting Verilog. pub fn set_usage(&self, usage: Usage) { - self.core.borrow_mut().usage = usage; + self.core.write().usage = usage; } pub fn set_default_connection_max_distance(&self, value: Option) { - self.core.borrow_mut().default_connection_max_distance = value; + self.core.write().default_connection_max_distance = value; } pub fn get_default_connection_max_distance(&self) -> Option { - self.core.borrow().default_connection_max_distance + self.core.read().default_connection_max_distance } /// Returns the `Usage` of this module definition. pub fn get_usage(&self) -> Usage { - self.core.borrow().usage.clone() + self.core.read().usage.clone() } pub fn set_metadata( @@ -158,22 +160,18 @@ impl ModDef { value: impl Into, ) -> Self { self.core - .borrow_mut() + .write() .mod_def_metadata .insert(key.into(), value.into()); self.clone() } pub fn get_metadata(&self, key: impl AsRef) -> Option { - self.core - .borrow() - .mod_def_metadata - .get(key.as_ref()) - .cloned() + self.core.read().mod_def_metadata.get(key.as_ref()).cloned() } pub fn clear_metadata(&self, key: impl AsRef) -> Self { - self.core.borrow_mut().mod_def_metadata.remove(key.as_ref()); + self.core.write().mod_def_metadata.remove(key.as_ref()); self.clone() } @@ -198,31 +196,31 @@ impl ModDef { shape.starts_with_leftmost_vertical_edge(), "ModDef shapes must start with the leftmost vertical edge." ); - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); core.track_occupancies = Some(TrackOccupancies::new(shape.num_edges())); core.shape = Some(shape); } /// Define the layer of this module. pub fn set_layer(&self, layer: impl AsRef) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); core.layer = Some(layer.as_ref().to_string()); } /// Returns this module's shape and its layer, if defined. pub fn get_shape(&self) -> Option { - self.core.borrow().shape.clone() + self.core.read().shape.clone() } /// Returns this module's layer, if defined. pub fn get_layer(&self) -> Option { - self.core.borrow().layer.clone() + self.core.read().layer.clone() } /// Returns the number of edges (vertices) of the current shape, if set. pub fn get_num_edges(&self) -> usize { self.core - .borrow() + .read() .shape .as_ref() .map(|s| s.num_edges()) @@ -231,7 +229,7 @@ impl ModDef { /// Sets the track definitions for this module. pub fn set_track_definitions(&self, track_definitions: TrackDefinitions) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); let shape = core .shape .as_ref() @@ -258,21 +256,20 @@ impl ModDef { } /// Returns a shared reference to this module's track definitions, if set. - pub fn get_track_definitions(&self) -> Option> { - Ref::filter_map(self.core.borrow(), |core| core.track_definitions.as_ref()).ok() + pub fn get_track_definitions(&self) -> Option> { + RwLockReadGuard::try_map(self.core.read(), |core| core.track_definitions.as_ref()).ok() } /// Returns a mutable reference to this module's track definitions, if set. - pub fn get_track_definitions_mut(&self) -> Option> { - RefMut::filter_map(self.core.borrow_mut(), |core| { - core.track_definitions.as_mut() - }) - .ok() + pub fn get_track_definitions_mut( + &self, + ) -> Option> { + RwLockWriteGuard::try_map(self.core.write(), |core| core.track_definitions.as_mut()).ok() } /// Looks up the [`TrackDefinition`] for `name`, if one has been registered. pub fn get_track(&self, name: impl AsRef) -> Option { - let core_borrowed = self.core.borrow(); + let core_borrowed = self.core.read(); let track_definitions = &core_borrowed.track_definitions; track_definitions .as_ref() @@ -282,7 +279,7 @@ impl ModDef { /// Returns the polygon edge at `edge_index`, or `None` if the shape is not /// defined or the index is out of bounds. pub fn get_edge(&self, edge_index: usize) -> Option { - let core_borrowed = self.core.borrow(); + let core_borrowed = self.core.read(); let shape = &core_borrowed.shape; shape.as_ref().map(|s| s.get_edge(edge_index)) } @@ -325,7 +322,7 @@ impl ModDef { min_index: i64, max_index: i64, ) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); let occupancies = core .track_occupancies .as_mut() @@ -343,7 +340,7 @@ impl ModDef { min_index: i64, max_index: i64, ) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); let occupancies = core .track_occupancies .as_mut() @@ -364,7 +361,7 @@ impl ModDef { keepout_min_index: i64, keepout_max_index: i64, ) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); let occupancies = core .track_occupancies .as_mut() @@ -405,7 +402,7 @@ impl ModDef { /// families. pub fn get_layers(&self) -> Vec { self.core - .borrow() + .read() .track_definitions .as_ref() .map(|td| td.0.keys().cloned().collect()) @@ -419,7 +416,7 @@ impl ModDef { layer: impl AsRef, ) -> Option { self.core - .borrow() + .read() .track_occupancies .as_ref() .and_then(|occupancies| { diff --git a/src/mod_def/core.rs b/src/mod_def/core.rs index 81ea6af..6f8d592 100644 --- a/src/mod_def/core.rs +++ b/src/mod_def/core.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; use std::ops::RangeFrom; -use std::rc::Rc; +use std::sync::Arc; use indexmap::IndexMap; @@ -25,14 +25,14 @@ pub struct ModDefCore { pub(crate) name: String, pub(crate) ports: IndexMap, pub(crate) interfaces: IndexMap>, - pub(crate) instances: IndexMap>>, + pub(crate) instances: IndexMap>>, pub(crate) usage: Usage, pub(crate) verilog_import: Option, /// Parameter overrides applied to this module definition (by name) pub(crate) parameters: IndexMap, pub(crate) mod_inst_connections: - IndexMap>>>, - pub(crate) mod_def_connections: IndexMap>>, + IndexMap>>>, + pub(crate) mod_def_connections: IndexMap>>, pub(crate) enum_ports: IndexMap, pub(crate) mod_def_metadata: Metadata, pub(crate) mod_def_port_metadata: HashMap, diff --git a/src/mod_def/emit.rs b/src/mod_def/emit.rs index 3f845f7..c820401 100644 --- a/src/mod_def/emit.rs +++ b/src/mod_def/emit.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::path::Path; -use std::rc::Rc; +use std::sync::Arc; use indexmap::IndexMap; use indexmap::map::Entry; @@ -166,10 +166,10 @@ impl ModDef { fn emit_recursive( &self, - emitted_module_names: &mut IndexMap>>, + emitted_module_names: &mut IndexMap>>, file: &mut VastFile, ) { - let core = self.core.borrow(); + let core = self.core.read(); if core.usage == Usage::EmitNothingAndStop || core.usage == Usage::EmitDefinitionAndStop { return; @@ -178,7 +178,7 @@ impl ModDef { match emitted_module_names.entry(core.name.clone()) { Entry::Occupied(entry) => { let existing_moddef = entry.get(); - if !Rc::ptr_eq(existing_moddef, &self.core) { + if !Arc::ptr_eq(existing_moddef, &self.core) { panic!("Two distinct modules with the same name: {}", core.name); } else { return; @@ -216,24 +216,24 @@ impl ModDef { // Create module instances for (inst_name, inst) in core.instances.iter() { - let core_borrowed = self.core.borrow(); + let core_borrowed = self.core.read(); let empty_connections = IndexMap::new(); let mod_inst_connections = match core_borrowed.mod_inst_connections.get(inst_name) { Some(mod_inst_connections) => mod_inst_connections, None => &empty_connections, }; - let module_name = inst.borrow().name.clone(); + let module_name = inst.read().name.clone(); let mut parameter_port_names: Vec = Vec::new(); let mut parameter_expr_vals: Vec = Vec::new(); let mut connection_port_names = Vec::new(); let mut connection_expressions = Vec::new(); - for (port_name, io) in inst.borrow().ports.iter() { + for (port_name, io) in inst.read().ports.iter() { connection_port_names.push(port_name.clone()); let enum_t = inst - .borrow() + .read() .enum_ports .get(port_name) .map(|enum_t| file.make_extern_type(enum_t)); @@ -246,10 +246,8 @@ impl ModDef { }; // break into non-overlapping chunks - let mut non_overlapping = port_slice_connections - .borrow() - .trace() - .make_non_overlapping(); + let mut non_overlapping = + port_slice_connections.read().trace().make_non_overlapping(); non_overlapping.retain(|c| !c.is_empty()); non_overlapping.sort_by_key(|c| -(c[0].this.msb as isize)); @@ -305,8 +303,8 @@ impl ModDef { } // Build parameter override expressions, if any - if !inst.borrow().parameters.is_empty() { - let param_core = inst.borrow(); + if !inst.read().parameters.is_empty() { + let param_core = inst.read(); for (param_name, spec) in param_core.parameters.iter() { parameter_port_names.push(param_name.clone()); if spec.value.sign() == num_bigint::Sign::Minus { @@ -343,17 +341,14 @@ impl ModDef { // Emit assign statements for ModDef ports if necessary for port_name in core.ports.keys() { - let core_borrowed = self.core.borrow(); + let core_borrowed = self.core.read(); let port_slice_connections = match core_borrowed.mod_def_connections.get(port_name) { Some(port_slice_connections) => port_slice_connections, None => panic!("{}.{} is unconnected", core.name, port_name), }; // break into non-overlapping chunks - let mut non_overlapping = port_slice_connections - .borrow() - .trace() - .make_non_overlapping(); + let mut non_overlapping = port_slice_connections.read().trace().make_non_overlapping(); non_overlapping.retain(|c| !c.is_empty()); non_overlapping.sort_by_key(|c| -(c[0].this.msb as isize)); diff --git a/src/mod_def/instances.rs b/src/mod_def/instances.rs index b270c49..93ff947 100644 --- a/src/mod_def/instances.rs +++ b/src/mod_def/instances.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 use itertools::Itertools; -use std::rc::Rc; +use std::sync::Arc; use crate::{ModDef, ModInst, mod_inst::HierPathElem}; @@ -9,12 +9,12 @@ impl ModDef { /// Returns a vector of all module instances within this module definition. pub fn get_instances(&self) -> Vec { self.core - .borrow() + .read() .instances .keys() .map(|name| ModInst { hierarchy: vec![HierPathElem { - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), inst_name: name.clone(), }], }) @@ -24,11 +24,11 @@ impl ModDef { /// Returns the module instance within this module definition with the given /// name; panics if an instance with that name does not exist. pub fn get_instance(&self, name: impl AsRef) -> ModInst { - let inner = self.core.borrow(); + let inner = self.core.read(); if inner.instances.contains_key(name.as_ref()) { ModInst { hierarchy: vec![HierPathElem { - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), inst_name: name.as_ref().to_string(), }], } @@ -57,10 +57,10 @@ impl ModDef { name: Option<&str>, autoconnect: Option<&[&str]>, ) -> ModInst { - if Rc::ptr_eq(&self.core, &moddef.core) { + if Arc::ptr_eq(&self.core, &moddef.core) { panic!( "Cannot instantiate a module within itself: {}", - self.core.borrow().name + self.core.read().name ); } @@ -68,19 +68,19 @@ impl ModDef { let name = if let Some(name) = name { name } else { - name_default = format!("{}_i", moddef.core.borrow().name); + name_default = format!("{}_i", moddef.core.read().name); name_default.as_str() }; if self.frozen() { panic!( "Module {} is frozen. wrap() first if modifications are needed.", - self.core.borrow().name + self.core.read().name ); } { - let mut inner = self.core.borrow_mut(); + let mut inner = self.core.write(); if inner.instances.contains_key(name) { panic!("Instance {}.{} already exists", inner.name, name); } @@ -92,7 +92,7 @@ impl ModDef { // Create the ModInst let inst = ModInst { hierarchy: vec![HierPathElem { - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), inst_name: name.to_string(), }], }; @@ -101,9 +101,9 @@ impl ModDef { if let Some(port_names) = autoconnect { for &port_name in port_names { // Check if the instantiated module has this port - if let Some(io) = moddef.core.borrow().ports.get(port_name) { + if let Some(io) = moddef.core.read().ports.get(port_name) { { - let mut inner = self.core.borrow_mut(); + let mut inner = self.core.write(); if !inner.ports.contains_key(port_name) { inner.ports.insert(port_name.to_string(), io.clone()); } @@ -181,7 +181,7 @@ impl ModDef { } } None => { - let moddef_name = &moddef.core.borrow().name; + let moddef_name = &moddef.core.read().name; if indices_str.is_empty() { format!("{moddef_name}_i") } else { diff --git a/src/mod_def/intf.rs b/src/mod_def/intf.rs index e426562..16b48fb 100644 --- a/src/mod_def/intf.rs +++ b/src/mod_def/intf.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; use regex::Regex; -use std::rc::Rc; +use std::sync::Arc; use crate::{Intf, ModDef}; @@ -18,7 +18,7 @@ impl ModDef { name: impl AsRef, mapping: IndexMap, ) -> Intf { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); if core.interfaces.contains_key(name.as_ref()) { panic!( "Interface {} already exists in module {}", @@ -29,7 +29,7 @@ impl ModDef { core.interfaces.insert(name.as_ref().to_string(), mapping); Intf::ModDef { name: name.as_ref().to_string(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), } } @@ -72,7 +72,7 @@ impl ModDef { ) -> Intf { let mut mapping = IndexMap::new(); { - let core = self.core.borrow(); + let core = self.core.read(); for port_name in core.ports.keys() { for prefix in prefixes { if port_name.starts_with(prefix) { @@ -128,7 +128,7 @@ impl ModDef { }) .collect::>(); { - let core = self.core.borrow(); + let core = self.core.read(); for port_name in core.ports.keys() { for (regex, replace) in ®exes { if regex.is_match(port_name) { @@ -162,11 +162,11 @@ impl ModDef { /// Returns the interface with the given name; panics if an interface with /// that name does not exist. pub fn get_intf(&self, name: impl AsRef) -> Intf { - let core = self.core.borrow(); + let core = self.core.read(); if core.interfaces.contains_key(name.as_ref()) { Intf::ModDef { name: name.as_ref().to_string(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), } } else { panic!( @@ -180,19 +180,19 @@ impl ModDef { /// Returns `true` if this module definition has an interface with the given /// name. pub fn has_intf(&self, name: impl AsRef) -> bool { - self.core.borrow().interfaces.contains_key(name.as_ref()) + self.core.read().interfaces.contains_key(name.as_ref()) } /// Returns a vector of all interfaces on this module definition with the /// given prefix. If `prefix` is `None`, returns all interfaces. pub fn get_intfs(&self, prefix: Option<&str>) -> Vec { - let inner = self.core.borrow(); + let inner = self.core.read(); let mut result = Vec::new(); for name in inner.interfaces.keys() { if prefix.is_none_or(|pfx| name.starts_with(pfx)) { result.push(Intf::ModDef { name: name.clone(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), }); } } diff --git a/src/mod_def/lefdef.rs b/src/mod_def/lefdef.rs index 2b38e6e..9a5f90f 100644 --- a/src/mod_def/lefdef.rs +++ b/src/mod_def/lefdef.rs @@ -139,7 +139,7 @@ impl ModDef { impl ModDef { fn to_lef_component(&self, opts: &LefDefOptions) -> LefComponent { - let core = self.core.borrow(); + let core = self.core.read(); let name = core.name.clone(); let shape = core .shape @@ -235,7 +235,7 @@ impl ModDef { } fn to_def_pins(&self, opts: &LefDefOptions) -> Vec { - let core = self.core.borrow(); + let core = self.core.read(); let (open_char, close_char) = opts.open_close_chars(); diff --git a/src/mod_def/parameterize.rs b/src/mod_def/parameterize.rs index c191dc1..4b1e728 100644 --- a/src/mod_def/parameterize.rs +++ b/src/mod_def/parameterize.rs @@ -2,9 +2,9 @@ use indexmap::IndexMap; use num_bigint::{BigInt, Sign}; -use std::cell::RefCell; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; -use std::rc::Rc; +use std::sync::Arc; use crate::{ IO, ModDef, ModDefCore, ParserConfig, Usage, mod_def::parser_param_to_param, @@ -56,7 +56,7 @@ impl ModDef { /// `_i`; this can be overridden via the optional /// `inst_name` argument. pub fn parameterize + Clone>(&self, parameters: &[(&str, T)]) -> ModDef { - let core = self.core.borrow(); + let core = self.core.read(); let bigint_params: Vec<(&str, BigInt)> = parameters .iter() .map(|(name, val)| (*name, val.clone().into())) @@ -72,7 +72,7 @@ impl ModDef { // Merge parameter overrides with any existing ones let mut merged_parameters: IndexMap = self .core - .borrow() + .read() .parameters .iter() .map(|(k, v)| (k.clone(), v.value.clone())) @@ -205,7 +205,7 @@ impl ModDef { } ModDef { - core: Rc::new(RefCell::new(ModDefCore { + core: Arc::new(RwLock::new(ModDefCore { name: core.name.clone(), ports, enum_ports, diff --git a/src/mod_def/parser.rs b/src/mod_def/parser.rs index 69a4314..77e21a2 100644 --- a/src/mod_def/parser.rs +++ b/src/mod_def/parser.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; use std::path::Path; -use std::rc::Rc; +use std::sync::Arc; use indexmap::IndexMap; @@ -96,7 +96,7 @@ impl ModDef { } ModDef { - core: Rc::new(RefCell::new(ModDefCore { + core: Arc::new(RwLock::new(ModDefCore { name: mod_def_name.to_string(), ports, enum_ports, diff --git a/src/mod_def/pins.rs b/src/mod_def/pins.rs index caa4892..7a47fe9 100644 --- a/src/mod_def/pins.rs +++ b/src/mod_def/pins.rs @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 use indexmap::{IndexMap, map::Entry}; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; -use std::rc::Rc; +use std::sync::Arc; use crate::mod_def::dtypes::{PhysicalPin, Polygon, Range}; use crate::{ConvertibleToPortSlice, ModDef, Port, PortSlice, for_each_edge_direction}; @@ -279,7 +280,7 @@ impl ModDef { /// Creates a scratch copy of the module for speculative placement checks. fn clone_for_pin_placement(&self) -> ModDef { use crate::mod_def::tracks::{TrackOccupancies, TrackOccupancy}; - let core = self.core.borrow(); + let core = self.core.read(); // Deep-copy track occupancies if present let cloned_occupancies: Option = @@ -329,17 +330,17 @@ impl ModDef { }; ModDef { - core: std::rc::Rc::new(std::cell::RefCell::new(new_core)), + core: Arc::new(RwLock::new(new_core)), } } /// Define a physical pin for this single-bit PortSlice, with an arbitrary /// polygon shape relative to `position` on the given `layer`. pub fn place_pin(&self, port_name: impl AsRef, bit: usize, pin: PhysicalPin) { - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); let io = core.ports.get(port_name.as_ref()).unwrap_or_else(|| { panic!( "Port {}.{} does not exist (adding physical pin)", - self.core.borrow().name, + self.core.read().name, port_name.as_ref() ) }); @@ -348,7 +349,7 @@ impl ModDef { panic!( "Bit {} out of range for port {}.{} with width {}", bit, - self.core.borrow().name, + self.core.read().name, port_name.as_ref(), width ); @@ -365,7 +366,7 @@ impl ModDef { /// Returns a list of single-bit port slices that do not have physical pins. pub fn unpinned_port_slices(&self) -> Vec { - let core = self.core.borrow(); + let core = self.core.read(); let mut missing = Vec::new(); for (port_name, io) in core.ports.iter() { @@ -376,7 +377,7 @@ impl ModDef { let port = Port::ModDef { name: port_name.clone(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), }; let pins = if let Some(pins) = core.physical_pins.get(port_name) { @@ -478,7 +479,7 @@ impl ModDef { ) { panic!( "Cannot place pin for {}.{}[{}] on edge {} (layer '{}', track {}): {}", - self.core.borrow().name, + self.core.read().name, port_name.as_ref(), bit, edge_index, @@ -886,7 +887,7 @@ impl ModDef { } pub fn get_physical_pin(&self, port_name: &str, bit: usize) -> PhysicalPin { - let core = self.core.borrow(); + let core = self.core.read(); core.get_physical_pin(port_name, bit) } } diff --git a/src/mod_def/placement.rs b/src/mod_def/placement.rs index 05f7ef5..6a2bf5b 100644 --- a/src/mod_def/placement.rs +++ b/src/mod_def/placement.rs @@ -8,7 +8,7 @@ use crate::{LefDefOptions, ModDef, Usage}; impl ModDef { pub fn bbox(&self) -> Option { - if let Some(shape) = &self.core.borrow().shape { + if let Some(shape) = &self.core.read().shape { Some(shape.bbox()) } else { let mut combined_bbox: Option = None; @@ -19,7 +19,7 @@ impl ModDef { if let Some(mut child_bbox) = child_bbox { let child_mod_inst_name = child.name(); if let Some(placement) = - self.core.borrow().inst_placements.get(child_mod_inst_name) + self.core.read().inst_placements.get(child_mod_inst_name) { child_bbox = child_bbox.apply_transform(&Mat3::from_orientation_then_translation( @@ -104,7 +104,7 @@ impl ModDef { // Instance-local placement matrix: Translation * Orientation let child_m = if let Some(placement) = - self.core.borrow().inst_placements.get(child_mod_inst_name) + self.core.read().inst_placements.get(child_mod_inst_name) { &m_curr * &Mat3::from_orientation_then_translation( @@ -115,7 +115,7 @@ impl ModDef { m_curr }; - match child.get_mod_def().core.borrow().usage { + match child.get_mod_def().core.read().usage { Usage::EmitStubAndStop | Usage::EmitDefinitionAndStop => { // Add placement information for this instance placements.insert( diff --git a/src/mod_def/ports.rs b/src/mod_def/ports.rs index 638fbe2..f69ceb5 100644 --- a/src/mod_def/ports.rs +++ b/src/mod_def/ports.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 use indexmap::map::Entry; -use std::rc::Rc; +use std::sync::Arc; use crate::{IO, ModDef, Port, PortSlice}; @@ -12,11 +12,11 @@ impl ModDef { if self.frozen() { panic!( "Module {} is frozen. wrap() first if modifications are needed.", - self.core.borrow().name + self.core.read().name ); } - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); match core.ports.entry(name.as_ref().to_string()) { Entry::Occupied(_) => { @@ -26,7 +26,7 @@ impl ModDef { entry.insert(io); Port::ModDef { name: name.as_ref().to_string(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), } } } @@ -34,17 +34,17 @@ impl ModDef { /// Returns `true` if this module definition has a port with the given name. pub fn has_port(&self, name: impl AsRef) -> bool { - self.core.borrow().ports.contains_key(name.as_ref()) + self.core.read().ports.contains_key(name.as_ref()) } /// Returns the port on this module definition with the given name; panics /// if a port with that name does not exist. pub fn get_port(&self, name: impl AsRef) -> Port { - let inner = self.core.borrow(); + let inner = self.core.read(); if inner.ports.contains_key(name.as_ref()) { Port::ModDef { name: name.as_ref().to_string(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), } } else { panic!("Port {}.{} does not exist", inner.name, name.as_ref()) @@ -61,13 +61,13 @@ impl ModDef { /// Returns a vector of all ports on this module definition with the given /// prefix. If `prefix` is `None`, returns all ports. pub fn get_ports(&self, prefix: Option<&str>) -> Vec { - let inner = self.core.borrow(); + let inner = self.core.read(); let mut result = Vec::new(); for name in inner.ports.keys() { if prefix.is_none_or(|pfx| name.starts_with(pfx)) { result.push(Port::ModDef { name: name.clone(), - mod_def_core: Rc::downgrade(&self.core), + mod_def_core: Arc::downgrade(&self.core), }); } } diff --git a/src/mod_def/shape.rs b/src/mod_def/shape.rs index 9c66afc..3a7fe47 100644 --- a/src/mod_def/shape.rs +++ b/src/mod_def/shape.rs @@ -6,7 +6,7 @@ impl ModDef { /// Returns `true` when the module shape is a four-vertex rectangle. This /// helper assumes the shape has already been validated as rectilinear. pub fn shape_is_rectangular(&self) -> bool { - let core = self.core.borrow(); + let core = self.core.read(); if let Some(shape) = &core.shape { // Shape is already checked to be rectilinear when it is // added to a ModDef, so we only need to check the number diff --git a/src/mod_def/stub.rs b/src/mod_def/stub.rs index c1d3c40..c3536a1 100644 --- a/src/mod_def/stub.rs +++ b/src/mod_def/stub.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; -use std::rc::Rc; +use std::sync::Arc; use indexmap::IndexMap; use regex::Regex; @@ -14,9 +14,9 @@ impl ModDef { /// ports and interfaces as the original module. The new module has no /// instantiations or internal connections. pub fn stub(&self, name: impl AsRef) -> ModDef { - let core = self.core.borrow(); + let core = self.core.read(); ModDef { - core: Rc::new(RefCell::new(ModDefCore { + core: Arc::new(RwLock::new(ModDefCore { name: name.as_ref().to_string(), ports: core.ports.clone(), // TODO(sherbst): 12/08/2024 should enum_ports be copied when stubbing? @@ -37,8 +37,8 @@ impl ModDef { mod_inst_metadata: HashMap::new(), mod_inst_port_metadata: HashMap::new(), mod_inst_intf_metadata: HashMap::new(), - shape: self.core.borrow().shape.clone(), - layer: self.core.borrow().layer.clone(), + shape: self.core.read().shape.clone(), + layer: self.core.read().layer.clone(), inst_placements: IndexMap::new(), physical_pins: IndexMap::new(), port_max_distances: IndexMap::new(), diff --git a/src/mod_def/tracks.rs b/src/mod_def/tracks.rs index 009ca91..2380c10 100644 --- a/src/mod_def/tracks.rs +++ b/src/mod_def/tracks.rs @@ -572,7 +572,7 @@ impl ModDef { pin_polygon: Option<&Polygon>, keepout_polygon: Option<&Polygon>, ) -> Result<(), PinPlacementError> { - let core = self.core.borrow(); + let core = self.core.read(); let occupancies = core .track_occupancies .as_ref() diff --git a/src/mod_def/wrap.rs b/src/mod_def/wrap.rs index 92c6e54..6f706fb 100644 --- a/src/mod_def/wrap.rs +++ b/src/mod_def/wrap.rs @@ -8,7 +8,7 @@ impl ModDef { /// the same ports as the original module, which are connected directly to /// ports with the same names on the instance of the original module. pub fn wrap(&self, def_name: Option<&str>, inst_name: Option<&str>) -> ModDef { - let original_name = &self.core.borrow().name; + let original_name = &self.core.read().name; let def_name_default; let def_name = if let Some(name) = def_name { @@ -24,8 +24,8 @@ impl ModDef { // Copy interface definitions. { - let original_core = self.core.borrow(); - let mut wrapper_core = wrapper.core.borrow_mut(); + let original_core = self.core.read(); + let mut wrapper_core = wrapper.core.write(); // Copy interface definitions for (intf_name, mapping) in &original_core.interfaces { @@ -37,7 +37,7 @@ impl ModDef { // For each port in the original module, add a corresponding port to the wrapper // and connect them. - for (port_name, io) in self.core.borrow().ports.iter() { + for (port_name, io) in self.core.read().ports.iter() { let wrapper_port = wrapper.add_port(port_name, io.clone()); let inst_port = inst.get_port(port_name); wrapper_port.connect(&inst_port); diff --git a/src/mod_inst.rs b/src/mod_inst.rs index 8ccf11e..6c45755 100644 --- a/src/mod_inst.rs +++ b/src/mod_inst.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 +use parking_lot::RwLock; use std::hash::{Hash, Hasher}; -use std::cell::RefCell; -use std::rc::{Rc, Weak}; +use std::sync::{Arc, Weak}; use num_bigint::BigInt; @@ -17,7 +17,7 @@ use crate::{Coordinate, Mat3, Orientation, PhysicalPin, Placement}; /// ( ... );` in Verilog. #[derive(Clone, Debug)] pub struct HierPathElem { - pub(crate) mod_def_core: Weak>, + pub(crate) mod_def_core: Weak>, pub(crate) inst_name: String, } @@ -25,7 +25,7 @@ impl PartialEq for HierPathElem { fn eq(&self, other: &Self) -> bool { match (self.mod_def_core.upgrade(), other.mod_def_core.upgrade()) { (Some(a_rc), Some(b_rc)) => { - Rc::ptr_eq(&a_rc, &b_rc) && (self.inst_name == other.inst_name) + Arc::ptr_eq(&a_rc, &b_rc) && (self.inst_name == other.inst_name) } _ => false, } @@ -34,12 +34,7 @@ impl PartialEq for HierPathElem { impl Hash for HierPathElem { fn hash(&self, state: &mut H) { - self.mod_def_core - .upgrade() - .unwrap() - .borrow() - .name - .hash(state); + self.mod_def_core.upgrade().unwrap().read().name.hash(state); self.inst_name.hash(state); } } @@ -50,7 +45,7 @@ pub struct ModInst { } impl ModInst { - pub(crate) fn mod_def_core_where_instantiated(&self) -> Rc> { + pub(crate) fn mod_def_core_where_instantiated(&self) -> Arc> { self.hierarchy .last() .expect("ModInst hierarchy cannot be empty") @@ -59,10 +54,10 @@ impl ModInst { .expect("Containing ModDefCore has been dropped") } - pub(crate) fn mod_def_core_of_instance(&self) -> Rc> { + pub(crate) fn mod_def_core_of_instance(&self) -> Arc> { let inst_name = self.name().to_string(); self.mod_def_core_where_instantiated() - .borrow() + .read() .instances .get(&inst_name) .unwrap_or_else(|| panic!("Instance named {} not found", inst_name)) @@ -85,7 +80,7 @@ impl ModInst { ) -> Self { let inst_name = self.name().to_string(); let core_rc = self.mod_def_core_where_instantiated(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); core.mod_inst_metadata .entry(inst_name) .or_default() @@ -96,7 +91,7 @@ impl ModInst { pub fn get_metadata(&self, key: impl AsRef) -> Option { let inst_name = self.name().to_string(); let core_rc = self.mod_def_core_where_instantiated(); - let core = core_rc.borrow(); + let core = core_rc.read(); core.mod_inst_metadata .get(&inst_name) .and_then(|metadata| metadata.get(key.as_ref()).cloned()) @@ -105,7 +100,7 @@ impl ModInst { pub fn clear_metadata(&self, key: impl AsRef) -> Self { let inst_name = self.name().to_string(); let core_rc = self.mod_def_core_where_instantiated(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if let Some(metadata) = core.mod_inst_metadata.get_mut(&inst_name) { metadata.remove(key.as_ref()); if metadata.is_empty() { @@ -184,7 +179,7 @@ impl ModInst { }); let placement = { - let core_borrowed = core.borrow(); + let core_borrowed = core.read(); core_borrowed.inst_placements.get(&frame.inst_name).copied() }; @@ -229,7 +224,7 @@ impl ModInst { /// descend into child instances. pub fn validate_connection_distances(&self) { let self_mod_def = self.get_mod_def(); - let self_mod_def_core_borrowed = self_mod_def.core.borrow(); + let self_mod_def_core_borrowed = self_mod_def.core.read(); let self_transform = self.get_transform(); @@ -311,7 +306,7 @@ impl ModInst { let other_transform = other_mod_inst.get_transform(); let other_port_name = other_port_slice.port.name(); let other_mod_def_core = other_mod_inst.get_mod_def().core; - let other_mod_def_core_borrowed = other_mod_def_core.borrow(); + let other_mod_def_core_borrowed = other_mod_def_core.read(); let Some(other_physical_pin) = other_mod_def_core_borrowed .physical_pins .get(other_port_name) @@ -348,18 +343,18 @@ impl ModInst { /// such interface exists. pub fn get_intf(&self, name: impl AsRef) -> Intf { let mod_def_core = self.mod_def_core_where_instantiated(); - let instances = &mod_def_core.borrow().instances; + let instances = &mod_def_core.read().instances; let inst_core = match instances.get(self.name()) { Some(inst_core) => inst_core.clone(), None => panic!( "Interface '{}' does not exist on module definition '{}'", name.as_ref(), - mod_def_core.borrow().name + mod_def_core.read().name ), }; - let inst_core_borrowed = inst_core.borrow(); + let inst_core_borrowed = inst_core.read(); if inst_core_borrowed.interfaces.contains_key(name.as_ref()) { Intf::ModInst { @@ -419,7 +414,7 @@ impl ModInst { pub(crate) fn debug_string(&self) -> String { let mut parts = Vec::new(); if let Some(frame) = self.hierarchy.first() { - parts.push(frame.mod_def_core.upgrade().unwrap().borrow().name.clone()); + parts.push(frame.mod_def_core.upgrade().unwrap().read().name.clone()); } for frame in &self.hierarchy { parts.push(frame.inst_name.clone()); @@ -441,7 +436,7 @@ impl ModInst { /// Place this instance at a coordinate with an orientation. pub fn place>(&self, coordinate: C, orientation: Orientation) { let core = self.mod_def_core_where_instantiated(); - core.borrow_mut().inst_placements.insert( + core.write().inst_placements.insert( self.name().to_string(), Placement { coordinate: coordinate.into(), @@ -487,7 +482,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "A" ); @@ -504,7 +499,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "A" ); @@ -513,7 +508,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "B" ); @@ -527,7 +522,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "B" ); @@ -556,7 +551,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "Top" ); @@ -565,7 +560,7 @@ mod tests { .mod_def_core .upgrade() .unwrap() - .borrow() + .read() .name, "Mid" ); @@ -653,7 +648,7 @@ mod tests { child_inst.place_pin("x", 0, world_pin); // The stored pin should reside in child-local space. - let core = child.core.borrow(); + let core = child.core.read(); let pins = core.physical_pins.get("x").unwrap(); let stored_pin = pins[0].as_ref().unwrap(); let expected_local = world_coord.apply_transform(&child_inst.get_transform().inverse()); diff --git a/src/pipeline.rs b/src/pipeline.rs index be7848b..f98beba 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -45,7 +45,7 @@ impl PipelineConfig { }, ); - mod_def.core.borrow_mut().parameters = parameters; + mod_def.core.write().parameters = parameters; mod_def.set_usage(Usage::EmitNothingAndStop); @@ -61,7 +61,7 @@ impl ModDef { pub(crate) fn resolve_pipeline_instance_name(&self, pipeline: &PipelineConfig) -> String { if let Some(inst_name) = pipeline.inst_name.as_ref() { // Explicit name provided: validate uniqueness - let core = self.core.borrow(); + let core = self.core.read(); assert!( !core.instances.contains_key(inst_name), "Cannot use pipeline instance name {}, since that instance name is already used in module definition {}.", @@ -71,7 +71,7 @@ impl ModDef { inst_name.clone() } else { // Otherwise generate a unique name using the module-local counter - let mut core = self.core.borrow_mut(); + let mut core = self.core.write(); loop { let name = format!("pipeline_conn_{}", core.pipeline_counter.next().unwrap()); if !core.instances.contains_key(&name) { diff --git a/src/port.rs b/src/port.rs index 70d9c92..4eb82d0 100644 --- a/src/port.rs +++ b/src/port.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::hash::{Hash, Hasher}; -use std::rc::{Rc, Weak}; +use std::sync::{Arc, Weak}; use crate::connection::PortSliceConnections; use crate::io::IO; @@ -41,7 +41,7 @@ impl PortDirectionality { #[derive(Clone, Debug)] pub enum Port { ModDef { - mod_def_core: Weak>, + mod_def_core: Weak>, name: String, }, ModInst { @@ -63,7 +63,7 @@ impl PartialEq for Port { name: b_name, }, ) => match (a_core.upgrade(), b_core.upgrade()) { - (Some(a_rc), Some(b_rc)) => Rc::ptr_eq(&a_rc, &b_rc) && (a_name == b_name), + (Some(a_rc), Some(b_rc)) => Arc::ptr_eq(&a_rc, &b_rc) && (a_name == b_name), _ => false, }, ( @@ -89,7 +89,7 @@ impl Hash for Port { Port::ModDef { name, mod_def_core } => { // Hash pointer identity and name if let Some(rc) = mod_def_core.upgrade() { - Rc::as_ptr(&rc).hash(state); + Arc::as_ptr(&rc).hash(state); } else { (0usize).hash(state); } @@ -102,7 +102,7 @@ impl Hash for Port { // Hash the chain of instance frame pointer identities and names, then port name for frame in hierarchy { if let Some(rc) = frame.mod_def_core.upgrade() { - Rc::as_ptr(&rc).hash(state); + Arc::as_ptr(&rc).hash(state); } else { (0usize).hash(state); } @@ -133,7 +133,7 @@ impl Port { match self { Port::ModDef { .. } => { let core_rc = self.get_mod_def_core_where_declared(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); core.mod_def_port_metadata .entry(self.name().to_string()) .or_default() @@ -145,7 +145,7 @@ impl Port { .expect("Port::ModInst hierarchy cannot be empty") .to_string(); let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); core.mod_inst_port_metadata .entry(inst_name) .or_default() @@ -161,7 +161,7 @@ impl Port { match self { Port::ModDef { .. } => self .get_mod_def_core_where_declared() - .borrow() + .read() .mod_def_port_metadata .get(self.name()) .and_then(|metadata| metadata.get(key.as_ref()).cloned()), @@ -170,7 +170,7 @@ impl Port { .inst_name() .expect("Port::ModInst hierarchy cannot be empty"); self.get_mod_def_core() - .borrow() + .read() .mod_inst_port_metadata .get(inst_name) .and_then(|ports| ports.get(self.name())) @@ -183,7 +183,7 @@ impl Port { match self { Port::ModDef { .. } => { let core_rc = self.get_mod_def_core_where_declared(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if let Some(metadata) = core.mod_def_port_metadata.get_mut(self.name()) { metadata.remove(key.as_ref()); if metadata.is_empty() { @@ -197,7 +197,7 @@ impl Port { .expect("Port::ModInst hierarchy cannot be empty") .to_string(); let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if let Some(ports) = core.mod_inst_port_metadata.get_mut(&inst_name) { if let Some(metadata) = ports.get_mut(self.name()) { metadata.remove(key.as_ref()); @@ -218,7 +218,7 @@ impl Port { pub fn io(&self) -> IO { match self { Port::ModDef { mod_def_core, name } => { - mod_def_core.upgrade().unwrap().borrow().ports[name].clone() + mod_def_core.upgrade().unwrap().read().ports[name].clone() } Port::ModInst { hierarchy, @@ -228,13 +228,9 @@ impl Port { let inst_frame = hierarchy .last() .expect("Port::ModInst hierarchy cannot be empty"); - inst_frame - .mod_def_core - .upgrade() - .unwrap() - .borrow() - .instances[inst_frame.inst_name.as_str()] - .borrow() + inst_frame.mod_def_core.upgrade().unwrap().read().instances + [inst_frame.inst_name.as_str()] + .read() .ports[port_name.as_str()] .clone() } @@ -258,7 +254,7 @@ impl Port { } } - pub(crate) fn get_mod_def_core(&self) -> Rc> { + pub(crate) fn get_mod_def_core(&self) -> Arc> { match self { Port::ModDef { mod_def_core, .. } => mod_def_core.upgrade().unwrap(), Port::ModInst { hierarchy, .. } => hierarchy @@ -276,7 +272,7 @@ impl Port { } } - pub(crate) fn get_mod_def_core_where_declared(&self) -> Rc> { + pub(crate) fn get_mod_def_core_where_declared(&self) -> Arc> { match self { Port::ModDef { mod_def_core, .. } => mod_def_core.upgrade().unwrap(), Port::ModInst { hierarchy, .. } => { @@ -284,7 +280,7 @@ impl Port { last.mod_def_core .upgrade() .unwrap() - .borrow() + .read() .instances .get(last.inst_name.as_str()) .unwrap() @@ -310,16 +306,16 @@ impl Port { /// of `a`). pub(crate) fn as_mod_def_port(&self) -> Port { Port::ModDef { - mod_def_core: Rc::downgrade(&self.get_mod_def_core_where_declared()), + mod_def_core: Arc::downgrade(&self.get_mod_def_core_where_declared()), name: self.name().to_string(), } } pub(crate) fn get_port_connections_define_if_missing( &self, - ) -> Rc> { + ) -> Arc> { let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); match self { Port::ModDef { .. } => core .mod_def_connections @@ -336,9 +332,9 @@ impl Port { } } - pub(crate) fn get_port_connections(&self) -> Option>> { + pub(crate) fn get_port_connections(&self) -> Option>> { let core_rc = self.get_mod_def_core(); - let core = core_rc.borrow(); + let core = core_rc.read(); match self { Port::ModDef { .. } => core .mod_def_connections @@ -379,7 +375,7 @@ impl Port { pub(crate) fn debug_string(&self) -> String { match self { Port::ModDef { name, mod_def_core } => { - format!("{}.{}", mod_def_core.upgrade().unwrap().borrow().name, name) + format!("{}.{}", mod_def_core.upgrade().unwrap().read().name, name) } Port::ModInst { port_name, .. } => { let inst = self diff --git a/src/port_slice.rs b/src/port_slice.rs index 3203b02..edf0e56 100644 --- a/src/port_slice.rs +++ b/src/port_slice.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -use std::cell::RefCell; +use parking_lot::RwLock; use std::fmt::{self, Debug}; -use std::rc::Rc; +use std::sync::Arc; use indexmap::map::Entry; @@ -208,13 +208,13 @@ impl PortSlice { let src_mod_def_core = src_mod_def.core; let dst_mod_def = self.get_mod_def_where_declared(); let dst_mod_def_core = dst_mod_def.core; - if !Rc::ptr_eq(&src_mod_def_core, &dst_mod_def_core) { + if !Arc::ptr_eq(&src_mod_def_core, &dst_mod_def_core) { panic!( "place_across_from requires source and target slices to belong to the same module definition" ); } - let core = src_mod_def_core.borrow(); + let core = src_mod_def_core.read(); let src_pin = core.get_physical_pin(source_slice.port.name(), source_slice.lsb); let src_position = src_pin.translation(); @@ -245,7 +245,7 @@ impl PortSlice { PortSlice { port: Port::ModDef { - mod_def_core: Rc::downgrade(&dst_mod_def_core), + mod_def_core: Arc::downgrade(&dst_mod_def_core), name: self.port.name().to_string(), }, msb: self.msb, @@ -416,7 +416,7 @@ impl PortSlice { format!("{}[{}:{}]", self.port.debug_string(), self.msb, self.lsb) } - pub(crate) fn get_mod_def_core(&self) -> Rc> { + pub(crate) fn get_mod_def_core(&self) -> Arc> { self.port.get_mod_def_core() } @@ -433,7 +433,7 @@ impl PortSlice { pub fn set_max_distance(&self, max_distance: Option) { let port_name = self.port.name().to_string(); let core_rc = self.port.get_mod_def_core_where_declared(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); let core_name = core.name.clone(); let width = core .ports @@ -474,7 +474,7 @@ impl PortSlice { /// any. pub(crate) fn get_port_connections(&self) -> Option { let connections = self.port.get_port_connections()?; - let connections_borrowed = connections.borrow(); + let connections_borrowed = connections.read(); Some(connections_borrowed.slice(self.msb, self.lsb)) } @@ -544,7 +544,7 @@ impl PortSlice { self.port .get_mod_def_core_where_declared() - .borrow() + .read() .physical_pins .get(self.port.name()) .and_then(|pins| pins.get(bit).and_then(|pin| pin.as_ref())) @@ -566,7 +566,7 @@ impl PortSlice { Port::ModDef { mod_def_core, name } => mod_def_core .upgrade() .unwrap() - .borrow() + .read() .get_physical_pin(name, self.lsb), Port::ModInst { .. } => { let mod_inst = self diff --git a/src/port_slice/connect.rs b/src/port_slice/connect.rs index 77e8919..c7512c0 100644 --- a/src/port_slice/connect.rs +++ b/src/port_slice/connect.rs @@ -4,7 +4,7 @@ use crate::port::PortDirectionality; use crate::{ ConvertibleToPortSlice, ConvertibleToPortSliceVec, IO, ModInst, PipelineConfig, Port, PortSlice, }; -use std::rc::Rc; +use std::sync::Arc; impl PortSlice { /// Specifies the net name to be used for this port slice. @@ -14,7 +14,7 @@ impl PortSlice { // within the containing ModDef. { let core_rc = self.get_mod_def_core(); - let mut core = core_rc.borrow_mut(); + let mut core = core_rc.write(); if !core.specified_net_names.insert(net.to_string()) { panic!( "Net \"{}\" has already been manually specified in module {}.", @@ -33,7 +33,7 @@ impl PortSlice { }; self.port .get_port_connections_define_if_missing() - .borrow_mut() + .write() .add(this, other); } else { panic!( @@ -116,7 +116,7 @@ impl PortSlice { ) { let other_as_slice = other.to_port_slice(); - if !Rc::ptr_eq(&self.get_mod_def_core(), &other_as_slice.get_mod_def_core()) { + if !Arc::ptr_eq(&self.get_mod_def_core(), &other_as_slice.get_mod_def_core()) { panic!( "Cannot connect {} and {} because they are in different module definitions", self.debug_string(), @@ -190,12 +190,12 @@ impl PortSlice { } else { self.port .get_port_connections_define_if_missing() - .borrow_mut() + .write() .add(self.clone(), other_as_slice.clone()); other_as_slice .port .get_port_connections_define_if_missing() - .borrow_mut() + .write() .add(other_as_slice.clone(), self.clone()); } } diff --git a/src/port_slice/tieoff.rs b/src/port_slice/tieoff.rs index e433f67..a46079c 100644 --- a/src/port_slice/tieoff.rs +++ b/src/port_slice/tieoff.rs @@ -14,7 +14,7 @@ impl PortSlice { self.port .get_port_connections_define_if_missing() - .borrow_mut() + .write() .add( self.to_port_slice(), Tieoff::new(big_int_value, self.width()), @@ -28,7 +28,7 @@ impl PortSlice { pub fn unused(&self) { self.port .get_port_connections_define_if_missing() - .borrow_mut() + .write() .add(self.to_port_slice(), Unused::new()); } diff --git a/tests/threading.rs b/tests/threading.rs new file mode 100644 index 0000000..389c826 --- /dev/null +++ b/tests/threading.rs @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::{HashMap, VecDeque}; +use std::panic::AssertUnwindSafe; +use std::sync::{Arc, Mutex, mpsc}; +use std::thread; + +use topstitch::*; + +fn parse_jobs_in_pool(sources: Vec<(&str, &str)>, n_workers: usize) -> HashMap { + assert!(n_workers > 0, "n_workers must be at least 1"); + + let owned_sources = sources + .into_iter() + .map(|(module_name, source)| (module_name.to_string(), source.to_string())) + .collect::>(); + + let total = owned_sources.len(); + let queue = Arc::new(Mutex::new(VecDeque::from(owned_sources))); + let (tx, rx) = mpsc::channel::>(); + + let mut handles = Vec::new(); + for _ in 0..n_workers { + let queue = Arc::clone(&queue); + let tx = tx.clone(); + + handles.push(thread::spawn(move || { + loop { + let job = { + let mut q = queue.lock().unwrap(); + q.pop_front() + }; + let Some((module_name, source)) = job else { + break; + }; + + let result = std::panic::catch_unwind(|| { + let source_file = slang_rs::str2tmpfile(&source) + .unwrap_or_else(|err| panic!("failed to create temp source file: {err}")); + let source_path = source_file + .path() + .to_str() + .unwrap_or_else(|| panic!("temp source path is not valid UTF-8")) + .to_string(); + let source_paths = [source_path.as_str()]; + let cfg = ParserConfig { + sources: &source_paths, + ignore_unknown_modules: true, + skip_unsupported: false, + extra_arguments: &["--threads", "1"], + ..Default::default() + }; + ModDef::from_verilog_with_config(&module_name, &cfg) + }) + .map(|md| (module_name.clone(), md)) + .map_err(|_| format!("parse panic in worker for module '{module_name}'")); + + if tx.send(result).is_err() { + break; + } + } + })); + } + drop(tx); + + let mut parsed = HashMap::new(); + for _ in 0..total { + match rx.recv().unwrap() { + Ok((module_name, md)) => { + let existing = parsed.insert(module_name.clone(), md); + assert!( + existing.is_none(), + "duplicate parsed module name '{module_name}'" + ); + } + Err(msg) => panic!("{msg}"), + } + } + + for handle in handles { + handle.join().expect("worker thread panicked unexpectedly"); + } + + parsed +} + +fn emit_jobs_in_pool(mod_defs: Vec<(String, ModDef)>, n_workers: usize) -> HashMap { + assert!(n_workers > 0, "n_workers must be at least 1"); + + let total = mod_defs.len(); + let queue = Arc::new(Mutex::new(VecDeque::from(mod_defs))); + let (tx, rx) = mpsc::channel::>(); + + let mut handles = Vec::new(); + for _ in 0..n_workers { + let queue = Arc::clone(&queue); + let tx = tx.clone(); + + handles.push(thread::spawn(move || { + loop { + let job = { + let mut q = queue.lock().unwrap(); + q.pop_front() + }; + let Some((module_name, mod_def)) = job else { + break; + }; + + let result = std::panic::catch_unwind(AssertUnwindSafe(|| mod_def.emit(true))) + .map(|emitted| (module_name.clone(), emitted)) + .map_err(|_| format!("emit panic in worker for module '{module_name}'")); + + if tx.send(result).is_err() { + break; + } + } + })); + } + drop(tx); + + let mut emitted = HashMap::new(); + for _ in 0..total { + match rx.recv().unwrap() { + Ok((module_name, verilog)) => { + let existing = emitted.insert(module_name.clone(), verilog); + assert!( + existing.is_none(), + "duplicate emitted module name '{module_name}'" + ); + } + Err(msg) => panic!("{msg}"), + } + } + + for handle in handles { + handle.join().expect("worker thread panicked unexpectedly"); + } + + emitted +} + +#[test] +fn test_parallel_parse() { + const LETTERS: &[&str] = &["A", "B", "C", "D"]; + let mut verilog = Vec::new(); + + for letter in LETTERS { + verilog.push(format!( + "module {letter}(input wire [7:0] in, output wire [7:0] out); endmodule" + )); + } + + let parsed = parse_jobs_in_pool( + LETTERS + .iter() + .zip(verilog.iter()) + .map(|(letter, verilog)| (*letter, verilog.as_str())) + .collect(), + 4, + ); + + let top = ModDef::new("Top"); + + for letter in LETTERS { + top.instantiate(&parsed[*letter], None, None) + .unused_and_tieoff(0); + } + + let expected = format!( + "\ +module Top; +{}endmodule +", + LETTERS + .iter() + .map(|letter| { + format!(" {letter} {letter}_i (\n .in(8'h00),\n .out()\n );\n") + }) + .collect::>() + .join("") + ); + + assert_eq!(top.emit(true), expected); +} + +#[test] +fn test_parallel_emit() { + const LETTERS: &[&str] = &["A", "B", "C", "D"]; + let mut mod_defs = Vec::new(); + + for letter in LETTERS { + let mod_def = ModDef::new(letter); + mod_def.add_port("in", IO::Input(8)).unused(); + mod_def.add_port("out", IO::Output(8)).tieoff(0); + mod_defs.push((letter.to_string(), mod_def)); + } + + let emitted = emit_jobs_in_pool(mod_defs, 4); + + let emitted_in_order = LETTERS + .iter() + .map(|letter| emitted[*letter].as_str()) + .collect::>() + .join("\n"); + + let expected_in_order = LETTERS + .iter() + .map(|letter| { + format!( + "\ +module {letter}( + input wire [7:0] in, + output wire [7:0] out +); + assign out = 8'h00; +endmodule +" + ) + }) + .collect::>() + .join("\n"); + + assert_eq!(emitted_in_order, expected_in_order); +}