Skip to main content

logicaffeine_compile/codegen_sva/
rtl_kg.rs

1//! RTL KG + Spec-RTL Linking
2//!
3//! Converts RtlModule → HwKnowledgeGraph and links spec KG to RTL KG.
4
5use super::rtl_extract::{RtlModule, PortDirection};
6use logicaffeine_language::semantics::knowledge_graph::{
7    HwKnowledgeGraph, SignalRole, KgRelation,
8};
9
10/// Convert an RtlModule to a HwKnowledgeGraph.
11pub fn rtl_to_kg(module: &RtlModule) -> HwKnowledgeGraph {
12    let mut kg = HwKnowledgeGraph::new();
13
14    for port in &module.ports {
15        let role = match &port.direction {
16            PortDirection::Input => SignalRole::Input,
17            PortDirection::Output => SignalRole::Output,
18            PortDirection::Inout => SignalRole::Internal,
19        };
20        // Override for detected clocks
21        let role = if module.clocks.contains(&port.name) {
22            SignalRole::Clock
23        } else {
24            role
25        };
26        kg.add_signal(&port.name, port.width, role);
27    }
28
29    for sig in &module.signals {
30        kg.add_signal(&sig.name, sig.width, SignalRole::Internal);
31    }
32
33    kg
34}
35
36/// Link result reporting unmatched signals.
37#[derive(Debug)]
38pub struct LinkResult {
39    pub matched: Vec<(String, String)>,
40    pub unmatched_spec: Vec<String>,
41    pub unmatched_rtl: Vec<String>,
42}
43
44/// Link a spec KG to an RTL KG by signal name matching.
45/// Strategy: exact match → case-insensitive match.
46pub fn link_kg(spec_kg: &HwKnowledgeGraph, rtl_kg: &HwKnowledgeGraph) -> LinkResult {
47    let mut matched = Vec::new();
48    let mut used_rtl: std::collections::HashSet<String> = std::collections::HashSet::new();
49
50    let rtl_names: Vec<String> = rtl_kg.signals.iter().map(|s| s.name.clone()).collect();
51
52    for spec_sig in &spec_kg.signals {
53        // Exact match
54        if let Some(rtl_name) = rtl_names.iter().find(|n| **n == spec_sig.name) {
55            matched.push((spec_sig.name.clone(), rtl_name.clone()));
56            used_rtl.insert(rtl_name.clone());
57            continue;
58        }
59        // Case-insensitive match
60        if let Some(rtl_name) = rtl_names.iter().find(|n| n.to_lowercase() == spec_sig.name.to_lowercase()) {
61            matched.push((spec_sig.name.clone(), rtl_name.clone()));
62            used_rtl.insert(rtl_name.clone());
63            continue;
64        }
65    }
66
67    let unmatched_spec: Vec<String> = spec_kg.signals.iter()
68        .filter(|s| !matched.iter().any(|(spec, _)| spec == &s.name))
69        .map(|s| s.name.clone())
70        .collect();
71
72    let unmatched_rtl: Vec<String> = rtl_kg.signals.iter()
73        .filter(|s| !used_rtl.contains(&s.name))
74        .map(|s| s.name.clone())
75        .collect();
76
77    LinkResult { matched, unmatched_spec, unmatched_rtl }
78}