1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Redstone EDA: the minimal `routing`-feature bridge surface.
//!
//! New surface — no old `ffi/` counterpart. Thin wrappers over
//! `crate::routing` (which itself re-exports `nucleation-routing`): route a
//! net through a schematic, route many with negotiated congestion and
//! per-net-class rules (`route_all`), run DRC, run LVS, run the STA upper
//! bound. Structured results cross as JSON strings (PORTING.md rule 9).
//! The full Workspace / cell-template API stays native-only until the
//! compositor needs it bridged.
//!
//! Bindings are regenerated (`tools/gen-bindings.sh`); the module compiles
//! under `--features bridge-full,routing`.
#[diplomat::bridge]
pub mod ffi {
use super::super::schematic::ffi::Schematic;
use super::super::shared::ffi::NucleationError;
use diplomat_runtime::DiplomatWrite;
use std::fmt::Write;
/// Namespacing opaque for routing entry points (static methods taking
/// `&Schematic` explicitly, like `Autostack`).
#[diplomat::opaque]
pub struct Routing;
impl Routing {
/// Route one net from `(sx, sy, sz)` to `(dx, dy, dz)` with default
/// rules (torch-ladder vias, stair cap 4, refresh 5) and write the
/// emitted geometry into the schematic. Writes the routed path as a
/// JSON array of `[x, y, z]` cells.
#[allow(clippy::too_many_arguments)]
pub fn route_net(
schematic: &mut Schematic,
sx: i32,
sy: i32,
sz: i32,
dx: i32,
dy: i32,
dz: i32,
label: &DiplomatStr,
out: &mut DiplomatWrite,
) -> Result<(), NucleationError> {
let label =
core::str::from_utf8(label).map_err(|_| NucleationError::InvalidArgument)?;
let res =
crate::routing::route_net(&mut schematic.0, (sx, sy, sz), (dx, dy, dz), label)
.map_err(|_| NucleationError::InvalidArgument)?;
let cells: Vec<String> = res
.path
.iter()
.map(|p| format!("[{},{},{}]", p.x, p.y, p.z))
.collect();
let _ = write!(out, "[{}]", cells.join(","));
Ok(())
}
/// Route every net in `nets_json` with negotiated congestion
/// (pnr-core PathFinder) in one labelled workspace, write the
/// geometry into the schematic, and write the JSON report
/// (`routes` with per-net `path`/`delay_rt`, `notes`,
/// `violations`). Supports per-net-class rule overrides
/// (`classes`: io_contract `NetClassRule`s, with `region`
/// resolving named route zones tagged on the schematic's
/// DefinitionRegions), plus `bounds`, `budget` and `congestion`
/// options — see `crate::routing::route_all_schematic` for the
/// exact request shape.
pub fn route_all(
schematic: &mut Schematic,
nets_json: &DiplomatStr,
out: &mut DiplomatWrite,
) -> Result<(), NucleationError> {
let json =
core::str::from_utf8(nets_json).map_err(|_| NucleationError::InvalidArgument)?;
let report =
crate::routing::route_all_schematic(&mut schematic.0, json).map_err(|e| {
crate::bridge::set_last_error_detail(e);
NucleationError::InvalidArgument
})?;
let _ = write!(out, "{report}");
Ok(())
}
/// LVS v1: compare an intended netlist (`{"nets": [{"name",
/// "terminals": [[x,y,z], ...]}]}`) against the conduction
/// netlist extracted statically from the schematic (dust
/// adjacency incl. cut diagonals plus repeater/comparator/torch
/// through-component edges). Writes `{"clean", "matched",
/// "opens", "shorts", "cycles"}`.
pub fn lvs(
schematic: &Schematic,
intent_json: &DiplomatStr,
out: &mut DiplomatWrite,
) -> Result<(), NucleationError> {
let json =
core::str::from_utf8(intent_json).map_err(|_| NucleationError::InvalidArgument)?;
let report = crate::routing::lvs_schematic(&schematic.0, json).map_err(|e| {
crate::bridge::set_last_error_detail(e);
NucleationError::InvalidArgument
})?;
let _ = write!(out, "{}", crate::routing::lvs_report_json(&report));
Ok(())
}
/// Run design-rule checks (support audit, repeater-cycle detection,
/// optional decay) over the schematic. Writes a JSON array; each
/// element has `kind` plus violation-specific fields. Label-aware
/// short checking needs a labelled workspace and stays native.
pub fn drc(
schematic: &Schematic,
check_decay: bool,
out: &mut DiplomatWrite,
) -> Result<(), NucleationError> {
use crate::routing::Violation;
let opts = crate::routing::DrcOptions {
aliases: vec![],
skip_decay: !check_decay,
};
let vs = crate::routing::drc_schematic(&schematic.0, &opts);
let items: Vec<String> = vs
.iter()
.map(|v| match v {
Violation::Short {
label_a,
label_b,
at_a,
at_b,
} => format!(
"{{\"kind\":\"short\",\"label_a\":{label_a:?},\"label_b\":{label_b:?},\"at_a\":[{},{},{}],\"at_b\":[{},{},{}]}}",
at_a.x, at_a.y, at_a.z, at_b.x, at_b.y, at_b.z
),
Violation::Floating { at, block } => format!(
"{{\"kind\":\"floating\",\"at\":[{},{},{}],\"block\":{block:?}}}",
at.x, at.y, at.z
),
Violation::UnattachedWallTorch { at, anchor } => format!(
"{{\"kind\":\"unattached_wall_torch\",\"at\":[{},{},{}],\"anchor\":[{},{},{}]}}",
at.x, at.y, at.z, anchor.x, anchor.y, anchor.z
),
Violation::RepeaterCycle { diodes } => {
let ds: Vec<String> = diodes
.iter()
.map(|p| format!("[{},{},{}]", p.x, p.y, p.z))
.collect();
format!("{{\"kind\":\"repeater_cycle\",\"diodes\":[{}]}}", ds.join(","))
}
Violation::PowerStarved { at, distance } => format!(
"{{\"kind\":\"power_starved\",\"at\":[{},{},{}],\"distance\":{distance}}}",
at.x, at.y, at.z
),
})
.collect();
let _ = write!(out, "[{}]", items.join(","));
Ok(())
}
/// Static timing over the schematic plus a gate netlist given as
/// JSON: `{"inputs": ["a", ...], "gates": [{"out": "y",
/// "ins": ["a", "b"], "delay_rt": 2}, ...]}`. Writes
/// `{"arrival_rt": {sig: rt}, "critical": [sig, ...]}`.
pub fn sta(
schematic: &Schematic,
netlist_json: &DiplomatStr,
out: &mut DiplomatWrite,
) -> Result<(), NucleationError> {
let json =
core::str::from_utf8(netlist_json).map_err(|_| NucleationError::InvalidArgument)?;
let parsed: serde_json::Value =
serde_json::from_str(json).map_err(|_| NucleationError::Parse)?;
let inputs: Vec<String> = parsed["inputs"]
.as_array()
.ok_or(NucleationError::InvalidArgument)?
.iter()
.filter_map(|v| v.as_str().map(str::to_string))
.collect();
let gates: Vec<crate::routing::sta::Gate> = parsed["gates"]
.as_array()
.ok_or(NucleationError::InvalidArgument)?
.iter()
.map(|g| {
Some(crate::routing::sta::Gate {
out: g["out"].as_str()?.to_string(),
ins: g["ins"]
.as_array()?
.iter()
.filter_map(|v| v.as_str().map(str::to_string))
.collect(),
delay_rt: g["delay_rt"].as_u64().unwrap_or(1) as u32,
})
})
.collect::<Option<Vec<_>>>()
.ok_or(NucleationError::InvalidArgument)?;
let report = crate::routing::sta_schematic(&schematic.0, &inputs, &gates)
.map_err(|_| NucleationError::InvalidArgument)?;
let arrivals: Vec<String> = report
.arrival_rt
.iter()
.map(|(k, v)| format!("{k:?}:{v}"))
.collect();
let critical: Vec<String> = report.critical.iter().map(|s| format!("{s:?}")).collect();
let _ = write!(
out,
"{{\"arrival_rt\":{{{}}},\"critical\":[{}]}}",
arrivals.join(","),
critical.join(",")
);
Ok(())
}
}
}