celox_runtime/
reflection.rs1use 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}