Skip to main content

virtuoso_cli/client/
schematic_ops.rs

1use crate::client::bridge::escape_skill_string;
2
3#[derive(Default)]
4pub struct SchematicOps;
5
6impl SchematicOps {
7    pub fn new() -> Self {
8        Self
9    }
10
11    pub fn create_instance(
12        &self,
13        lib: &str,
14        cell: &str,
15        view: &str,
16        name: &str,
17        origin: (i64, i64),
18    ) -> String {
19        let lib = escape_skill_string(lib);
20        let cell = escape_skill_string(cell);
21        let view = escape_skill_string(view);
22        let name = escape_skill_string(name);
23        let (x, y) = origin;
24        format!(
25            r#"let((cv master inst) cv = RB_SCH_CV master = dbOpenCellViewByType("{lib}" "{cell}" "{view}" nil "r") inst = dbCreateInst(cv master "{name}" list({x} {y}) "R0" 1) inst)"#
26        )
27    }
28
29    pub fn create_wire(&self, points: &[(i64, i64)], layer: &str, net_name: &str) -> String {
30        let layer = escape_skill_string(layer);
31        let net_name = escape_skill_string(net_name);
32        let pts: String = points
33            .iter()
34            .map(|(x, y)| format!("list({x} {y})"))
35            .collect::<Vec<_>>()
36            .join(" ");
37        format!(
38            r#"let((cv) cv = RB_SCH_CV dbCreateWire(cv dbMakeNet(cv "{net_name}") dbFindLayerByName(cv "{layer}") list({pts})))"#
39        )
40    }
41
42    pub fn create_wire_between_terms(
43        &self,
44        inst1: &str,
45        term1: &str,
46        inst2: &str,
47        term2: &str,
48        net_name: &str,
49    ) -> String {
50        let inst1 = escape_skill_string(inst1);
51        let inst2 = escape_skill_string(inst2);
52        let net_name = escape_skill_string(net_name);
53        format!(
54            r#"let((cv net) cv = RB_SCH_CV net = dbMakeNet(cv "{net_name}") dbCreateWire(net dbFindTermByName(cv "{inst1}") dbFindTermByName(cv "{inst2}")))"#
55        )
56    }
57
58    pub fn create_wire_label(&self, net_name: &str, origin: (i64, i64)) -> String {
59        let net_name = escape_skill_string(net_name);
60        let (x, y) = origin;
61        format!(
62            r#"let((cv net) cv = RB_SCH_CV net = dbFindNetByName(cv "{net_name}") when(net dbCreateLabel(cv net "{net_name}" list({x} {y}) "centerCenter" "R0" "stick" 0.0625)))"#
63        )
64    }
65
66    pub fn create_pin(&self, net_name: &str, pin_type: &str, origin: (i64, i64)) -> String {
67        let net_name = escape_skill_string(net_name);
68        let (x, y) = origin;
69        format!(
70            r#"let((cv net pinInst) cv = RB_SCH_CV net = dbMakeNet(cv "{net_name}") pinInst = dbCreateInst(cv dbOpenCellViewByType("basic" "ipin" "symbol" nil "r") "PIN_{net_name}" list({x} {y}) "R0" 1) dbCreatePin(net pinInst))"#
71        )
72    }
73
74    pub fn check(&self) -> String {
75        r#"let((cv) cv = RB_SCH_CV schCheck(cv))"#.into()
76    }
77
78    pub fn open_cellview(&self, lib: &str, cell: &str, view: &str) -> String {
79        let lib = escape_skill_string(lib);
80        let cell = escape_skill_string(cell);
81        let view = escape_skill_string(view);
82        // dbOpenCellViewByType with viewType="schematic" mode="a":
83        //   creates cellview if absent, opens for editing (non-interactive)
84        // Store in RB_SCH_CV global for use by subsequent commands
85        format!(
86            r#"RB_SCH_CV = dbOpenCellViewByType("{lib}" "{cell}" "{view}" "schematic" "a")"#
87        )
88    }
89
90    pub fn save(&self) -> String {
91        r#"let((cv) cv = RB_SCH_CV dbSave(cv))"#.into()
92    }
93
94    pub fn set_instance_param(&self, inst_name: &str, param: &str, value: &str) -> String {
95        let inst_name = escape_skill_string(inst_name);
96        let param = escape_skill_string(param);
97        let value = escape_skill_string(value);
98        format!(
99            r#"let((cv inst) cv = RB_SCH_CV inst = car(setof(i cv~>instances i~>name == "{inst_name}")) when(inst dbReplaceProp(inst "{param}" "string" "{value}")))"#
100        )
101    }
102
103    /// Generate SKILL script that assigns net names to instance terminals.
104    /// Returns a complete SKILL script string to write to a temp file and load.
105    pub fn generate_connection_script(
106        &self,
107        connections: &[(String, String, String)], // (inst, term, net)
108    ) -> String {
109        let mut lines = Vec::new();
110        lines.push("let((cv inst iterm net)".to_string());
111        lines.push("cv = RB_SCH_CV".to_string());
112        for (inst_name, term_name, net_name) in connections {
113            let inst_name = escape_skill_string(inst_name);
114            let term_name = escape_skill_string(term_name);
115            let net_name = escape_skill_string(net_name);
116            lines.push(format!(
117                r#"inst = car(setof(i cv~>instances strcmp(i~>name "{inst_name}")==0))"#
118            ));
119            lines.push(format!(
120                r#"iterm = car(setof(x inst~>instTerms strcmp(x~>name "{term_name}")==0))"#
121            ));
122            lines.push(format!(
123                r#"net = dbMakeNet(cv "{net_name}")"#
124            ));
125            // Create a wire at the instTerm position to connect it
126            lines.push(r#"when(iterm schCreateWire(cv net "draw" "full" list(list(0 0) list(0 0))))"#.to_string());
127        }
128        lines.push("t)".to_string());
129        lines.join("\n")
130    }
131
132    /// Assign net name to instance terminal — simplified version.
133    /// Creates a named net and connects it to the instTerm.
134    pub fn assign_net(&self, inst_name: &str, term_name: &str, net_name: &str) -> String {
135        let inst_name = escape_skill_string(inst_name);
136        let term_name = escape_skill_string(term_name);
137        let net_name = escape_skill_string(net_name);
138        format!(
139            r#"RB_INST = car(setof(i RB_SCH_CV~>instances strcmp(i~>name "{inst_name}")==0)) RB_ITERM = car(setof(x RB_INST~>instTerms strcmp(x~>name "{term_name}")==0)) RB_NET = dbMakeNet(RB_SCH_CV "{net_name}") schCreateWire(RB_SCH_CV RB_NET "draw" "full" list(list(0 0) list(0 0)))"#
140        )
141    }
142}