Skip to main content

celox_runtime/
reflection.rs

1//! Source-independent hierarchy and signal metadata retained by native images.
2
3use celox_design::{DomainKind, PortTypeKind, StateAddr};
4use serde::{Deserialize, Serialize};
5
6use crate::SignalRef;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct ReflectionScopeId(pub u32);
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct ReflectionSignalId(pub u32);
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
15pub enum SignalDirection {
16    Input,
17    Output,
18    Inout,
19    Internal,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
23pub struct ReflectionScope {
24    pub name: String,
25    pub full_name: String,
26    pub module_name: String,
27    pub parent: Option<ReflectionScopeId>,
28    pub children: Vec<ReflectionScopeId>,
29    pub signals: Vec<ReflectionSignalId>,
30}
31
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
33pub struct ReflectionSignal {
34    pub name: String,
35    pub full_name: String,
36    pub parent: ReflectionScopeId,
37    pub state_address: StateAddr,
38    pub signal: SignalRef,
39    pub direction: SignalDirection,
40    pub domain_kind: DomainKind,
41    pub signed: bool,
42    pub packed_dims: Vec<usize>,
43    pub unpacked_dims: Vec<usize>,
44    pub type_kind: PortTypeKind,
45}
46
47#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
48pub struct DesignReflection {
49    scopes: Vec<ReflectionScope>,
50    signals: Vec<ReflectionSignal>,
51}
52
53impl DesignReflection {
54    pub fn new(scopes: Vec<ReflectionScope>, signals: Vec<ReflectionSignal>) -> Self {
55        Self { scopes, signals }
56    }
57
58    pub fn scopes(&self) -> &[ReflectionScope] {
59        &self.scopes
60    }
61
62    pub fn signals(&self) -> &[ReflectionSignal] {
63        &self.signals
64    }
65
66    pub fn scope(&self, id: ReflectionScopeId) -> Option<&ReflectionScope> {
67        self.scopes.get(id.0 as usize)
68    }
69
70    pub fn signal(&self, id: ReflectionSignalId) -> Option<&ReflectionSignal> {
71        self.signals.get(id.0 as usize)
72    }
73
74    pub fn scope_by_name(&self, full_name: &str) -> Option<(ReflectionScopeId, &ReflectionScope)> {
75        self.scopes
76            .binary_search_by(|scope| scope.full_name.as_str().cmp(full_name))
77            .ok()
78            .map(|index| (ReflectionScopeId(index as u32), &self.scopes[index]))
79    }
80
81    pub fn signal_by_name(
82        &self,
83        full_name: &str,
84    ) -> Option<(ReflectionSignalId, &ReflectionSignal)> {
85        self.signals
86            .binary_search_by(|signal| signal.full_name.as_str().cmp(full_name))
87            .ok()
88            .map(|index| (ReflectionSignalId(index as u32), &self.signals[index]))
89    }
90
91    pub fn validate(&self) -> Result<(), String> {
92        if self.scopes.is_empty() {
93            return Err("reflection has no root scope".into());
94        }
95        if self.scopes[0].parent.is_some() {
96            return Err("reflection root scope has a parent".into());
97        }
98        for (index, scope) in self.scopes.iter().enumerate() {
99            if index != 0 && scope.parent.and_then(|parent| self.scope(parent)).is_none() {
100                return Err(format!("scope `{}` has an invalid parent", scope.full_name));
101            }
102            if index > 0 && self.scopes[index - 1].full_name >= scope.full_name {
103                return Err("reflection scopes are not uniquely name-sorted".into());
104            }
105            if scope
106                .children
107                .iter()
108                .any(|child| self.scope(*child).is_none())
109            {
110                return Err(format!("scope `{}` has an invalid child", scope.full_name));
111            }
112            if scope
113                .signals
114                .iter()
115                .any(|signal| self.signal(*signal).is_none())
116            {
117                return Err(format!("scope `{}` has an invalid signal", scope.full_name));
118            }
119        }
120        for (index, signal) in self.signals.iter().enumerate() {
121            if self.scope(signal.parent).is_none() {
122                return Err(format!(
123                    "signal `{}` has an invalid parent",
124                    signal.full_name
125                ));
126            }
127            if index > 0 && self.signals[index - 1].full_name >= signal.full_name {
128                return Err("reflection signals are not uniquely name-sorted".into());
129            }
130        }
131        Ok(())
132    }
133}