Skip to main content

virtuoso_cli/client/
maestro_ops.rs

1use crate::client::bridge::escape_skill_string;
2
3pub struct MaestroOps;
4
5impl MaestroOps {
6    /// Open maestro session in background (non-GUI) mode.
7    /// Returns session string like "fnxSession4".
8    pub fn open_session(&self, lib: &str, cell: &str, view: &str) -> String {
9        let lib = escape_skill_string(lib);
10        let cell = escape_skill_string(cell);
11        let view = escape_skill_string(view);
12        format!(r#"maeOpenSetup("{lib}" "{cell}" "{view}")"#)
13    }
14
15    /// Close a maestro session, force-cancelling any in-flight simulation.
16    pub fn close_session(&self, session: &str) -> String {
17        let session = escape_skill_string(session);
18        format!(r#"maeCloseSession("{session}" ?forceClose t)"#)
19    }
20
21    /// List all active maestro sessions.
22    pub fn list_sessions(&self) -> String {
23        r#"let((sessions out sep) sessions = maeGetSessions() out = "[" sep = "" foreach(s sessions out = strcat(out sep sprintf(nil "\"%s\"" s)) sep = ",") strcat(out "]"))"#.into()
24    }
25
26    /// Set a design variable value.
27    /// maeSetVar(name value ?typeName "test"|"corner" ?typeValue ?session)
28    pub fn set_var(&self, _session: &str, name: &str, value: &str) -> String {
29        let name = escape_skill_string(name);
30        let value = escape_skill_string(value);
31        format!(r#"maeSetVar("{name}" "{value}")"#)
32    }
33
34    /// Get a design variable value.
35    pub fn get_var(&self, name: &str) -> String {
36        let name = escape_skill_string(name);
37        format!(r#"maeGetVar("{name}")"#)
38    }
39
40    /// List all design variables. Returns JSON via sprintf.
41    pub fn list_vars(&self, _session: &str) -> String {
42        r#"let((vars out sep) vars = asiGetDesignVarList(asiGetCurrentSession()) out = "[" sep = "" foreach(v vars out = strcat(out sep sprintf(nil "{\"name\":\"%s\",\"value\":\"%s\"}" car(v) cadr(v))) sep = ",") strcat(out "]"))"#.to_string()
43    }
44
45    /// Get enabled analyses for a session (resolves setup name internally).
46    /// maeGetEnabledAnalysis(t_setupName) — takes setup name, not session name.
47    pub fn get_analyses(&self, session: &str) -> String {
48        let session = escape_skill_string(session);
49        format!(
50            r#"let((setup) setup = car(maeGetSetup(?session "{session}")) maeGetEnabledAnalysis(setup))"#
51        )
52    }
53
54    /// Enable an analysis type on a session (resolves setup name internally).
55    /// maeSetAnalysis(t_setupName t_analysisType) — returns t on success.
56    /// analysisType: "ac" | "dc" | "tran" | "noise" | etc.
57    pub fn set_analysis(&self, session: &str, analysis_type: &str) -> String {
58        let session = escape_skill_string(session);
59        let analysis_type = escape_skill_string(analysis_type);
60        format!(
61            r#"let((setup) setup = car(maeGetSetup(?session "{session}")) maeSetAnalysis(setup "{analysis_type}"))"#
62        )
63    }
64
65    /// Run simulation asynchronously. Returns immediately.
66    pub fn run_simulation(&self, session: &str) -> String {
67        let session = escape_skill_string(session);
68        format!(r#"maeRunSimulation(?session "{session}")"#)
69    }
70
71    /// Get test outputs (measurement expressions).
72    /// maeGetTestOutputs(t_testName [?session t_session])
73    pub fn get_outputs(&self, test_name: &str) -> String {
74        let test_name = escape_skill_string(test_name);
75        format!(
76            r#"let((outs out sep) outs = maeGetTestOutputs("{test_name}") out = "[" sep = "" foreach(o outs out = strcat(out sep sprintf(nil "{{\"name\":\"%s\",\"type\":\"%s\"}}" car(o) cadr(o))) sep = ",") strcat(out "]"))"#
77        )
78    }
79
80    /// Add an output expression to the session (resolves setup name internally).
81    /// maeAddOutput(t_outputName t_testName ?expr e)
82    pub fn add_output(&self, session: &str, output_name: &str, expr: &str) -> String {
83        let session = escape_skill_string(session);
84        let output_name = escape_skill_string(output_name);
85        let expr = escape_skill_string(expr);
86        format!(
87            r#"let((setup) setup = car(maeGetSetup(?session "{session}")) maeAddOutput("{output_name}" setup ?expr "{expr}"))"#
88        )
89    }
90
91    /// Set the design target for a test.
92    pub fn set_design(&self, session: &str, lib: &str, cell: &str, view: &str) -> String {
93        let session = escape_skill_string(session);
94        let lib = escape_skill_string(lib);
95        let cell = escape_skill_string(cell);
96        let view = escape_skill_string(view);
97        format!(
98            r#"maeSetDesign(?session "{session}" ?libName "{lib}" ?cellName "{cell}" ?viewName "{view}")"#
99        )
100    }
101
102    /// Save maestro setup to disk.
103    pub fn save_setup(&self, session: &str) -> String {
104        let session = escape_skill_string(session);
105        format!(r#"maeSaveSetup(?session "{session}")"#)
106    }
107
108    /// Get simulation messages (errors/warnings from last run).
109    pub fn get_sim_messages(&self, session: &str) -> String {
110        let session = escape_skill_string(session);
111        format!(r#"maeGetSimulationMessages(?session "{session}")"#)
112    }
113
114    /// Export results to CSV.
115    pub fn export_results(&self, session: &str, file_path: &str) -> String {
116        let session = escape_skill_string(session);
117        let file_path = escape_skill_string(file_path);
118        format!(
119            r#"maeExportOutputView(?session "{session}" ?fileName "{file_path}" ?view "Detail")"#
120        )
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    fn ops() -> MaestroOps {
129        MaestroOps
130    }
131
132    #[test]
133    fn open_session_quoting() {
134        let s = ops().open_session("myLib", "myCell", "adexl");
135        assert_eq!(s, r#"maeOpenSetup("myLib" "myCell" "adexl")"#);
136    }
137
138    #[test]
139    fn open_session_escapes_quotes() {
140        let s = ops().open_session(r#"lib"x"#, "cell", "adexl");
141        assert!(s.contains(r#"lib\"x"#), "{s}");
142    }
143
144    #[test]
145    fn set_var_format() {
146        let s = ops().set_var("sess1", "Vdd", "1.8");
147        assert_eq!(s, r#"maeSetVar("Vdd" "1.8")"#);
148    }
149
150    #[test]
151    fn run_simulation_includes_session() {
152        let s = ops().run_simulation("sess1");
153        assert!(s.contains("maeRunSimulation"), "{s}");
154        assert!(s.contains("\"sess1\""), "{s}");
155    }
156
157    #[test]
158    fn set_analysis_resolves_setup() {
159        let s = ops().set_analysis("sess1", "ac");
160        assert!(s.contains("maeGetSetup"), "must resolve setup: {s}");
161        assert!(s.contains("maeSetAnalysis"), "{s}");
162        assert!(s.contains("\"ac\""), "{s}");
163    }
164
165    #[test]
166    fn add_output_includes_expr() {
167        let s = ops().add_output("sess1", "gain", "getData(\"vout\")");
168        assert!(s.contains("maeAddOutput"), "{s}");
169        assert!(s.contains("\"gain\""), "{s}");
170    }
171}