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::BinaryOp { left, right, op } => {
472 if matches!(op, crate::token::TokenType::If) {
476 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
478 in_safety, in_liveness, Position::Antecedent, impl_depth);
479 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
480 in_safety, in_liveness, Position::Consequent, impl_depth);
481
482 if let (Some(left_sig), Some(right_sig)) =
485 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
486 {
487 if left_sig != right_sig {
488 kg.add_edge(&left_sig, &right_sig, KgRelation::Triggers, None);
489
490 let is_next = matches!(right,
492 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. }
493 ) || is_kripke_next(right, interner);
494 if is_next && in_safety {
495 kg.add_entity(
496 format!("{}_responds_to_{}", right_sig, left_sig),
497 HwEntityType::ResponseProperty {
498 trigger: left_sig.clone(),
499 response: right_sig.clone(),
500 bound: Some(1),
501 },
502 );
503 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Triggers { delay: Some(1) });
504 }
505
506 let is_eventually = matches!(right,
508 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. }
509 ) || is_kripke_eventually(right, interner);
510 if is_eventually && in_safety {
511 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::EventuallyFollows);
512 }
513 }
514 }
515 } else if matches!(op, crate::token::TokenType::Implies) {
516 let left_is_accessibility = if let LogicExpr::Predicate { name, .. } = left {
520 let pn = interner.resolve(*name).to_string();
521 pn.contains("Accessible") || pn.contains("Reachable") || pn.contains("Next_Temporal")
522 } else { false };
523
524 if !left_is_accessibility {
525 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
529 in_safety, in_liveness, position, impl_depth);
530 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
531 in_safety, in_liveness, position, impl_depth);
532
533 if let (Some(left_sig), Some(right_sig)) =
534 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
535 {
536 if left_sig != right_sig {
537 kg.add_edge(&left_sig, &right_sig, KgRelation::Triggers, None);
538
539 let is_next = matches!(right,
541 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. }
542 ) || is_kripke_next(right, interner);
543 if is_next && in_safety {
544 kg.add_entity(
545 format!("{}_responds_to_{}", right_sig, left_sig),
546 HwEntityType::ResponseProperty {
547 trigger: left_sig.clone(),
548 response: right_sig.clone(),
549 bound: Some(1),
550 },
551 );
552 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Triggers { delay: Some(1) });
553 }
554
555 let is_eventually = matches!(right,
557 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. }
558 ) || is_kripke_eventually(right, interner);
559 if is_eventually && in_safety {
560 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::EventuallyFollows);
561 }
562 }
563 }
564 } else {
565 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
567 in_safety, in_liveness, position, impl_depth);
568 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
569 in_safety, in_liveness, position, impl_depth);
570 }
571 } else {
572 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
574 in_safety, in_liveness, position, impl_depth);
575 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
576 in_safety, in_liveness, position, impl_depth);
577 }
578 }
579
580 LogicExpr::UnaryOp { operand, .. } => {
582 walk(operand, interner, kg, seen, antecedent, consequent, pred_names,
583 in_safety, in_liveness, position, impl_depth);
584
585 if let LogicExpr::BinaryOp { left, right, op: crate::token::TokenType::And } = operand {
587 if let (Some(left_sig), Some(right_sig)) =
588 (extract_signal_name(left, interner), extract_signal_name(right, interner))
589 {
590 if left_sig != right_sig {
591 kg.add_edge(&left_sig, &right_sig, KgRelation::Constrains, None);
592 kg.add_entity(
594 format!("mutex_{}_{}", left_sig, right_sig),
595 HwEntityType::MutexProperty {
596 signals: vec![left_sig.clone(), right_sig.clone()],
597 },
598 );
599 }
600 }
601 }
602 }
603
604 LogicExpr::Temporal { body, .. } => {
606 walk(body, interner, kg, seen, antecedent, consequent, pred_names,
607 in_safety, in_liveness, position, impl_depth);
608 }
609
610 LogicExpr::TemporalBinary { operator, left, right } => {
611 walk(left, interner, kg, seen, antecedent, consequent, pred_names,
612 in_safety, in_liveness, position, impl_depth);
613 walk(right, interner, kg, seen, antecedent, consequent, pred_names,
614 in_safety, in_liveness, position, impl_depth);
615
616 if matches!(operator, crate::ast::logic::BinaryTemporalOp::Until) {
618 if let (Some(left_sig), Some(right_sig)) =
619 (extract_hw_signal_name(left, interner), extract_hw_signal_name(right, interner))
620 {
621 if left_sig != right_sig {
622 kg.add_typed_edge(&left_sig, &right_sig, HwRelation::Precedes);
623 }
624 }
625 }
626 }
627
628 LogicExpr::Modal { operand, .. } => {
630 walk(operand, interner, kg, seen, antecedent, consequent, pred_names,
631 in_safety, in_liveness, position, impl_depth);
632 }
633
634 _ => {}
635 }
636 }
637
638 walk(expr, interner, &mut kg, &mut seen_signals,
639 &mut antecedent_signals, &mut consequent_signals, &mut predicate_names,
640 false, false, Position::Neutral, 0);
641
642 let signal_names: Vec<String> = seen_signals.iter().cloned().collect();
644 let handshake_pairs: Vec<(&str, &[&str])> = vec![
645 ("valid", &["ready", "rdy"]),
646 ("req", &["ack", "gnt", "grant"]),
647 ("request", &["acknowledge", "acknowledgment", "response", "grant"]),
648 ("cmd", &["resp", "response"]),
649 ("start", &["done", "complete"]),
650 ];
651 for (trigger_pattern, response_patterns) in &handshake_pairs {
652 let trigger_match = signal_names.iter().find(|s| s.to_lowercase().contains(trigger_pattern));
653 if let Some(trigger_sig) = trigger_match {
654 for resp_pattern in *response_patterns {
655 let resp_match = signal_names.iter().find(|s| {
656 let lower = s.to_lowercase();
657 lower.contains(resp_pattern) && *s != trigger_sig
658 });
659 if let Some(resp_sig) = resp_match {
660 kg.add_entity(
661 format!("handshake_{}_{}", trigger_sig, resp_sig),
662 HwEntityType::Handshake {
663 valid_signal: trigger_sig.clone(),
664 ready_signal: resp_sig.clone(),
665 },
666 );
667 kg.add_typed_edge(trigger_sig, resp_sig, HwRelation::HandshakesWith);
668 break; }
670 }
671 }
672 }
673
674 for sig_name in &seen_signals {
676 let in_ante = antecedent_signals.contains(sig_name);
677 let in_cons = consequent_signals.contains(sig_name);
678 let name_lower = sig_name.to_lowercase();
679
680 let role = if name_lower.contains("clk") || name_lower.contains("clock") {
681 SignalRole::Clock
682 } else if in_ante && !in_cons {
683 SignalRole::Input
684 } else if in_cons && !in_ante {
685 SignalRole::Output
686 } else {
687 SignalRole::Internal
688 };
689
690 kg.add_signal(sig_name, 1, role.clone());
691
692 if name_lower.contains("clk") || name_lower.contains("clock") {
694 kg.add_entity(sig_name, HwEntityType::Clock {
695 frequency: None,
696 domain: sig_name.clone(),
697 });
698 }
699 }
700
701 let mut mutex_entities: Vec<(String, HwEntityType)> = Vec::new();
703 for edge in &kg.edges {
704 let typed_rel = match &edge.relation {
705 KgRelation::Triggers => HwRelation::Triggers { delay: None },
706 KgRelation::Constrains => HwRelation::Constrains,
707 KgRelation::Temporal => HwRelation::Triggers { delay: None },
708 KgRelation::TypeOf => HwRelation::Contains,
709 };
710 kg.typed_edges.push((edge.from.clone(), edge.to.clone(), typed_rel));
711
712 if edge.relation == KgRelation::Constrains {
714 mutex_entities.push((
715 format!("mutex_{}_{}", edge.from, edge.to),
716 HwEntityType::MutexProperty {
717 signals: vec![edge.from.clone(), edge.to.clone()],
718 },
719 ));
720 }
721 }
722 let already_has_mutex = kg.entities.iter().any(|(_, e)| matches!(e, HwEntityType::MutexProperty { .. }));
724 if !already_has_mutex {
725 for (name, entity) in mutex_entities {
726 kg.add_entity(name, entity);
727 }
728 }
729
730 if !kg.entities.iter().any(|(_, e)| matches!(e, HwEntityType::MutexProperty { .. })) {
733 let sig_names: Vec<String> = kg.signals.iter().map(|s| s.name.clone()).collect();
734 let mut mutex_groups: std::collections::HashMap<String, Vec<String>> = std::collections::HashMap::new();
735 for name in &sig_names {
736 let lower = name.to_lowercase();
737 if let Some(underscore_pos) = lower.rfind('_') {
739 let suffix = &lower[underscore_pos+1..];
740 if suffix.len() <= 2 { let base = lower[..underscore_pos].to_string();
742 mutex_groups.entry(base).or_default().push(name.clone());
743 }
744 }
745 }
746 for (base, group) in &mutex_groups {
747 if group.len() >= 2 && (base.contains("grant") || base.contains("sel") || base.contains("enable")) {
748 kg.add_entity(
749 format!("mutex_{}", base),
750 HwEntityType::MutexProperty { signals: group.clone() },
751 );
752 for i in 0..group.len() {
754 for j in (i+1)..group.len() {
755 kg.add_edge(&group[i], &group[j], KgRelation::Constrains, None);
756 }
757 }
758 }
759 }
760 }
761
762 let prop_name = predicate_names.iter()
765 .find(|n| {
766 let lower = n.to_lowercase();
767 !lower.contains("accessible") && !lower.contains("reachable")
768 && !lower.contains("next_temporal") && lower != "and" && lower != "or"
769 })
770 .cloned();
771
772 let formula_desc = predicate_names.iter()
774 .filter(|n| {
775 let lower = n.to_lowercase();
776 !lower.contains("accessible") && !lower.contains("reachable")
777 && !lower.contains("next_temporal")
778 })
779 .cloned()
780 .collect::<Vec<_>>()
781 .join(", ");
782
783 match expr {
784 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Always, .. } => {
785 let name = prop_name.unwrap_or_else(|| "Safety".to_string());
786 kg.add_property(name.clone(), "safety", "G(...)");
787 kg.add_entity(&name, HwEntityType::SafetyProperty {
788 formula: format!("G({})", formula_desc),
789 });
790 }
791 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. } => {
792 let name = prop_name.unwrap_or_else(|| "Liveness".to_string());
793 kg.add_property(name.clone(), "liveness", "F(...)");
794 kg.add_entity(&name, HwEntityType::LivenessProperty {
795 formula: format!("F({})", formula_desc),
796 });
797 }
798 LogicExpr::Quantifier { kind: QuantifierKind::Universal, .. } => {
799 let name = prop_name.unwrap_or_else(|| "Safety".to_string());
801 kg.add_property(name.clone(), "safety", "G(...)");
802 kg.add_entity(&name, HwEntityType::SafetyProperty {
803 formula: format!("G({})", formula_desc),
804 });
805 }
806 LogicExpr::Quantifier { kind: QuantifierKind::Existential, .. } => {
807 let name = prop_name.unwrap_or_else(|| "Liveness".to_string());
808 kg.add_property(name.clone(), "liveness", "F(...)");
809 kg.add_entity(&name, HwEntityType::LivenessProperty {
810 formula: format!("F({})", formula_desc),
811 });
812 }
813 _ => {}
814 }
815
816 kg
817}
818
819fn is_kripke_next<'a>(
822 expr: &'a crate::ast::logic::LogicExpr<'a>,
823 interner: &crate::Interner,
824) -> bool {
825 use crate::ast::logic::{LogicExpr, QuantifierKind};
826 match expr {
827 LogicExpr::Quantifier { kind: QuantifierKind::Universal, body, variable, .. } => {
828 let var_name = interner.resolve(*variable).to_string();
829 if var_name.starts_with('w') {
830 if let LogicExpr::BinaryOp { left, op, .. } = body {
832 if matches!(op, crate::token::TokenType::Implies | crate::token::TokenType::If) {
833 if let LogicExpr::Predicate { name, .. } = left {
834 let pred_name = interner.resolve(*name).to_string();
835 if pred_name == "Next_Temporal" {
836 return true;
837 }
838 }
839 }
840 }
841 }
842 is_kripke_next(body, interner)
844 }
845 LogicExpr::BinaryOp { right, op, .. } if matches!(op, crate::token::TokenType::Implies) => {
846 is_kripke_next(right, interner)
848 }
849 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Next, .. } => true,
850 _ => false,
851 }
852}
853
854fn is_kripke_eventually<'a>(
857 expr: &'a crate::ast::logic::LogicExpr<'a>,
858 interner: &crate::Interner,
859) -> bool {
860 use crate::ast::logic::{LogicExpr, QuantifierKind};
861 match expr {
862 LogicExpr::Quantifier { kind: QuantifierKind::Existential, body, variable, .. } => {
863 let var_name = interner.resolve(*variable).to_string();
864 if var_name.starts_with('w') {
865 if let LogicExpr::BinaryOp { left, op: crate::token::TokenType::And, .. } = body {
866 if let LogicExpr::Predicate { name, .. } = left {
867 let pred_name = interner.resolve(*name).to_string();
868 if pred_name == "Reachable_Temporal" {
869 return true;
870 }
871 }
872 }
873 }
874 is_kripke_eventually(body, interner)
876 }
877 LogicExpr::Quantifier { kind: QuantifierKind::Universal, body, .. } => {
878 is_kripke_eventually(body, interner)
880 }
881 LogicExpr::BinaryOp { right, op, .. } if matches!(op, crate::token::TokenType::Implies) => {
882 is_kripke_eventually(right, interner)
883 }
884 LogicExpr::Temporal { operator: crate::ast::logic::TemporalOperator::Eventually, .. } => true,
885 _ => false,
886 }
887}
888
889fn extract_hw_signal_name<'a>(
893 expr: &'a crate::ast::logic::LogicExpr<'a>,
894 interner: &crate::Interner,
895) -> Option<String> {
896 use crate::ast::logic::LogicExpr;
897 match expr {
898 LogicExpr::Quantifier { body, .. } => extract_hw_signal_name(body, interner),
900 LogicExpr::BinaryOp { left, right, op } => {
901 if matches!(op, crate::token::TokenType::Implies) {
902 let left_name = extract_signal_name(left, interner);
904 if left_name.is_some() {
905 return left_name;
906 }
907 extract_hw_signal_name(right, interner)
908 } else {
909 extract_hw_signal_name(left, interner)
910 .or_else(|| extract_hw_signal_name(right, interner))
911 }
912 }
913 _ => extract_signal_name(expr, interner),
915 }
916}
917
918fn extract_signal_name<'a>(
922 expr: &'a crate::ast::logic::LogicExpr<'a>,
923 interner: &crate::Interner,
924) -> Option<String> {
925 use crate::ast::logic::LogicExpr;
926 match expr {
927 LogicExpr::Predicate { name, args, .. } => {
928 let pred_name = interner.resolve(*name).to_string();
929 if pred_name.contains("Accessible") || pred_name.contains("Reachable")
931 || pred_name.contains("Next_Temporal")
932 || pred_name == "Agent" || pred_name == "Theme"
933 {
934 return None;
935 }
936 let arg_name = args.iter().find_map(|arg| match arg {
938 crate::ast::logic::Term::Constant(sym)
939 | crate::ast::logic::Term::Variable(sym) => {
940 let aname = interner.resolve(*sym).to_string();
941 if (!aname.starts_with('w') || aname.len() > 3) && aname.len() > 1 {
942 Some(aname)
943 } else {
944 None
945 }
946 }
947 _ => None,
948 });
949 if let Some(an) = arg_name {
950 return Some(an);
951 }
952 if pred_name.len() > 1 && pred_name.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) {
954 return Some(pred_name);
955 }
956 None
957 }
958 LogicExpr::Quantifier { body, .. } => extract_signal_name(body, interner),
960 LogicExpr::BinaryOp { left, right, op } => {
963 if matches!(op, crate::token::TokenType::Implies) {
964 extract_signal_name(right, interner)
966 .or_else(|| extract_signal_name(left, interner))
967 } else {
968 extract_signal_name(left, interner)
970 .or_else(|| extract_signal_name(right, interner))
971 }
972 }
973 LogicExpr::NeoEvent(data) => {
975 let verb_name = interner.resolve(data.verb).to_string();
976 Some(verb_name)
977 }
978 _ => None,
979 }
980}