1use std::collections::{HashMap, HashSet};
4use std::fmt::Formatter;
5
6use arcstr::ArcStr;
7use scir::{Cell, ChildId, Concat, IndexOwned, Instance, LibraryBuilder};
8use serde::{Deserialize, Serialize};
9use substrate::schematic::{ConvertedPrimitive, ScirBinding};
10use uniquify::Names;
11
12use crate::schematic::schema::Schema;
13use crate::schematic::{ConvertPrimitive, InstancePath, RawCellContents, RawCellKind};
14use crate::types::schematic::{Node, NodePath, TerminalPath};
15
16use super::{CellId, InstanceId, RawCell};
17
18pub struct RawLib<S: Schema + ?Sized> {
20 pub scir: scir::Library<S>,
22 pub conv: ScirLibConversion,
26}
27
28impl<S: Schema<Primitive = impl Clone>> Clone for RawLib<S> {
29 fn clone(&self) -> Self {
30 Self {
31 scir: self.scir.clone(),
32 conv: self.conv.clone(),
33 }
34 }
35}
36
37impl<S: Schema<Primitive = impl std::fmt::Debug>> std::fmt::Debug for RawLib<S> {
38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 let mut builder = f.debug_struct("RawLib");
40 let _ = builder.field("scir", &self.scir);
41 let _ = builder.field("conv", &self.conv);
42 builder.finish()
43 }
44}
45
46#[derive(Debug, Clone)]
50pub struct ScirLibConversion {
51 pub(crate) cells: HashMap<CellId, SubstrateCellConversion>,
53 top: Option<CellId>,
55}
56
57impl ScirLibConversion {
58 pub fn corresponding_cell<S: Schema + ?Sized>(
62 &self,
63 cell: &RawCell<S>,
64 ) -> Option<scir::CellId> {
65 let conv = self.cells.get(&cell.id)?;
66 match conv {
67 SubstrateCellConversion::Cell(c) => c.cell_id,
68 SubstrateCellConversion::Primitive(_) => None,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Default)]
74pub(crate) struct ScirLibConversionBuilder {
75 pub(crate) cells: HashMap<CellId, SubstrateCellConversion>,
77 top: Option<CellId>,
79}
80
81#[derive(Debug, Clone)]
83pub enum ConvertedNodePath {
84 Cell(scir::SliceOnePath),
86 Primitive {
88 id: scir::PrimitiveId,
90 instances: scir::InstancePath,
92 port: ArcStr,
94 index: usize,
96 },
97}
98
99impl ScirLibConversionBuilder {
100 fn new() -> Self {
101 Default::default()
102 }
103
104 fn build(self) -> ScirLibConversion {
105 ScirLibConversion {
106 cells: self.cells,
107 top: self.top,
108 }
109 }
110
111 #[inline]
112 pub(crate) fn add_cell(&mut self, id: CellId, conv: impl Into<SubstrateCellConversion>) {
113 self.cells.insert(id, conv.into());
114 }
115}
116
117impl<S: Schema> RawLib<S> {
118 fn convert_instance_path_inner<'a>(
119 &'a self,
120 top: CellId,
121 instances: impl IntoIterator<Item = &'a InstanceId>,
122 ) -> Option<(
123 scir::InstancePath,
124 SubstrateCellConversionRef<&'a ScirCellConversion, &'a ScirPrimitiveConversion>,
125 )> {
126 let mut cell = self.conv.cells.get(&top)?.as_ref();
127 let mut scir_id: scir::ChildId = self.scir.top_cell()?.into();
128
129 let mut scir_instances = scir::InstancePath::new(self.scir.top_cell()?);
130 for inst in instances {
131 let conv = cell.get_cell()?.instances.get(inst).unwrap();
132 match conv.instance.as_ref() {
133 ConvertedScirInstanceContentRef::Cell(id) => {
134 scir_id = self.scir.cell(scir_id.into_cell()?).instance(*id).child();
135 scir_instances.push(*id);
136 if let Some(conv) = self.conv.cells.get(&conv.child) {
137 cell = conv.as_ref();
138 }
139 }
140 ConvertedScirInstanceContentRef::InlineCell(conv) => {
141 cell = SubstrateCellConversionRef::Cell(conv);
142 }
143 }
144 }
145 Some((scir_instances, cell))
146 }
147
148 pub fn convert_node_path(&self, path: &NodePath) -> Option<ConvertedNodePath> {
150 let (instances, cell) = self.convert_instance_path_inner(path.top, &path.instances)?;
151
152 Some(match cell {
153 SubstrateCellConversionRef::Cell(cell) => ConvertedNodePath::Cell(
154 scir::SliceOnePath::new(instances, *cell.signals.get(&path.node)?),
155 ),
156 SubstrateCellConversionRef::Primitive(p) => {
157 let (port, index) = p.ports.get(&path.node)?.first()?;
158 ConvertedNodePath::Primitive {
159 id: p.primitive_id,
160 instances,
161 port: port.clone(),
162 index: *index,
163 }
164 }
165 })
166 }
167
168 pub fn convert_node(&self, node: &Node) -> Option<ConvertedNodePath> {
170 let top = self.conv.top?;
171 let empty = Vec::new();
172 let (instances, cell) = self.convert_instance_path_inner(top, &empty)?;
173
174 Some(match cell {
175 SubstrateCellConversionRef::Cell(cell) => ConvertedNodePath::Cell(
176 scir::SliceOnePath::new(instances, *cell.signals.get(node)?),
177 ),
178 SubstrateCellConversionRef::Primitive(p) => {
179 let (port, index) = p.ports.get(node)?.first()?;
180 ConvertedNodePath::Primitive {
181 id: p.primitive_id,
182 instances,
183 port: port.clone(),
184 index: *index,
185 }
186 }
187 })
188 }
189
190 pub fn convert_instance_path(&self, path: &InstancePath) -> Option<scir::InstancePath> {
192 let (instances, _) = self.convert_instance_path_inner(path.top, &path.path)?;
193 Some(instances)
194 }
195
196 pub fn convert_terminal_path(&self, path: &TerminalPath) -> Option<Vec<ConvertedNodePath>> {
202 let mut cell = self.conv.cells.get(&path.top)?.as_ref();
203
204 let scir_id = self.scir.top_cell()?;
205 let mut instances = scir::InstancePath::new(scir_id);
206 let mut scir_id = ChildId::Cell(scir_id);
207 let mut last_clear = false;
208 for inst in &path.instances {
209 let conv = cell.into_cell()?.instances.get(inst).unwrap();
210 match conv.instance.as_ref() {
211 ConvertedScirInstanceContentRef::Cell(id) => {
212 scir_id = self.scir.cell(scir_id.into_cell()?).instance(*id).child();
213 instances.push(*id);
214 cell = self.conv.cells.get(&conv.child)?.as_ref();
215 last_clear = false;
216 }
217 ConvertedScirInstanceContentRef::InlineCell(conv) => {
218 cell = SubstrateCellConversionRef::Cell(conv);
219 last_clear = true;
220 }
221 }
222 }
223
224 match cell {
225 SubstrateCellConversionRef::Cell(cell) => {
226 let slice = *cell.signals.get(&path.node)?;
232 Some(if last_clear {
233 let mut signals = Vec::new();
234 self.find_connected_terminals(
235 cell,
236 self.scir.cell(scir_id.into_cell()?),
237 slice,
238 &mut instances,
239 &mut signals,
240 );
241 signals
242 } else {
243 vec![ConvertedNodePath::Cell(scir::SliceOnePath::new(
244 instances, slice,
245 ))]
246 })
247 }
248 SubstrateCellConversionRef::Primitive(p) => {
249 let mut out = Vec::new();
250 for (port, index) in p.ports.get(&path.node)? {
251 out.push(ConvertedNodePath::Primitive {
252 id: p.primitive_id,
253 instances: instances.clone(),
254 port: port.clone(),
255 index: *index,
256 });
257 }
258 Some(out)
259 }
260 }
261 }
262
263 fn find_connected_terminals_in_scir_instance(
266 &self,
267 parent_cell: &scir::Cell,
268 id: scir::InstanceId,
269 slice: scir::SliceOne,
270 instances: &mut scir::InstancePath,
271 signals: &mut Vec<ConvertedNodePath>,
272 ) -> Option<()> {
273 instances.push(id);
274 let inst = parent_cell.instance(id);
275 for (name, conn) in inst.connections() {
276 let mut port_index = 0;
277 for part in conn.parts() {
278 if slice.signal() == part.signal() {
279 let concat_index = match (slice.index(), part.range()) {
280 (None, None) => Some(port_index),
281 (Some(index), Some(range)) => {
282 if range.contains(index) {
283 Some(port_index + index - range.start())
284 } else {
285 None
286 }
287 }
288 _ => None,
289 };
290
291 if let Some(concat_index) = concat_index {
292 match inst.child() {
293 ChildId::Cell(id) => {
294 let child_cell = self.scir.cell(id);
295 let port = child_cell.port(name);
296 let port_slice = child_cell.signal(port.signal()).slice();
297 let tail = port_slice
298 .slice_one()
299 .unwrap_or_else(|| port_slice.index(concat_index));
300 signals.push(ConvertedNodePath::Cell(scir::SliceOnePath::new(
301 instances.clone(),
302 tail,
303 )));
304 }
305 ChildId::Primitive(id) => {
306 signals.push(ConvertedNodePath::Primitive {
307 id,
308 instances: instances.clone(),
309 port: name.clone(),
310 index: concat_index,
311 });
312 }
313 }
314 }
315 }
316 port_index += part.width();
317 }
318 }
319 instances.pop().unwrap();
320 Some(())
321 }
322
323 fn find_connected_terminals(
326 &self,
327 conv: &ScirCellConversion,
328 parent_cell: &scir::Cell,
329 slice: scir::SliceOne,
330 instances: &mut scir::InstancePath,
331 signals: &mut Vec<ConvertedNodePath>,
332 ) -> Option<()> {
333 for (_, conv) in conv.instances.iter() {
334 match conv.instance.as_ref() {
335 ConvertedScirInstanceContentRef::Cell(id) => {
336 self.find_connected_terminals_in_scir_instance(
337 parent_cell,
338 *id,
339 slice,
340 instances,
341 signals,
342 );
343 }
344 ConvertedScirInstanceContentRef::InlineCell(conv) => {
345 self.find_connected_terminals(conv, parent_cell, slice, instances, signals);
346 }
347 }
348 }
349 Some(())
350 }
351}
352
353#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
354#[enumify::enumify]
355enum ConvertedScirInstanceContent<U, F> {
356 Cell(U),
357 InlineCell(F),
358}
359
360type ConvertedScirInstance = ConvertedScirInstanceContent<scir::InstanceId, ScirCellConversion>;
362
363#[enumify::enumify]
364#[derive(Debug, Clone)]
365pub(crate) enum SubstrateCellConversion {
366 Cell(ScirCellConversion),
367 Primitive(ScirPrimitiveConversion),
368}
369
370impl From<ScirCellConversion> for SubstrateCellConversion {
371 fn from(value: ScirCellConversion) -> Self {
372 Self::Cell(value)
373 }
374}
375
376impl From<ScirPrimitiveConversion> for SubstrateCellConversion {
377 fn from(value: ScirPrimitiveConversion) -> Self {
378 Self::Primitive(value)
379 }
380}
381
382#[derive(Debug, Default, Clone)]
386pub(crate) struct ScirCellConversion {
387 pub(crate) cell_id: Option<scir::CellId>,
389 pub(crate) signals: HashMap<Node, scir::SliceOne>,
391 pub(crate) instances: HashMap<InstanceId, ScirInstanceConversion>,
393}
394
395#[derive(Debug, Clone)]
399pub(crate) struct ScirPrimitiveConversion {
400 pub(crate) primitive_id: scir::PrimitiveId,
401 pub(crate) ports: HashMap<Node, Vec<(ArcStr, usize)>>,
403}
404
405impl ScirCellConversion {
406 #[inline]
407 fn new() -> Self {
408 Self::default()
409 }
410}
411
412#[derive(Debug, Clone)]
413pub(crate) struct ScirInstanceConversion {
414 child: CellId,
416 instance: ConvertedScirInstance,
421}
422
423pub(crate) struct ScirLibExportContext<S: Schema + ?Sized> {
424 lib: LibraryBuilder<S>,
425 conv: ScirLibConversionBuilder,
426 cell_names: Names<CellId>,
427}
428
429impl<S: Schema + ?Sized> Default for ScirLibExportContext<S> {
430 fn default() -> Self {
431 Self {
432 lib: LibraryBuilder::new(),
433 conv: ScirLibConversionBuilder::new(),
434 cell_names: Names::new(),
435 }
436 }
437}
438
439impl<S: Schema<Primitive = impl Clone> + ?Sized> Clone for ScirLibExportContext<S> {
440 fn clone(&self) -> Self {
441 Self {
442 lib: self.lib.clone(),
443 conv: self.conv.clone(),
444 cell_names: self.cell_names.clone(),
445 }
446 }
447}
448
449impl<S: Schema<Primitive = impl std::fmt::Debug> + ?Sized> std::fmt::Debug
450 for ScirLibExportContext<S>
451{
452 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
453 let mut builder = f.debug_struct("ScirLibExportContext");
454 let _ = builder.field("lib", &self.lib);
455 let _ = builder.field("conv", &self.conv);
456 let _ = builder.field("cell_names", &self.cell_names);
457 builder.finish()
458 }
459}
460
461impl<S: Schema + ?Sized> ScirLibExportContext<S> {
462 fn new() -> Self {
463 Self::default()
464 }
465}
466
467#[derive(Debug, Default, Clone)]
468enum FlatExport {
469 Yes(Vec<scir::SliceOne>),
470 #[default]
471 No,
472}
473
474impl FlatExport {
475 #[inline]
476 pub fn is_yes(&self) -> bool {
477 matches!(self, FlatExport::Yes(_))
478 }
479
480 #[inline]
481 pub fn is_no(&self) -> bool {
482 !self.is_yes()
483 }
484}
485
486struct ScirCellExportContext {
487 inst_idx: u64,
488 cell: scir::Cell,
489}
490
491impl ScirCellExportContext {
492 #[inline]
493 pub fn new(cell: scir::Cell) -> Self {
494 Self { inst_idx: 0, cell }
495 }
496}
497
498impl<S: Schema + ?Sized> RawCell<S> {
499 fn node_name(&self, node: Node) -> String {
505 let node = self.roots[&node];
506 self.node_names[&node].to_string()
507 }
508 pub(crate) fn to_scir_lib(&self) -> Result<RawLib<S>, ConvError> {
515 let mut lib_ctx = ScirLibExportContext::new();
516 let scir_id = self.to_scir_cell(&mut lib_ctx)?;
517
518 if let ChildId::Cell(scir_id) = scir_id {
519 lib_ctx.lib.set_top(scir_id);
520 }
521 lib_ctx.conv.top = Some(self.id);
522
523 Ok(RawLib {
524 scir: lib_ctx.lib.build()?,
525 conv: lib_ctx.conv.build(),
526 })
527 }
528
529 fn to_scir_cell(&self, lib_ctx: &mut ScirLibExportContext<S>) -> Result<ChildId, ConvError> {
532 if let Some(conv) = lib_ctx.conv.cells.get(&self.id) {
533 return match conv {
534 SubstrateCellConversion::Cell(c) => Ok(c.cell_id.unwrap().into()),
535 SubstrateCellConversion::Primitive(p) => Ok(p.primitive_id.into()),
536 };
537 }
538
539 let name = lib_ctx.cell_names.assign_name(self.id, &self.name);
540
541 Ok(match &self.contents {
542 RawCellContents::Cell(_) => {
543 let mut cell_ctx = ScirCellExportContext::new(Cell::new(name));
544 let mut conv =
545 self.export_instances(lib_ctx, &mut cell_ctx, FlatExport::No, None)?;
546 let ScirCellExportContext {
547 cell: scir_cell, ..
548 } = cell_ctx;
549
550 let id = lib_ctx.lib.merge_cell(scir_cell);
551 conv.cell_id = Some(id);
552 lib_ctx.conv.add_cell(self.id, conv);
553
554 id.into()
555 }
556 RawCellContents::Scir(ScirBinding {
557 lib,
558 cell: id,
559 port_map,
560 }) => {
561 let map = lib_ctx.lib.merge_cells((**lib).clone(), [*id]);
562 let id = map.new_cell_id(*id);
563 let mut conv = ScirCellConversion::new();
564 conv.cell_id = Some(id);
565 let cell = lib_ctx.lib.cell(id);
566
567 for port in cell.ports() {
568 let info = cell.signal(port.signal());
569 let nodes = &port_map.get(&info.name).unwrap_or_else(|| {
570 panic!(
571 "port {} not found in SCIR binding for cell {}",
572 info.name,
573 cell.name()
574 )
575 });
576
577 for (i, node) in nodes.iter().enumerate() {
578 conv.signals.insert(
579 *node,
580 if info.width.is_some() {
581 info.slice().index(i)
582 } else {
583 info.slice().slice_one().unwrap()
584 },
585 );
586 }
587 }
588
589 lib_ctx.conv.add_cell(self.id, conv);
590
591 id.into()
592 }
593 RawCellContents::Primitive(p) => {
594 let id = lib_ctx.lib.add_primitive(p.primitive.clone());
595 let mut ports = HashMap::new();
596 for (port, nodes) in &p.port_map {
597 for (i, node) in nodes.iter().enumerate() {
598 ports.entry(*node).or_insert(vec![]).push((port.clone(), i));
599 }
600 }
601 let conv = ScirPrimitiveConversion {
602 primitive_id: id,
603 ports,
604 };
605 lib_ctx.conv.add_cell(self.id, conv);
606
607 id.into()
608 }
609 RawCellContents::ConvertedPrimitive(p) => {
610 let id = lib_ctx.lib.add_primitive(
611 <ConvertedPrimitive<S> as ConvertPrimitive<S>>::convert_primitive(p)
612 .map_err(|_| ConvError::UnsupportedPrimitive)?,
613 );
614 let mut ports = HashMap::new();
615 for (port, nodes) in p.port_map() {
616 for (i, node) in nodes.iter().enumerate() {
617 ports.entry(*node).or_insert(vec![]).push((port.clone(), i));
618 }
619 }
620 let conv = ScirPrimitiveConversion {
621 primitive_id: id,
622 ports,
623 };
624 lib_ctx.conv.add_cell(self.id, conv);
625
626 id.into()
627 }
628 })
629 }
630 fn export_instances(
633 &self,
634 lib_ctx: &mut ScirLibExportContext<S>,
635 cell_ctx: &mut ScirCellExportContext,
636 flatten: FlatExport,
637 name_prefix: Option<&str>,
638 ) -> Result<ScirCellConversion, ConvError> {
639 if flatten.is_yes() {
640 assert!(
641 self.contents.is_cell(),
642 "can only flat-export Substrate cells"
643 );
644 }
645 let mut conv = ScirCellConversion::new();
646 let mut nodes = HashMap::new();
647 let mut roots_added = HashSet::new();
648 let mut names = uniquify::Names::new();
649 let prefix = name_prefix.unwrap_or("");
650
651 if let FlatExport::Yes(ref ports) = flatten {
652 assert_eq!(ports.len(), self.ports.len());
654 for (port, s) in self.ports.iter().zip(ports) {
655 let root = self.roots[&port.node()];
656 roots_added.insert(root);
657 nodes.insert(root, *s);
658 }
659 }
660
661 for (&src, &root) in self.roots.iter() {
662 let s = if !roots_added.contains(&root) {
663 let s = cell_ctx.cell.add_node(self.node_name(root));
664 roots_added.insert(root);
665 nodes.insert(root, s);
666 s
667 } else {
668 nodes[&root]
669 };
670 nodes.insert(src, s);
671 conv.signals.insert(src, s);
672 }
673
674 match self.contents.as_ref() {
675 RawCellKind::Scir(_)
676 | RawCellKind::Primitive(_)
677 | RawCellKind::ConvertedPrimitive(_) => {
678 unreachable!()
682 }
683 RawCellKind::Cell(contents) => {
684 for instance in contents.instances.iter() {
685 let child_id: ChildId = match &instance.child.contents {
686 RawCellContents::Primitive(_) | RawCellContents::ConvertedPrimitive(_) => {
687 instance.child.to_scir_cell(lib_ctx)?
688 }
689 _ => {
690 if instance.child.flatten {
691 let ports = instance.connections.iter().map(|c| nodes[c]).collect();
692 let inst_conv = instance.child.export_instances(
693 lib_ctx,
694 cell_ctx,
695 FlatExport::Yes(ports),
696 Some(&format!("{}{}_", prefix, &*instance.name)),
697 )?;
698 conv.instances.insert(
699 instance.id,
700 ScirInstanceConversion {
701 child: instance.child.id,
702 instance: ConvertedScirInstance::InlineCell(inst_conv),
703 },
704 );
705 continue;
706 } else {
707 instance.child.to_scir_cell(lib_ctx)?
708 }
709 }
710 };
711 let name = names.assign_name((), &instance.name);
712 assert_eq!(
713 name,
714 instance.name,
715 "instance name `{}` in cell `{}` is not unique",
716 instance.name,
717 cell_ctx.cell.name()
718 );
719 let mut sinst =
720 Instance::new(arcstr::format!("{}{}", prefix, instance.name), child_id);
721 cell_ctx.inst_idx += 1;
722
723 assert_eq!(instance.child.ports.len(), instance.connections.len());
724
725 let mut conns = HashMap::new();
726 for (port, &conn) in instance.child.ports.iter().zip(&instance.connections) {
727 conns.insert(port.node(), conn);
728 }
729 let port_map = match &instance.child.contents {
730 RawCellContents::Primitive(p) => p.port_map.clone(),
731 RawCellContents::ConvertedPrimitive(p) => p.port_map().clone(),
732 RawCellContents::Scir(p) => p.port_map().clone(),
733 _ => HashMap::from_iter(instance.child.ports.iter().map(|port| {
734 (
735 instance.child.node_name(port.node()).into(),
736 vec![port.node()],
737 )
738 })),
739 };
740 for (port, port_nodes) in port_map {
741 sinst.connect(
742 port,
743 Concat::from_iter(
744 port_nodes.into_iter().map(|node| nodes[&conns[&node]]),
745 ),
746 );
747 }
748
749 if let RawCellContents::ConvertedPrimitive(p) = &instance.child.contents {
750 <ConvertedPrimitive<S> as ConvertPrimitive<S>>::convert_instance(
751 p, &mut sinst,
752 )
753 .map_err(|_| ConvError::UnsupportedPrimitive)?;
754 }
755
756 let id = cell_ctx.cell.add_instance(sinst);
757 conv.instances.insert(
758 instance.id,
759 ScirInstanceConversion {
760 child: instance.child.id,
761 instance: ConvertedScirInstanceContent::Cell(id),
762 },
763 );
764 }
765 }
766 }
767
768 if flatten.is_no() {
769 for port in self.ports.iter() {
770 cell_ctx
771 .cell
772 .expose_port(nodes[&port.node()], port.direction());
773 }
774 }
775 Ok(conv)
776 }
777}
778
779#[derive(thiserror::Error, Debug, Clone)]
781pub enum ConvError {
782 #[error("error in converted SCIR library")]
784 Scir(#[from] scir::Issues),
785 #[error("unsupported primitive")]
787 UnsupportedPrimitive,
788}
789
790pub(crate) fn export_multi_top_scir_lib<S: Schema + ?Sized>(
796 cells: &[&RawCell<S>],
797) -> Result<RawLib<S>, ConvError> {
798 let mut lib_ctx = ScirLibExportContext::new();
799
800 for &cell in cells {
801 cell.to_scir_cell(&mut lib_ctx)?;
802 }
803
804 Ok(RawLib {
805 scir: lib_ctx.lib.build()?,
806 conv: lib_ctx.conv.build(),
807 })
808}