1use std::collections::HashMap;
6use std::fmt::{Display, Formatter};
7
8use diagnostics::{Diagnostic, IssueSet, Severity};
9
10use super::*;
11
12#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
14pub struct Net {
15 cell_name: ArcStr,
17 signal_name: ArcStr,
19 idx: Option<usize>,
21}
22
23impl Display for Net {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}/{}", self.cell_name, self.signal_name)?;
26 if let Some(idx) = self.idx {
27 write!(f, "[{idx}]")?;
28 }
29 Ok(())
30 }
31}
32
33#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
35pub struct DriverIssue {
36 cause: Cause,
37 severity: Severity,
38 net: Net,
39}
40
41#[derive(Debug, Clone, Default)]
43struct NetState {
44 drivers: usize,
50 taps: usize,
56 inouts: usize,
60}
61
62impl NetState {
63 #[inline]
65 fn new() -> Self {
66 Self::default()
67 }
68
69 fn degree(&self) -> usize {
71 self.drivers + self.taps + self.inouts
72 }
73
74 fn eff_drivers(&self) -> usize {
78 self.inouts + self.drivers
79 }
80
81 fn validate(&self, net: Net, output: &mut IssueSet<DriverIssue>) {
83 if self.drivers > 1 {
84 output.add(DriverIssue::new_and_log(
85 Cause::MultipleDrivers,
86 net.clone(),
87 Severity::Info,
88 ));
89 }
90
91 if self.taps > 0 && self.inouts + self.drivers == 0 {
92 output.add(DriverIssue::new_and_log(
93 Cause::NoDrivers,
94 net.clone(),
95 Severity::Warning,
96 ));
97 }
98
99 if self.degree() == 0 {
100 output.add(DriverIssue::new_and_log(
101 Cause::Floating,
102 net.clone(),
103 Severity::Warning,
104 ));
105 }
106
107 if self.taps == 0 && self.eff_drivers() == 1 {
108 output.add(DriverIssue::new_and_log(
109 Cause::NotConnected,
110 net.clone(),
111 Severity::Info,
112 ));
113 }
114 }
115}
116
117#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
119pub enum Cause {
120 NotConnected,
124 Floating,
126 MultipleDrivers,
132 NoDrivers,
136}
137
138impl Diagnostic for DriverIssue {
139 fn severity(&self) -> Severity {
140 self.severity
141 }
142}
143
144impl DriverIssue {
145 pub(crate) fn new(cause: Cause, net: Net, severity: Severity) -> Self {
147 Self {
148 cause,
149 net,
150 severity,
151 }
152 }
153
154 #[inline]
156 pub fn cause(&self) -> &Cause {
157 &self.cause
158 }
159
160 pub(crate) fn new_and_log(cause: Cause, net: Net, severity: Severity) -> Self {
164 let result = Self::new(cause, net, severity);
165 match severity {
166 Severity::Info => tracing::event!(Level::INFO, issue = ?result.cause, "{}", result),
167 Severity::Warning => tracing::event!(Level::WARN, issue = ?result.cause, "{}", result),
168 Severity::Error => tracing::event!(Level::ERROR, issue = ?result.cause, "{}", result),
169 }
170 result
171 }
172}
173
174impl Display for DriverIssue {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 write!(f, "{}: {}", self.cause, self.net)
177 }
178}
179
180impl Display for Cause {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 match self {
183 Self::Floating => write!(f, "floating net"),
184 Self::MultipleDrivers => write!(f, "multiple drivers on the same net"),
185 Self::NoDrivers => write!(f, "net is used (i.e. read from), but has no drivers"),
186 Self::NotConnected => write!(f, "net is driven but never used elsewhere"),
187 }
188 }
189}
190
191impl<S: Schema + ?Sized> LibraryBuilder<S> {
192 pub fn validate_drivers(&self) -> IssueSet<DriverIssue> {
194 let _guard = span!(Level::INFO, "performing driver analysis on SCIR Library").entered();
195 let mut issues = IssueSet::new();
196 self.validate_drivers_inner(&mut issues);
197 issues
198 }
199
200 fn validate_drivers_inner(&self, issues: &mut IssueSet<DriverIssue>) {
201 for &id in self.cells.keys() {
202 self.validate_cell_drivers(id, issues);
203 }
204 }
205
206 fn validate_cell_drivers(&self, id: CellId, issues: &mut IssueSet<DriverIssue>) {
207 let cell = self.cells.get(&id).unwrap();
208 let _guard =
209 span!(Level::INFO, "validating SCIR cell drivers", cell.id = %id, cell.name = %cell.name)
210 .entered();
211
212 let mut net_states: HashMap<SignalId, Vec<NetState>> =
213 HashMap::from_iter(cell.signals().map(|(id, info)| {
214 let len = info.width.unwrap_or(1);
215 (id, vec![NetState::new(); len])
216 }));
217
218 for port in cell.ports() {
219 for state in net_states.get_mut(&port.signal()).unwrap() {
220 match port.direction {
221 Direction::Input => state.drivers += 1,
222 Direction::Output => state.taps += 1,
223 Direction::InOut => state.inouts += 1,
224 }
225 }
226 }
227
228 for (_, instance) in cell.instances.iter() {
229 analyze_instance(self, &mut net_states, instance);
230 }
231
232 for (sig, list) in net_states.iter() {
233 for (i, state) in list.iter().enumerate() {
234 let info = cell.signal(*sig);
235 state.validate(
236 Net {
237 cell_name: cell.name().clone(),
238 signal_name: info.name.clone(),
239 idx: info.width.map(|_| i),
240 },
241 issues,
242 );
243 }
244 }
245 }
246}
247
248fn analyze_instance<S: Schema + ?Sized>(
249 lib: &LibraryBuilder<S>,
250 net_states: &mut HashMap<SignalId, Vec<NetState>>,
251 inst: &Instance,
252) {
253 match inst.child() {
254 ChildId::Cell(id) => {
255 let cell = lib.cell(id);
256 for (port, conn) in inst.connections() {
257 let dir = cell.port(port).direction;
258 for part in conn.parts() {
259 let states = net_states.get_mut(&part.signal()).unwrap();
260 if let Some(range) = part.range() {
261 for idx in range {
262 update_net_state(&mut states[idx], dir);
263 }
264 } else {
265 update_net_state(&mut states[0], dir);
266 }
267 }
268 }
269 }
270 ChildId::Primitive(_) => {
271 let dir = Direction::InOut;
274 for conn in inst.connections().values() {
275 for part in conn.parts() {
276 let states = net_states.get_mut(&part.signal()).unwrap();
277 if let Some(range) = part.range() {
278 for idx in range {
279 update_net_state(&mut states[idx], dir);
280 }
281 } else {
282 update_net_state(&mut states[0], dir);
283 }
284 }
285 }
286 }
287 }
288}
289
290fn update_net_state(state: &mut NetState, dir: Direction) {
291 match dir {
292 Direction::Output => state.drivers += 1,
293 Direction::Input => state.taps += 1,
294 Direction::InOut => state.inouts += 1,
295 }
296}