Skip to main content

veryl_analyzer/ir/
declaration.rs

1use crate::conv::Context;
2use crate::ir::assign_table::{AssignContext, AssignTable};
3use crate::ir::{
4    AssignDestination, Component, Comptime, Expression, FfTable, Statement, VarId, VarIndex,
5    VarSelect,
6};
7use indent::indent_all_by;
8use std::fmt;
9use std::sync::Arc;
10use veryl_parser::resource_table::StrId;
11use veryl_parser::token_range::TokenRange;
12
13#[derive(Clone, Default)]
14pub struct DeclarationBlock(pub Vec<Declaration>);
15
16impl DeclarationBlock {
17    pub fn new(decl: Declaration) -> Self {
18        Self(vec![decl])
19    }
20}
21
22#[derive(Clone)]
23pub enum Declaration {
24    Comb(CombDeclaration),
25    Ff(Box<FfDeclaration>),
26    Inst(Box<InstDeclaration>),
27    Initial(InitialDeclaration),
28    Final(FinalDeclaration),
29    Unsupported(TokenRange),
30    Null,
31}
32
33impl Declaration {
34    pub fn new_comb(statements: Vec<Statement>) -> Self {
35        Self::Comb(CombDeclaration { statements })
36    }
37
38    pub fn new_ff(clock: FfClock, reset: Option<FfReset>, statements: Vec<Statement>) -> Self {
39        Self::Ff(Box::new(FfDeclaration {
40            clock,
41            reset,
42            statements,
43        }))
44    }
45
46    pub fn is_null(&self) -> bool {
47        matches!(self, Declaration::Null)
48    }
49
50    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
51        match self {
52            Declaration::Comb(x) => x.eval_assign(context, assign_table),
53            Declaration::Ff(x) => x.eval_assign(context, assign_table),
54            Declaration::Inst(x) => x.eval_assign(context, assign_table),
55            Declaration::Initial(x) => x.eval_assign(context, assign_table),
56            Declaration::Final(x) => x.eval_assign(context, assign_table),
57            Declaration::Unsupported(_) => (),
58            Declaration::Null => (),
59        }
60        assign_table.refernced.clear();
61    }
62
63    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
64        match self {
65            Declaration::Ff(x) => x.gather_ff(context, table, decl),
66            Declaration::Comb(x) => x.gather_ff_comb(context, table, decl),
67            Declaration::Inst(x) => x.gather_ff(context, table, decl),
68            _ => {}
69        }
70    }
71}
72
73impl fmt::Display for Declaration {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Declaration::Comb(x) => x.fmt(f),
77            Declaration::Ff(x) => x.fmt(f),
78            Declaration::Inst(x) => x.fmt(f),
79            Declaration::Initial(x) => x.fmt(f),
80            Declaration::Final(x) => x.fmt(f),
81            Declaration::Unsupported(_) => "/* unsupported */".fmt(f),
82            Declaration::Null => "".fmt(f),
83        }
84    }
85}
86
87#[derive(Clone)]
88pub struct CombDeclaration {
89    pub statements: Vec<Statement>,
90}
91
92impl CombDeclaration {
93    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
94        for x in &self.statements {
95            x.eval_assign(context, assign_table, AssignContext::Comb, &[]);
96        }
97    }
98
99    pub fn gather_ff_comb(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
100        for x in &self.statements {
101            x.gather_ff_comb_assign(context, table, decl);
102        }
103    }
104}
105
106impl fmt::Display for CombDeclaration {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        let mut ret = "comb {\n".to_string();
109
110        for x in &self.statements {
111            let text = format!("{}\n", x);
112            ret.push_str(&indent_all_by(2, text));
113        }
114
115        ret.push('}');
116        ret.fmt(f)
117    }
118}
119
120#[derive(Clone)]
121pub struct FfClock {
122    pub id: VarId,
123    pub index: VarIndex,
124    pub select: VarSelect,
125    pub comptime: Comptime,
126}
127
128impl fmt::Display for FfClock {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        format!("{}{}{}", self.id, self.index, self.select).fmt(f)
131    }
132}
133
134#[derive(Clone)]
135pub struct FfReset {
136    pub id: VarId,
137    pub index: VarIndex,
138    pub select: VarSelect,
139    pub comptime: Comptime,
140}
141
142impl fmt::Display for FfReset {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        format!("{}{}{}", self.id, self.index, self.select).fmt(f)
145    }
146}
147
148#[derive(Clone)]
149pub struct FfDeclaration {
150    pub clock: FfClock,
151    pub reset: Option<FfReset>,
152    pub statements: Vec<Statement>,
153}
154
155impl FfDeclaration {
156    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
157        for x in &self.statements {
158            x.eval_assign(context, assign_table, AssignContext::Ff, &[]);
159        }
160    }
161
162    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
163        for x in &self.statements {
164            x.gather_ff(context, table, decl);
165        }
166    }
167}
168
169impl fmt::Display for FfDeclaration {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        let mut ret = if let Some(x) = &self.reset {
172            format!("ff ({}, {}) {{\n", self.clock, x)
173        } else {
174            format!("ff ({}) {{\n", self.clock)
175        };
176
177        for x in &self.statements {
178            let text = format!("{}\n", x);
179            ret.push_str(&indent_all_by(2, text));
180        }
181
182        ret.push('}');
183        ret.fmt(f)
184    }
185}
186
187#[derive(Clone)]
188pub struct InstInput {
189    pub id: VarId,
190    pub expr: Expression,
191}
192
193impl fmt::Display for InstInput {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        write!(f, "{} <- {}", self.id, self.expr)
196    }
197}
198
199#[derive(Clone)]
200pub struct InstOutput {
201    pub id: VarId,
202    pub dst: Vec<AssignDestination>,
203}
204
205impl fmt::Display for InstOutput {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        let mut ret = format!("{} -> ", self.id);
208
209        if self.dst.len() == 1 {
210            ret.push_str(&format!("{}", self.dst[0]));
211        } else if !self.dst.is_empty() {
212            ret.push_str(&format!("{{{}", self.dst[0]));
213            for d in &self.dst[1..] {
214                ret.push_str(&format!(", {}", d));
215            }
216            ret.push('}');
217        }
218
219        ret.fmt(f)
220    }
221}
222
223#[derive(Clone)]
224pub struct InstDeclaration {
225    pub name: StrId,
226    pub inputs: Vec<InstInput>,
227    pub outputs: Vec<InstOutput>,
228    /// `Arc`-shared: without it, repeated instantiations of the same generic
229    /// module each deep-clone the `Component`, blowing pass2 memory.
230    pub component: Arc<Component>,
231}
232
233impl InstDeclaration {
234    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
235        for x in &self.outputs {
236            for dst in &x.dst {
237                dst.eval_assign(context, assign_table, AssignContext::Ff);
238            }
239        }
240
241        if let Component::SystemVerilog(x) = self.component.as_ref() {
242            for dst in &x.connects {
243                dst.eval_assign(context, assign_table, AssignContext::SystemVerilog);
244            }
245        }
246    }
247
248    /// Register the variables read by each instance input expression so
249    /// downstream analyses (e.g. comb-to-FF hoist reach) can see that
250    /// those reads happen in a comb context (effectively a continuous-
251    /// assign of the expression to the child port).  Tagged
252    /// `from_ff=false` so the `is_ff` classifier ignores them.
253    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
254        for input in &self.inputs {
255            input.expr.gather_ff(context, table, decl, None, false);
256        }
257    }
258}
259
260impl fmt::Display for InstDeclaration {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        let mut ret = format!("inst {} (\n", self.name);
263
264        for x in &self.inputs {
265            let text = format!("{};\n", x);
266            ret.push_str(&indent_all_by(2, text));
267        }
268
269        for x in &self.outputs {
270            let text = format!("{};\n", x);
271            ret.push_str(&indent_all_by(2, text));
272        }
273
274        ret.push_str(") {\n");
275
276        let text = format!("{}\n", self.component);
277        ret.push_str(&indent_all_by(2, text));
278
279        ret.push('}');
280        ret.fmt(f)
281    }
282}
283
284#[derive(Clone)]
285pub struct InitialDeclaration {
286    pub statements: Vec<Statement>,
287}
288
289impl InitialDeclaration {
290    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
291        for x in &self.statements {
292            x.eval_assign(context, assign_table, AssignContext::Initial, &[]);
293        }
294    }
295}
296
297impl fmt::Display for InitialDeclaration {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        let mut ret = "initial {\n".to_string();
300
301        for x in &self.statements {
302            let text = format!("{}\n", x);
303            ret.push_str(&indent_all_by(2, text));
304        }
305
306        ret.push('}');
307        ret.fmt(f)
308    }
309}
310
311#[derive(Clone)]
312pub struct FinalDeclaration {
313    pub statements: Vec<Statement>,
314}
315
316impl FinalDeclaration {
317    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
318        for x in &self.statements {
319            x.eval_assign(context, assign_table, AssignContext::Final, &[]);
320        }
321    }
322}
323
324impl fmt::Display for FinalDeclaration {
325    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326        let mut ret = "final {\n".to_string();
327
328        for x in &self.statements {
329            let text = format!("{}\n", x);
330            ret.push_str(&indent_all_by(2, text));
331        }
332
333        ret.push('}');
334        ret.fmt(f)
335    }
336}