calyx_frontend/ast.rs
1//! Abstract Syntax Tree for Calyx
2use super::parser;
3use crate::{Attributes, PortDef, Primitive, source_info::SourceInfoTable};
4use atty::Stream;
5use calyx_utils::{CalyxResult, Error, GPosIdx, Id, PosString};
6use std::{num::NonZeroU64, path::PathBuf};
7
8/// Corresponds to an individual Calyx file.
9#[derive(Debug)]
10pub struct NamespaceDef {
11 /// Path to extern files.
12 pub imports: Vec<PosString>,
13 /// List of component definitions.
14 pub components: Vec<ComponentDef>,
15 /// Extern statements and any primitive declarations in them.
16 pub externs: Vec<(Option<PosString>, Vec<Primitive>)>,
17 /// Optional opaque metadata.
18 pub metadata: Option<String>,
19 /// Optional Source Info table
20 pub source_info_table: Option<SourceInfoTable>,
21}
22
23impl NamespaceDef {
24 /// Construct a namespace from a file or the input stream.
25 /// If no file is provided, the input stream must be a TTY.
26 pub fn construct(file: &Option<PathBuf>) -> CalyxResult<Self> {
27 match file {
28 Some(file) => parser::CalyxParser::parse_file(file),
29 None => {
30 if atty::isnt(Stream::Stdin) {
31 parser::CalyxParser::parse(std::io::stdin())
32 } else {
33 Err(Error::invalid_file(
34 "No file provided and terminal not a TTY".to_string(),
35 ))
36 }
37 }
38 }
39 }
40
41 /// Construct a namespace from a definition using a string.
42 pub fn construct_from_str(inp: &str) -> CalyxResult<Self> {
43 parser::CalyxParser::parse(inp.as_bytes())
44 }
45}
46
47/// AST statement for defining components.
48#[derive(Debug)]
49pub struct ComponentDef {
50 /// Name of the component.
51 pub name: Id,
52 /// Defines input and output ports along with their attributes.
53 pub signature: Vec<PortDef<u64>>,
54 /// List of instantiated sub-components
55 pub cells: Vec<Cell>,
56 /// List of groups
57 pub groups: Vec<Group>,
58 /// List of StaticGroups
59 pub static_groups: Vec<StaticGroup>,
60 /// List of fsms
61 pub fsms: Vec<Fsm>,
62 /// List of continuous assignments
63 pub continuous_assignments: Vec<Wire>,
64 /// Single control statement for this component.
65 pub control: Control,
66 /// Attributes attached to this component
67 pub attributes: Attributes,
68 /// True iff this is a combinational component
69 pub is_comb: bool,
70 /// (Optional) latency of component, if it is static
71 pub latency: Option<NonZeroU64>,
72}
73
74impl ComponentDef {
75 pub fn new<S>(
76 name: S,
77 is_comb: bool,
78 latency: Option<NonZeroU64>,
79 signature: Vec<PortDef<u64>>,
80 ) -> Self
81 where
82 S: Into<Id>,
83 {
84 Self {
85 name: name.into(),
86 signature,
87 cells: Vec::new(),
88 groups: Vec::new(),
89 static_groups: Vec::new(),
90 fsms: Vec::new(),
91 continuous_assignments: Vec::new(),
92 control: Control::empty(),
93 attributes: Attributes::default(),
94 is_comb,
95 latency,
96 }
97 }
98}
99
100/// Statement that refers to a port on a subcomponent.
101/// This is distinct from a `Portdef` which defines a port.
102#[derive(Debug)]
103pub enum Port {
104 /// Refers to the port named `port` on the subcomponent
105 /// `component`.
106 Comp { component: Id, port: Id },
107
108 /// Refers to the port named `port` on the component
109 /// currently being defined.
110 This { port: Id },
111
112 /// `struct_elem[name]` parses into `Hole { struct_elem, name }`
113 /// and is a hole named `name` on `struct_elem`, which is the
114 /// name of either a group or an FSM
115 Hole { struct_elem: Id, name: Id },
116}
117
118// ===================================
119// AST for wire guard expressions
120// ===================================
121
122#[derive(Debug)]
123pub enum NumType {
124 Decimal,
125 Binary,
126 Octal,
127 Hex,
128}
129
130/// Custom bitwidth numbers
131#[derive(Debug)]
132pub struct BitNum {
133 pub width: u64,
134 pub num_type: NumType,
135 pub val: u64,
136 pub span: GPosIdx,
137}
138
139/// Atomic operations used in guard conditions and RHS of the
140/// guarded assignments.
141#[derive(Debug)]
142pub enum Atom {
143 /// Accessing a particular port on a component.
144 Port(Port),
145 /// A constant.
146 Num(BitNum),
147}
148
149/// The AST for GuardExprs
150#[derive(Debug)]
151pub enum GuardExpr {
152 // Logical operations
153 And(Box<GuardExpr>, Box<GuardExpr>),
154 Or(Box<GuardExpr>, Box<GuardExpr>),
155 Not(Box<GuardExpr>),
156 CompOp(CompGuard),
157 Atom(Atom),
158}
159
160/// Guard Comparison Type
161pub type CompGuard = (GuardComp, Atom, Atom);
162
163/// The AST for StaticGuardExprs
164#[derive(Debug)]
165pub enum StaticGuardExpr {
166 And(Box<StaticGuardExpr>, Box<StaticGuardExpr>),
167 Or(Box<StaticGuardExpr>, Box<StaticGuardExpr>),
168 Not(Box<StaticGuardExpr>),
169 CompOp(CompGuard),
170 Atom(Atom),
171 StaticInfo((u64, u64)),
172}
173
174/// Possible comparison operators for guards.
175#[derive(Debug)]
176pub enum GuardComp {
177 Eq,
178 Neq,
179 Gt,
180 Lt,
181 Geq,
182 Leq,
183}
184
185/// Guards `expr` using the optional guard condition `guard`.
186#[derive(Debug)]
187pub struct Guard {
188 pub guard: Option<GuardExpr>,
189 pub expr: Atom,
190}
191
192/// Guards `expr` using the optional guard condition `guard`.
193#[derive(Debug)]
194pub struct StaticGuard {
195 pub guard: Option<StaticGuardExpr>,
196 pub expr: Atom,
197}
198
199// ===================================
200// Data definitions for Structure
201// ===================================
202
203/// Prototype of the cell definition
204#[derive(Debug)]
205pub struct Proto {
206 /// Name of the primitive.
207 pub name: Id,
208 /// Parameter binding for primitives
209 pub params: Vec<u64>,
210}
211
212/// The Cell AST nodes.
213#[derive(Debug)]
214pub struct Cell {
215 /// Name of the cell.
216 pub name: Id,
217 /// Name of the prototype this cell was built from.
218 pub prototype: Proto,
219 /// Attributes attached to this cell definition
220 pub attributes: Attributes,
221 /// Whether this cell is external
222 pub reference: bool,
223}
224
225/// Methods for constructing the structure AST nodes.
226impl Cell {
227 /// Constructs a primitive cell instantiation.
228 pub fn from(
229 name: Id,
230 proto: Id,
231 params: Vec<u64>,
232 attributes: Attributes,
233 reference: bool,
234 ) -> Cell {
235 Cell {
236 name,
237 prototype: Proto {
238 name: proto,
239 params,
240 },
241 attributes,
242 reference,
243 }
244 }
245}
246
247#[derive(Debug)]
248pub struct Group {
249 pub name: Id,
250 pub wires: Vec<Wire>,
251 pub attributes: Attributes,
252 pub is_comb: bool,
253}
254
255#[derive(Debug)]
256pub struct StaticGroup {
257 pub name: Id,
258 pub wires: Vec<StaticWire>,
259 pub attributes: Attributes,
260 pub latency: NonZeroU64,
261}
262
263#[derive(Debug)]
264/// The next state transition for an FSM.
265///
266/// Transitions are either `Unconditional` (always transition to one specific state) or
267/// `Conditional` (with a set of guards mapping to next states). For the `Conditional` case, we
268/// have a list of guards and their corresponding states; the last entry in this list is the
269/// default transition.
270pub enum Transition {
271 Unconditional(u64),
272 Conditional(Vec<(Option<GuardExpr>, u64)>),
273}
274
275#[derive(Debug)]
276/// A single state in an FSM.
277///
278/// A `FSMRule` consists of the set of assignments that are active in a given state and the
279/// transitions from this state to other states. The assignments are a list of wires, like a
280/// `group` definition.
281pub struct FSMState {
282 pub assignments: Vec<Wire>,
283 pub transition: Transition,
284}
285
286#[derive(Debug)]
287/// A fsm block has a name, attributes, and `FSMRule`s
288/// that correspond to all the states within the fsm.
289pub struct Fsm {
290 // Name of the fsm construct
291 pub name: Id,
292 // Attributes attached to this fsm
293 pub attributes: Attributes,
294 // A list of rules, indexed by state.
295 pub fsm_states: Vec<FSMState>,
296}
297
298/// Data for the `->` structure statement.
299#[derive(Debug)]
300pub struct Wire {
301 /// Source of the wire.
302 pub src: Guard,
303
304 /// Guarded destinations of the wire.
305 pub dest: Port,
306
307 /// Attributes for this assignment
308 pub attributes: Attributes,
309}
310
311/// Data for the `->` structure statement.
312#[derive(Debug)]
313pub struct StaticWire {
314 /// Source of the wire.
315 pub src: StaticGuard,
316
317 /// Guarded destinations of the wire.
318 pub dest: Port,
319
320 /// Attributes for this assignment
321 pub attributes: Attributes,
322}
323
324/// Control AST nodes.
325/// Since enables and static enables are indistinguishable to the AST, there
326/// is single Control Enum for both Static and Dynamic Control
327#[derive(Debug)]
328#[allow(clippy::large_enum_variant)]
329pub enum Control {
330 /// Represents sequential composition of control statements.
331 Seq {
332 /// List of `Control` statements to run in sequence.
333 stmts: Vec<Control>,
334 /// Attributes
335 attributes: Attributes,
336 },
337 /// Represents parallel composition of control statements.
338 Par {
339 /// List of `Control` statements to run in sequence.
340 stmts: Vec<Control>,
341 /// Attributes
342 attributes: Attributes,
343 },
344 /// Standard imperative if statement
345 If {
346 /// Port that connects the conditional check.
347 port: Port,
348
349 /// Modules that need to be enabled to send signal on `port`.
350 cond: Option<Id>,
351
352 /// Control for the true branch.
353 tbranch: Box<Control>,
354
355 /// Control for the true branch.
356 fbranch: Box<Control>,
357
358 /// Attributes
359 attributes: Attributes,
360 },
361 /// Standard imperative while statement
362 While {
363 /// Port that connects the conditional check.
364 port: Port,
365
366 /// Modules that need to be enabled to send signal on `port`.
367 cond: Option<Id>,
368
369 /// Control for the loop body.
370 body: Box<Control>,
371
372 /// Attributes
373 attributes: Attributes,
374 },
375 /// Static Repeat (essentially a bounded while loop w/o a condition)
376 Repeat {
377 /// Control for the true branch.
378 num_repeats: u64,
379
380 /// Control for the true branch.
381 body: Box<Control>,
382
383 /// Attributes
384 attributes: Attributes,
385 },
386 /// Runs the control for a list of subcomponents.
387 Enable {
388 /// Group to be enabled
389 comp: Id,
390 /// Attributes
391 attributes: Attributes,
392 },
393 /// Invoke component with input/output assignments.
394 Invoke {
395 /// Name of the component to be invoked.
396 comp: Id,
397 /// Input assignments
398 inputs: Vec<(Id, Atom)>,
399 /// Output assignments
400 outputs: Vec<(Id, Atom)>,
401 /// Attributes
402 attributes: Attributes,
403 /// Combinational group that may execute with this invoke.
404 comb_group: Option<Id>,
405 /// External cells that may execute with this invoke.
406 ref_cells: Vec<(Id, Id)>,
407 },
408 /// Invoke component with input/output assignments.
409 StaticInvoke {
410 /// Name of the component to be invoked.
411 comp: Id,
412 /// Input assignments
413 inputs: Vec<(Id, Atom)>,
414 /// Output assignments
415 outputs: Vec<(Id, Atom)>,
416 /// Attributes
417 attributes: Attributes,
418 /// External cells that may execute with this invoke.
419 ref_cells: Vec<(Id, Id)>,
420 /// Combinational group that may execute with this invoke.
421 comb_group: Option<Id>,
422 /// (optional) latency. Latency can be inferred if not given.
423 latency: Option<NonZeroU64>,
424 },
425 /// Control statement that does nothing.
426 Empty {
427 /// Attributes
428 attributes: Attributes,
429 },
430 /// Represents sequential composition of static control statements.
431 StaticSeq {
432 /// List of `Control` statements to run in sequence.
433 /// If not all of these stmts are static, we should error out
434 stmts: Vec<Control>,
435 /// Attributes
436 attributes: Attributes,
437 /// Optional latency for the seq
438 latency: Option<NonZeroU64>,
439 },
440 /// Represents parallel composition of static control statements.
441 StaticPar {
442 /// List of `Control` statements to run in sequence.
443 /// If not all of these stmts are static, we should error out
444 stmts: Vec<Control>,
445 /// Attributes
446 attributes: Attributes,
447 /// Optional latency for the par
448 latency: Option<NonZeroU64>,
449 },
450 /// Static if statement.
451 StaticIf {
452 /// Port that connects the conditional check.
453 port: Port,
454
455 /// Control for the true branch.
456 tbranch: Box<Control>,
457
458 /// Control for the true branch.
459 fbranch: Box<Control>,
460
461 /// Attributes
462 attributes: Attributes,
463
464 /// Optional latency; should be the longer of the two branches
465 latency: Option<NonZeroU64>,
466 },
467 /// Static Repeat (essentially a bounded while loop w/o a condition)
468 StaticRepeat {
469 /// Control for the true branch.
470 num_repeats: u64,
471
472 /// Control for the true branch.
473 body: Box<Control>,
474
475 /// Attributes
476 attributes: Attributes,
477 },
478}
479
480impl Control {
481 pub fn empty() -> Control {
482 Control::Empty {
483 attributes: Attributes::default(),
484 }
485 }
486
487 pub fn get_attributes(&self) -> &Attributes {
488 match self {
489 Control::Seq { attributes, .. } => attributes,
490 Control::Par { attributes, .. } => attributes,
491 Control::If { attributes, .. } => attributes,
492 Control::While { attributes, .. } => attributes,
493 Control::Repeat { attributes, .. } => attributes,
494 Control::Enable { attributes, .. } => attributes,
495 Control::Invoke { attributes, .. } => attributes,
496 Control::Empty { attributes, .. } => attributes,
497 Control::StaticSeq { attributes, .. } => attributes,
498 Control::StaticPar { attributes, .. } => attributes,
499 Control::StaticIf { attributes, .. } => attributes,
500 Control::StaticRepeat { attributes, .. } => attributes,
501 Control::StaticInvoke { attributes, .. } => attributes,
502 }
503 }
504}