1use serde::{Serialize, Deserialize};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub enum PortDirection { Input, Output, Inout }
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22pub enum SignalType { Wire, Reg, Logic }
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub enum ResetPolarity { ActiveHigh, ActiveLow }
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub enum CounterDirection { Up, Down, UpDown }
29
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub enum ArbitrationScheme { RoundRobin, Priority, WeightedRoundRobin }
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
48pub enum HwEntityType {
49 Module { name: String, is_top: bool },
51 Port { direction: PortDirection, width: u32, domain: Option<String> },
52 Signal { width: u32, signal_type: SignalType, domain: Option<String> },
53 Register { width: u32, reset_value: Option<u64>, clock: Option<String> },
54 Memory { depth: u32, width: u32, ports: u8 },
55 Fifo { depth: u32, width: u32 },
56 Bus { width: u32, protocol: Option<String> },
57 Parameter { value: String },
58
59 Fsm { states: Vec<String>, initial: Option<String> },
61 Counter { width: u32, direction: CounterDirection },
62 Arbiter { scheme: ArbitrationScheme, ports: u8 },
63 Decoder { input_width: u32, output_width: u32 },
64 Mux { inputs: u8, select_width: u32 },
65
66 Clock { frequency: Option<String>, domain: String },
68 Reset { polarity: ResetPolarity, synchronous: bool },
69 Interrupt { priority: Option<u8>, edge_triggered: bool },
70
71 Handshake { valid_signal: String, ready_signal: String },
73 Pipeline { stages: u32, stall_signal: Option<String> },
74 Transaction { request: String, response: String },
75
76 DataPath { width: u32, signed: bool },
78 Address { width: u32, base: Option<u64>, range: Option<u64> },
79 Configuration { fields: Vec<String> },
80
81 SafetyProperty { formula: String },
83 LivenessProperty { formula: String },
84 FairnessProperty { formula: String },
85 ResponseProperty { trigger: String, response: String, bound: Option<u32> },
86 MutexProperty { signals: Vec<String> },
87 StabilityProperty { signal: String, condition: String },
88}
89
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub enum HwRelation {
105 Drives,
107 DrivesRegistered { clock: String },
108 DataFlow,
109 Reads,
110 Writes,
111 Controls,
113 Selects,
114 Enables,
115 Resets,
116 Triggers { delay: Option<u32> },
118 Constrains,
119 Follows { min: u32, max: u32 },
120 Precedes,
121 Preserves,
122 Contains,
124 Instantiates,
125 ConnectsTo,
126 BelongsToDomain { domain: String },
127 HandshakesWith,
129 Acknowledges,
130 Pipelines { stages: u32 },
131 MutuallyExcludes,
133 EventuallyFollows,
134 AssumedBy,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
143pub enum SignalRole {
144 Input,
145 Output,
146 Internal,
147 Clock,
148}
149
150#[derive(Debug, Clone)]
152pub struct KgSignal {
153 pub name: String,
154 pub width: u32,
155 pub role: SignalRole,
156}
157
158#[derive(Debug, Clone)]
160pub struct KgProperty {
161 pub name: String,
162 pub property_type: String,
163 pub operator: String,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum KgRelation {
169 Temporal,
170 Triggers,
171 Constrains,
172 TypeOf,
173}
174
175#[derive(Debug, Clone)]
177pub struct KgEdge {
178 pub from: String,
179 pub to: String,
180 pub relation: KgRelation,
181 pub property: Option<String>,
182}
183
184#[derive(Debug, Clone)]
186pub struct HwKnowledgeGraph {
187 pub signals: Vec<KgSignal>,
188 pub properties: Vec<KgProperty>,
189 pub edges: Vec<KgEdge>,
190 pub entities: Vec<(String, HwEntityType)>,
192 pub typed_edges: Vec<(String, String, HwRelation)>,
194}
195
196impl HwKnowledgeGraph {
197 pub fn new() -> Self {
198 Self {
199 signals: Vec::new(),
200 properties: Vec::new(),
201 edges: Vec::new(),
202 entities: Vec::new(),
203 typed_edges: Vec::new(),
204 }
205 }
206
207 pub fn add_entity(&mut self, name: impl Into<String>, entity: HwEntityType) {
208 self.entities.push((name.into(), entity));
209 }
210
211 pub fn add_typed_edge(&mut self, from: impl Into<String>, to: impl Into<String>, relation: HwRelation) {
212 self.typed_edges.push((from.into(), to.into(), relation));
213 }
214
215 pub fn to_json(&self) -> String {
217 let mut out = String::from("{\n");
218
219 out.push_str(" \"signals\": [\n");
221 for (i, sig) in self.signals.iter().enumerate() {
222 let role = match &sig.role {
223 SignalRole::Input => "input",
224 SignalRole::Output => "output",
225 SignalRole::Internal => "internal",
226 SignalRole::Clock => "clock",
227 };
228 out.push_str(&format!(
229 " {{\"name\": \"{}\", \"width\": {}, \"role\": \"{}\"}}",
230 sig.name, sig.width, role
231 ));
232 if i < self.signals.len() - 1 {
233 out.push(',');
234 }
235 out.push('\n');
236 }
237 out.push_str(" ],\n");
238
239 out.push_str(" \"properties\": [\n");
241 for (i, prop) in self.properties.iter().enumerate() {
242 out.push_str(&format!(
243 " {{\"name\": \"{}\", \"type\": \"{}\", \"operator\": \"{}\"}}",
244 prop.name, prop.property_type, prop.operator
245 ));
246 if i < self.properties.len() - 1 {
247 out.push(',');
248 }
249 out.push('\n');
250 }
251 out.push_str(" ],\n");
252
253 out.push_str(" \"edges\": [\n");
255 for (i, edge) in self.edges.iter().enumerate() {
256 let rel = match &edge.relation {
257 KgRelation::Temporal => "temporal",
258 KgRelation::Triggers => "triggers",
259 KgRelation::Constrains => "constrains",
260 KgRelation::TypeOf => "type_of",
261 };
262 let prop = edge
263 .property
264 .as_deref()
265 .map(|p| format!(", \"property\": \"{}\"", p))
266 .unwrap_or_default();
267 out.push_str(&format!(
268 " {{\"from\": \"{}\", \"to\": \"{}\", \"relation\": \"{}\"{}}}",
269 edge.from, edge.to, rel, prop
270 ));
271 if i < self.edges.len() - 1 {
272 out.push(',');
273 }
274 out.push('\n');
275 }
276 out.push_str(" ],\n");
277
278 out.push_str(" \"entities\": [\n");
280 for (i, (name, entity)) in self.entities.iter().enumerate() {
281 let entity_json = serde_json::to_string(entity).unwrap_or_else(|_| "{}".to_string());
282 out.push_str(&format!(
283 " {{\"name\": \"{}\", \"entity_type\": {}}}",
284 name, entity_json
285 ));
286 if i < self.entities.len() - 1 {
287 out.push(',');
288 }
289 out.push('\n');
290 }
291 out.push_str(" ],\n");
292
293 out.push_str(" \"typed_edges\": [\n");
295 for (i, (from, to, rel)) in self.typed_edges.iter().enumerate() {
296 let rel_json = serde_json::to_string(rel).unwrap_or_else(|_| "{}".to_string());
297 out.push_str(&format!(
298 " {{\"from\": \"{}\", \"to\": \"{}\", \"relation\": {}}}",
299 from, to, rel_json
300 ));
301 if i < self.typed_edges.len() - 1 {
302 out.push(',');
303 }
304 out.push('\n');
305 }
306 out.push_str(" ]\n");
307
308 out.push('}');
309 out
310 }
311
312 pub fn add_signal(&mut self, name: impl Into<String>, width: u32, role: SignalRole) {
313 self.signals.push(KgSignal {
314 name: name.into(),
315 width,
316 role,
317 });
318 }
319
320 pub fn add_property(
321 &mut self,
322 name: impl Into<String>,
323 property_type: impl Into<String>,
324 operator: impl Into<String>,
325 ) {
326 self.properties.push(KgProperty {
327 name: name.into(),
328 property_type: property_type.into(),
329 operator: operator.into(),
330 });
331 }
332
333 pub fn add_edge(
334 &mut self,
335 from: impl Into<String>,
336 to: impl Into<String>,
337 relation: KgRelation,
338 property: Option<String>,
339 ) {
340 self.edges.push(KgEdge {
341 from: from.into(),
342 to: to.into(),
343 relation,
344 property,
345 });
346 }
347}
348
349impl Default for HwKnowledgeGraph {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355pub fn extract_from_kripke_ast<'a>(
368 expr: &'a crate::ast::logic::LogicExpr<'a>,
369 interner: &crate::Interner,
370) -> HwKnowledgeGraph {
371 use crate::ast::logic::{LogicExpr, QuantifierKind};
372 use std::collections::{HashSet, HashMap};
373
374 let mut kg = HwKnowledgeGraph::new();
375 let mut seen_signals: HashSet<String> = HashSet::new();
376 let mut antecedent_signals: HashSet<String> = HashSet::new();
378 let mut consequent_signals: HashSet<String> = HashSet::new();
379 let mut predicate_names: Vec<String> = Vec::new();
381
382 #[derive(Clone, Copy, PartialEq)]
384 enum Position {
385 Neutral,
386 Antecedent,
387 Consequent,
388 }
389
390 fn walk<'a>(
391 expr: &'a LogicExpr<'a>,
392 interner: &crate::Interner,
393 kg: &mut HwKnowledgeGraph,
394 seen: &mut HashSet<String>,
395 antecedent: &mut HashSet<String>,
396 consequent: &mut HashSet<String>,
397 pred_names: &mut Vec<String>,
398 in_safety: bool,
399 in_liveness: bool,
400 position: Position,
401 impl_depth: u32,
402 ) {
403 match expr {
404 LogicExpr::Predicate { name, args, world } => {
405 let pred_name = interner.resolve(*name).to_string();
406
407 if pred_name.contains("Accessible") || pred_name.contains("Reachable")
409 || pred_name.contains("Next_Temporal")
410 {
411 return;
412 }
413
414 if !pred_name.starts_with('w') {
416 pred_names.push(pred_name.clone());
417 }
418
419 if world.is_some() {
422 let pred_lower = pred_name.to_lowercase();
424 if !pred_lower.is_empty() {
425 seen.insert(pred_name.clone());
426 match position {
427 Position::Antecedent => { antecedent.insert(pred_name.clone()); }
428 Position::Consequent => { consequent.insert(pred_name.clone()); }
429 Position::Neutral => {}
430 }
431 }
432
433 for arg in args.iter() {
434 if let crate::ast::logic::Term::Constant(sym)
435 | crate::ast::logic::Term::Variable(sym) = arg
436 {
437 let arg_name = interner.resolve(*sym).to_string();
438 if (!arg_name.starts_with('w') || arg_name.len() > 3)
440 && arg_name.len() > 1
441 {
442 seen.insert(arg_name.clone());
443 match position {
444 Position::Antecedent => { antecedent.insert(arg_name); }
445 Position::Consequent => { consequent.insert(arg_name); }
446 Position::Neutral => {}
447 }
448 }
449 }
450 }
451 }
452 }
453
454 LogicExpr::Quantifier { kind: QuantifierKind::Universal, variable, body, .. } => {
456 let var_name = interner.resolve(*variable).to_string();
457 let is_temporal_world = var_name.starts_with('w');
458 walk(body, interner, kg, seen, antecedent, consequent, pred_names,
459 in_safety || is_temporal_world, in_liveness, position, impl_depth);
460 }
461
462 LogicExpr::Quantifier { kind: QuantifierKind::Existential, variable, body, .. } => {
464 let var_name = interner.resolve(*variable).to_string();
465 let is_temporal_world = var_name.starts_with('w');
466 walk(body, interner, kg, seen, antecedent, consequent, pred_names,
467 in_safety, in_liveness || is_temporal_world, position, impl_depth);
468 }
469
470 LogicExpr::Quantifier { body, .. } => {
472 walk(body, interner, kg, seen, antecedent, consequent, pred_names,
473 in_safety, in_liveness, position, impl_depth);
474 }
475
476 LogicExpr::Atom(sym) => {
478 let name = interner.resolve(*sym).to_string();
479 if !name.is_empty() && name.len() > 1 {
480 seen.insert(name.clone());
481 match position {
482 Position::Antecedent => { antecedent.insert(name); }
483 Position::Consequent => { consequent.insert(name); }
484 Position::Neutral => {}
485 }
486 }
487 }
488
489 LogicExpr::BinaryOp { left, right, op } => {
491 if matches!(op, crate::token::TokenType::If) {
495 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
497 in_safety, in_liveness, Position::Antecedent, impl_depth);
498 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
499 in_safety, in_liveness, Position::Consequent, impl_depth);
500
501 if let (Some(left_sig), Some(right_sig)) =
504 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
505 {
506 if left_sig != right_sig {
507 kg.add_edge(&left_sig, &right_sig, KgRelation::Triggers, None);
508
509 let is_next = matches!(right,
511 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. }
512 ) || is_kripke_next(right, interner);
513 if is_next && in_safety {
514 kg.add_entity(
515 format!("{}_responds_to_{}", right_sig, left_sig),
516 HwEntityType::ResponseProperty {
517 trigger: left_sig.clone(),
518 response: right_sig.clone(),
519 bound: Some(1),
520 },
521 );
522 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Triggers { delay: Some(1) });
523 }
524
525 let is_eventually = matches!(right,
527 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. }
528 ) || is_kripke_eventually(right, interner);
529 if is_eventually && in_safety {
530 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::EventuallyFollows);
531 }
532 }
533 }
534 } else if matches!(op, crate::token::TokenType::Implies) {
535 let left_is_accessibility = if let LogicExpr::Predicate { name, .. } = left {
539 let pn = interner.resolve(*name).to_string();
540 pn.contains("Accessible") || pn.contains("Reachable") || pn.contains("Next_Temporal")
541 } else { false };
542
543 if !left_is_accessibility {
544 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
548 in_safety, in_liveness, position, impl_depth);
549 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
550 in_safety, in_liveness, position, impl_depth);
551
552 if let (Some(left_sig), Some(right_sig)) =
553 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
554 {
555 if left_sig != right_sig {
556 kg.add_edge(&left_sig, &right_sig, KgRelation::Triggers, None);
557
558 let is_next = matches!(right,
560 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. }
561 ) || is_kripke_next(right, interner);
562 if is_next && in_safety {
563 kg.add_entity(
564 format!("{}_responds_to_{}", right_sig, left_sig),
565 HwEntityType::ResponseProperty {
566 trigger: left_sig.clone(),
567 response: right_sig.clone(),
568 bound: Some(1),
569 },
570 );
571 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Triggers { delay: Some(1) });
572 }
573
574 let is_eventually = matches!(right,
576 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. }
577 ) || is_kripke_eventually(right, interner);
578 if is_eventually && in_safety {
579 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::EventuallyFollows);
580 }
581 }
582 }
583 } else {
584 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
586 in_safety, in_liveness, position, impl_depth);
587 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
588 in_safety, in_liveness, position, impl_depth);
589 }
590 } else {
591 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
593 in_safety, in_liveness, position, impl_depth);
594 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
595 in_safety, in_liveness, position, impl_depth);
596 }
597 }
598
599 LogicExpr::UnaryOp { operand, .. } => {
601 walk(operand, interner, kg, seen, antecedent, consequent, pred_names,
602 in_safety, in_liveness, position, impl_depth);
603
604 if let LogicExpr::BinaryOp { left, right, op: crate::token::TokenType::And } = operand {
606 if let (Some(left_sig), Some(right_sig)) =
607 (extract_signal_name(left, interner), extract_signal_name(right, interner))
608 {
609 if left_sig != right_sig {
610 kg.add_edge(&left_sig, &right_sig, KgRelation::Constrains, None);
611 kg.add_entity(
613 format!("mutex_{}_{}", left_sig, right_sig),
614 HwEntityType::MutexProperty {
615 signals: vec![left_sig.clone(), right_sig.clone()],
616 },
617 );
618 }
619 }
620 }
621 }
622
623 LogicExpr::Temporal { body, .. } => {
625 walk(body, interner, kg, seen, antecedent, consequent, pred_names,
626 in_safety, in_liveness, position, impl_depth);
627 }
628
629 LogicExpr::TemporalBinary { operator, left, right } => {
630 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
631 in_safety, in_liveness, position, impl_depth);
632 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
633 in_safety, in_liveness, position, impl_depth);
634
635 if matches!(operator, crate::ast::logic::BinaryTemporalOp::Until) {
637 if let (Some(left_sig), Some(right_sig)) =
638 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
639 {
640 if left_sig != right_sig {
641 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Precedes);
642 }
643 }
644 }
645 }
646
647 LogicExpr::Modal { operand, .. } => {
649 walk(operand, interner, kg, seen, antecedent, consequent, pred_names,
650 in_safety, in_liveness, position, impl_depth);
651 }
652
653 _ => {}
654 }
655 }
656
657 walk(expr, interner, &mut kg, &mut seen_signals,
658 &mut antecedent_signals, &mut consequent_signals, &mut predicate_names,
659 false, false, Position::Neutral, 0);
660
661 let signal_names: Vec<String> = seen_signals.iter().cloned().collect();
663 let handshake_pairs: Vec<(&str, &[&str])> = vec![
664 ("valid", &["ready", "rdy"]),
665 ("req", &["ack", "gnt", "grant"]),
666 ("request", &["acknowledge", "acknowledgment", "response", "grant"]),
667 ("cmd", &["resp", "response"]),
668 ("start", &["done", "complete"]),
669 ];
670 for (trigger_pattern, response_patterns) in &handshake_pairs {
671 let trigger_match = signal_names.iter().find(|s| s.to_lowercase().contains(trigger_pattern));
672 if let Some(trigger_sig) = trigger_match {
673 for resp_pattern in *response_patterns {
674 let resp_match = signal_names.iter().find(|s| {
675 let lower = s.to_lowercase();
676 lower.contains(resp_pattern) && *s != trigger_sig
677 });
678 if let Some(resp_sig) = resp_match {
679 kg.add_entity(
680 format!("handshake_{}_{}", trigger_sig, resp_sig),
681 HwEntityType::Handshake {
682 valid_signal: trigger_sig.clone(),
683 ready_signal: resp_sig.clone(),
684 },
685 );
686 kg.add_typed_edge(trigger_sig, resp_sig, HwRelation::HandshakesWith);
687 break; }
689 }
690 }
691 }
692
693 for sig_name in &seen_signals {
695 let in_ante = antecedent_signals.contains(sig_name);
696 let in_cons = consequent_signals.contains(sig_name);
697 let name_lower = sig_name.to_lowercase();
698
699 let role = if name_lower.contains("clk") || name_lower.contains("clock") {
700 SignalRole::Clock
701 } else if in_ante && !in_cons {
702 SignalRole::Input
703 } else if in_cons && !in_ante {
704 SignalRole::Output
705 } else {
706 SignalRole::Internal
707 };
708
709 kg.add_signal(sig_name, 1, role.clone());
710
711 if name_lower.contains("clk") || name_lower.contains("clock") {
713 kg.add_entity(sig_name, HwEntityType::Clock {
714 frequency: None,
715 domain: sig_name.clone(),
716 });
717 }
718 }
719
720 let mut mutex_entities: Vec<(String, HwEntityType)> = Vec::new();
722 for edge in &kg.edges {
723 let typed_rel = match &edge.relation {
724 KgRelation::Triggers => HwRelation::Triggers { delay: None },
725 KgRelation::Constrains => HwRelation::Constrains,
726 KgRelation::Temporal => HwRelation::Triggers { delay: None },
727 KgRelation::TypeOf => HwRelation::Contains,
728 };
729 kg.typed_edges.push((edge.from.clone(), edge.to.clone(), typed_rel));
730
731 if edge.relation == KgRelation::Constrains {
733 mutex_entities.push((
734 format!("mutex_{}_{}", edge.from, edge.to),
735 HwEntityType::MutexProperty {
736 signals: vec![edge.from.clone(), edge.to.clone()],
737 },
738 ));
739 }
740 }
741 let already_has_mutex = kg.entities.iter().any(|(_, e)| matches!(e, HwEntityType::MutexProperty { .. }));
743 if !already_has_mutex {
744 for (name, entity) in mutex_entities {
745 kg.add_entity(name, entity);
746 }
747 }
748
749 if !kg.entities.iter().any(|(_, e)| matches!(e, HwEntityType::MutexProperty { .. })) {
752 let sig_names: Vec<String> = kg.signals.iter().map(|s| s.name.clone()).collect();
753 let mut mutex_groups: std::collections::HashMap<String, Vec<String>> = std::collections::HashMap::new();
754 for name in &sig_names {
755 let lower = name.to_lowercase();
756 if let Some(underscore_pos) = lower.rfind('_') {
758 let suffix = &lower[underscore_pos+1..];
759 if suffix.len() <= 2 { let base = lower[..underscore_pos].to_string();
761 mutex_groups.entry(base).or_default().push(name.clone());
762 }
763 }
764 }
765 for (base, group) in &mutex_groups {
766 if group.len() >= 2 && (base.contains("grant") || base.contains("sel") || base.contains("enable")) {
767 kg.add_entity(
768 format!("mutex_{}", base),
769 HwEntityType::MutexProperty { signals: group.clone() },
770 );
771 for i in 0..group.len() {
773 for j in (i+1)..group.len() {
774 kg.add_edge(&group[i], &group[j], KgRelation::Constrains, None);
775 }
776 }
777 }
778 }
779 }
780
781 let prop_name = predicate_names.iter()
784 .find(|n| {
785 let lower = n.to_lowercase();
786 !lower.contains("accessible") && !lower.contains("reachable")
787 && !lower.contains("next_temporal") && lower != "and" && lower != "or"
788 })
789 .cloned();
790
791 let formula_desc = predicate_names.iter()
793 .filter(|n| {
794 let lower = n.to_lowercase();
795 !lower.contains("accessible") && !lower.contains("reachable")
796 && !lower.contains("next_temporal")
797 })
798 .cloned()
799 .collect::<Vec<_>>()
800 .join(", ");
801
802 match expr {
803 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Always, .. } => {
804 let name = prop_name.unwrap_or_else(|| "Safety".to_string());
805 kg.add_property(name.clone(), "safety", "G(...)");
806 kg.add_entity(&name, HwEntityType::SafetyProperty {
807 formula: format!("G({})", formula_desc),
808 });
809 }
810 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. } => {
811 let name = prop_name.unwrap_or_else(|| "Liveness".to_string());
812 kg.add_property(name.clone(), "liveness", "F(...)");
813 kg.add_entity(&name, HwEntityType::LivenessProperty {
814 formula: format!("F({})", formula_desc),
815 });
816 }
817 LogicExpr::Quantifier { kind: QuantifierKind::Universal, .. } => {
818 let name = prop_name.unwrap_or_else(|| "Safety".to_string());
820 kg.add_property(name.clone(), "safety", "G(...)");
821 kg.add_entity(&name, HwEntityType::SafetyProperty {
822 formula: format!("G({})", formula_desc),
823 });
824 }
825 LogicExpr::Quantifier { kind: QuantifierKind::Existential, .. } => {
826 let name = prop_name.unwrap_or_else(|| "Liveness".to_string());
827 kg.add_property(name.clone(), "liveness", "F(...)");
828 kg.add_entity(&name, HwEntityType::LivenessProperty {
829 formula: format!("F({})", formula_desc),
830 });
831 }
832 _ => {}
833 }
834
835 kg
836}
837
838fn is_kripke_next<'a>(
841 expr: &'a crate::ast::logic::LogicExpr<'a>,
842 interner: &crate::Interner,
843) -> bool {
844 use crate::ast::logic::{LogicExpr, QuantifierKind};
845 match expr {
846 LogicExpr::Quantifier { kind: QuantifierKind::Universal, body, variable, .. } => {
847 let var_name = interner.resolve(*variable).to_string();
848 if var_name.starts_with('w') {
849 if let LogicExpr::BinaryOp { left, op, .. } = body {
851 if matches!(op, crate::token::TokenType::Implies | crate::token::TokenType::If) {
852 if let LogicExpr::Predicate { name, .. } = left {
853 let pred_name = interner.resolve(*name).to_string();
854 if pred_name == "Next_Temporal" {
855 return true;
856 }
857 }
858 }
859 }
860 }
861 is_kripke_next(body, interner)
863 }
864 LogicExpr::BinaryOp { right, op, .. } if matches!(op, crate::token::TokenType::Implies) => {
865 is_kripke_next(right, interner)
867 }
868 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. } => true,
869 _ => false,
870 }
871}
872
873fn is_kripke_eventually<'a>(
876 expr: &'a crate::ast::logic::LogicExpr<'a>,
877 interner: &crate::Interner,
878) -> bool {
879 use crate::ast::logic::{LogicExpr, QuantifierKind};
880 match expr {
881 LogicExpr::Quantifier { kind: QuantifierKind::Existential, body, variable, .. } => {
882 let var_name = interner.resolve(*variable).to_string();
883 if var_name.starts_with('w') {
884 if let LogicExpr::BinaryOp { left, op: crate::token::TokenType::And, .. } = body {
885 if let LogicExpr::Predicate { name, .. } = left {
886 let pred_name = interner.resolve(*name).to_string();
887 if pred_name == "Reachable_Temporal" {
888 return true;
889 }
890 }
891 }
892 }
893 is_kripke_eventually(body, interner)
895 }
896 LogicExpr::Quantifier { kind: QuantifierKind::Universal, body, .. } => {
897 is_kripke_eventually(body, interner)
899 }
900 LogicExpr::BinaryOp { right, op, .. } if matches!(op, crate::token::TokenType::Implies) => {
901 is_kripke_eventually(right, interner)
902 }
903 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. } => true,
904 _ => false,
905 }
906}
907
908fn extract_hw_signal_name<'a>(
912 expr: &'a crate::ast::logic::LogicExpr<'a>,
913 interner: &crate::Interner,
914) -> Option<String> {
915 use crate::ast::logic::LogicExpr;
916 match expr {
917 LogicExpr::Quantifier { body, .. } => extract_hw_signal_name(body, interner),
919 LogicExpr::BinaryOp { left, right, op } => {
920 if matches!(op, crate::token::TokenType::Implies) {
921 let left_name = extract_signal_name(left, interner);
923 if left_name.is_some() {
924 return left_name;
925 }
926 extract_hw_signal_name(right, interner)
927 } else {
928 extract_hw_signal_name(left, interner)
929 .or_else(|| extract_hw_signal_name(right, interner))
930 }
931 }
932 _ => extract_signal_name(expr, interner),
934 }
935}
936
937fn extract_signal_name<'a>(
941 expr: &'a crate::ast::logic::LogicExpr<'a>,
942 interner: &crate::Interner,
943) -> Option<String> {
944 use crate::ast::logic::LogicExpr;
945 match expr {
946 LogicExpr::Predicate { name, args, .. } => {
947 let pred_name = interner.resolve(*name).to_string();
948 if pred_name.contains("Accessible") || pred_name.contains("Reachable")
950 || pred_name.contains("Next_Temporal")
951 || pred_name == "Agent" || pred_name == "Theme"
952 {
953 return None;
954 }
955 let arg_name = args.iter().find_map(|arg| match arg {
957 crate::ast::logic::Term::Constant(sym)
958 | crate::ast::logic::Term::Variable(sym) => {
959 let aname = interner.resolve(*sym).to_string();
960 if (!aname.starts_with('w') || aname.len() > 3) && aname.len() > 1 {
961 Some(aname)
962 } else {
963 None
964 }
965 }
966 _ => None,
967 });
968 if let Some(an) = arg_name {
969 return Some(an);
970 }
971 if pred_name.len() > 1 && pred_name.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) {
973 return Some(pred_name);
974 }
975 None
976 }
977 LogicExpr::Quantifier { body, .. } => extract_signal_name(body, interner),
979 LogicExpr::BinaryOp { left, right, op } => {
982 if matches!(op, crate::token::TokenType::Implies) {
983 extract_signal_name(right, interner)
985 .or_else(|| extract_signal_name(left, interner))
986 } else {
987 extract_signal_name(left, interner)
989 .or_else(|| extract_signal_name(right, interner))
990 }
991 }
992 LogicExpr::NeoEvent(data) => {
994 let verb_name = interner.resolve(data.verb).to_string();
995 Some(verb_name)
996 }
997 _ => None,
998 }
999}