From ca099d237471a99623ab4b97d899928b317f3e34 Mon Sep 17 00:00:00 2001 From: Sai Date: Thu, 30 Jul 2026 09:34:02 -0700 Subject: [PATCH] Add analog named events (VAMS-2023 5.10.4) VAMS-2023 5.10.4 (Mantis 7809) describes how analog named events are declared, triggered and detected: event ana_event; analog begin @(timer(1n)) -> ana_event; @(ana_event) $strobe("detected"); end None of this existed in OpenVAF: `event` was not a token keyword, `->` was not lexed, and `@()` was silently swallowed by the monitored-event path without resolving the name. Front end --------- - lexer/tokens: `->` (`Trigger`) and the `event` keyword. - grammar: `EventDecl` as a module item and `EventTriggerStmt` as a statement. The trigger target is wrapped in a path expression so the name is resolved by the same machinery as every other reference, and `EventStmt` now exposes its event expression. - hir_def: `Event`/`EventId` item-tree entity interned like a branch, declared in the module scope; `Stmt::EventTrigger` and `Event::Named`. `@(x)` is a named event when `x` is a bare path and a monitored event otherwise. - hir_ty: `Ty::Event`/`TyRequirement::Event`; both `-> ev;` and `@(ev)` require a declared event, so naming a variable is a type error and an undeclared name is a resolve error. - validation: `event_trigger` belongs to `analog_event_statement`, not to `analog_statement`, so in an analog block a trigger is only allowed under an event control (`@(timer(1n)) -> ev;`); it is also allowed in the standalone procedural blocks. Tracked with a dedicated flag rather than `BodyCtx` so a probe-dependent `if` inside the event body does not produce a false positive. Lowering -------- A named event becomes a boolean place, `PlaceKind::NamedEvent`, that is false when the block starts, set to true by `-> ev;` and used as the guard of the `@(ev)` body. That gives the sequential semantics of a single evaluation: a detection placed before the trigger does not fire, one placed after it fires exactly when the trigger executed, and a conditional trigger produces a real phi rather than a constant. Known limits, all pre-existing and documented rather than papered over: - OpenVAF has no digital lane, so an event cannot be observed by an `always` block, and triggering in a procedural block is not visible to the analog block (the flag is per MIR function). - monitored events (`@(timer(...))`, `@(cross(...))`) always evaluate their body, so a trigger inside one always fires. - event arrays (`event ev[0:3];`) are rejected with a parse error. Tests: - ui/named_events.va: declarations (several per statement), triggers from event statements and detections, accepted without diagnostics. - ui/named_events_err.log: trigger outside an event statement, trigger and detection naming a variable, and an undeclared event. - mir/named_events.{va,mir}: snapshot showing the guard being `false` before the trigger, a phi for a conditional trigger and `true` for an unconditional one. - integration_tests/VAMS2023_EVENTS + osdi/vams2023_events.snap: end-to-end compile/link/load of a device whose conductance is selected by a named event. Co-Authored-By: Claude Opus 5 --- .../VAMS2023_EVENTS/vams2023_events.va | 40 +++++++++++ openvaf/hir/src/body.rs | 65 ++++++++++++++--- openvaf/hir/src/lib.rs | 23 ++++++- openvaf/hir_def/src/body/lower.rs | 15 +++- openvaf/hir_def/src/body/pretty.rs | 5 ++ openvaf/hir_def/src/db.rs | 8 ++- openvaf/hir_def/src/expr.rs | 62 ++++++++++++++--- openvaf/hir_def/src/item_tree.rs | 16 ++++- openvaf/hir_def/src/item_tree/lower.rs | 17 ++++- openvaf/hir_def/src/item_tree/pretty.rs | 3 + openvaf/hir_def/src/lib.rs | 8 ++- openvaf/hir_def/src/nameres.rs | 19 ++++- openvaf/hir_def/src/nameres/collect.rs | 3 + openvaf/hir_lower/src/body.rs | 5 +- openvaf/hir_lower/src/ctx.rs | 2 + openvaf/hir_lower/src/lib.rs | 8 ++- openvaf/hir_lower/src/stmt.rs | 20 ++++++ openvaf/hir_ty/src/inference.rs | 10 ++- openvaf/hir_ty/src/types.rs | 11 ++- openvaf/hir_ty/src/validation.rs | 20 ++++++ openvaf/hir_ty/src/validation/body.rs | 24 +++++++ openvaf/lexer/src/lib.rs | 6 ++ openvaf/parser/src/grammar/items/module.rs | 10 +++ openvaf/parser/src/grammar/stmts.rs | 41 +++++++++-- openvaf/syntax/src/ast/generated/nodes.rs | 69 ++++++++++++++++++- openvaf/syntax/veriloga.ungram | 10 ++- openvaf/test_data/mir/named_events.mir | 57 +++++++++++++++ openvaf/test_data/mir/named_events.va | 36 ++++++++++ openvaf/test_data/osdi/vams2023_events.snap | 16 +++++ openvaf/test_data/ui/named_events.va | 33 +++++++++ openvaf/test_data/ui/named_events_err.log | 26 +++++++ openvaf/test_data/ui/named_events_err.va | 23 +++++++ openvaf/tokens/src/lexer.rs | 3 + openvaf/tokens/src/lib.rs | 1 + openvaf/tokens/src/parser/generated.rs | 13 +++- sourcegen/src/ast.rs | 1 + sourcegen/src/ast/src.rs | 4 ++ 37 files changed, 683 insertions(+), 50 deletions(-) create mode 100644 integration_tests/VAMS2023_EVENTS/vams2023_events.va create mode 100644 openvaf/test_data/mir/named_events.mir create mode 100644 openvaf/test_data/mir/named_events.va create mode 100644 openvaf/test_data/osdi/vams2023_events.snap create mode 100644 openvaf/test_data/ui/named_events.va create mode 100644 openvaf/test_data/ui/named_events_err.log create mode 100644 openvaf/test_data/ui/named_events_err.va diff --git a/integration_tests/VAMS2023_EVENTS/vams2023_events.va b/integration_tests/VAMS2023_EVENTS/vams2023_events.va new file mode 100644 index 00000000..0b6ff84e --- /dev/null +++ b/integration_tests/VAMS2023_EVENTS/vams2023_events.va @@ -0,0 +1,40 @@ +// VAMS-2023 5.10.4 (Mantis 7809): analog named events - declared with `event`, +// triggered with `-> name;` from an analog event statement, detected with +// `@(name)`. +`include "disciplines.vams" + +module vams2023_events(a, c); + inout a, c; + electrical a, c; + + parameter real r = 1000.0 from (0.0:inf); + parameter real period = 1e-9 from (0.0:inf); + + event sample_event; + event blank_event; + + real gain; + integer samples; + + analog begin + gain = 1.0; + samples = 0; + + // conditional trigger from a monitored event statement + @(timer(period)) begin + if (r > 100.0) + -> sample_event; + end + + // an event that is never triggered + @(blank_event) gain = 0.0; + + // detection after the trigger + @(sample_event) begin + samples = samples + 1; + gain = 2.0; + end + + I(a, c) <+ gain * V(a, c) / r; + end +endmodule diff --git a/openvaf/hir/src/body.rs b/openvaf/hir/src/body.rs index a41bd90a..6cded852 100644 --- a/openvaf/hir/src/body.rs +++ b/openvaf/hir/src/body.rs @@ -10,8 +10,8 @@ use hir_ty::types::{Signature, Ty}; pub use syntax::ast::{BinaryOp, UnaryOp}; use crate::{ - Branch, BranchWrite, CompilationDB, Function, FunctionArg, NatureAttribute, Node, Parameter, - Variable, + Branch, BranchWrite, CompilationDB, Function, FunctionArg, NamedEvent, NatureAttribute, Node, + Parameter, Variable, }; #[derive(Debug, Clone)] @@ -54,6 +54,16 @@ impl<'a> BodyRef<'a> { Some((src, dst)) } + /// Resolves the path expression naming a [`NamedEvent`] in `-> ev;` or + /// `@(ev)`. `None` if the name did not resolve to an event (already + /// diagnosed by type inference). + pub fn resolve_event(&self, expr: ExprId) -> Option { + match self.infere.expr_types[expr] { + Ty::Event(id) => Some(NamedEvent { id }), + _ => None, + } + } + fn resolve_path(&self, expr: ExprId) -> Ref { match self.infere.expr_types[expr] { Ty::Var(_, id) => Ref::Variable(Variable { id }), @@ -188,6 +198,10 @@ impl<'a> BodyRef<'a> { hir_def::Stmt::EventControl { ref event, body } => { Some(Stmt::EventControl { event, body }) } + // an unresolved event was already diagnosed; drop the statement + hir_def::Stmt::EventTrigger { event } => { + Some(Stmt::EventTrigger { event: self.resolve_event(event)? }) + } hir_def::Stmt::Assignment { val, assignment_kind, .. } => { let indirect = assignment_kind == syntax::ast::AssignOp::Indirect; let stmt = match self.infere.assignment_destination[&stmnt] { @@ -269,14 +283,45 @@ pub enum ContributeKind { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Stmt<'a> { Expr(ExprId), - EventControl { event: &'a Event, body: StmtId }, - Contribute { kind: ContributeKind, branch: BranchWrite, rhs: ExprId }, - Assignment { lhs: AssignmentLhs, rhs: ExprId }, - Block { body: &'a [StmtId] }, - If { cond: ExprId, then_branch: StmtId, else_branch: StmtId }, - ForLoop { init: StmtId, cond: ExprId, incr: StmtId, body: StmtId }, - WhileLoop { cond: ExprId, body: StmtId }, - Case { discr: ExprId, case_arms: &'a [Case] }, // TODO lint on unreachable + EventControl { + event: &'a Event, + body: StmtId, + }, + /// VAMS-2023 5.10.4: `-> ev;` + EventTrigger { + event: NamedEvent, + }, + Contribute { + kind: ContributeKind, + branch: BranchWrite, + rhs: ExprId, + }, + Assignment { + lhs: AssignmentLhs, + rhs: ExprId, + }, + Block { + body: &'a [StmtId], + }, + If { + cond: ExprId, + then_branch: StmtId, + else_branch: StmtId, + }, + ForLoop { + init: StmtId, + cond: ExprId, + incr: StmtId, + body: StmtId, + }, + WhileLoop { + cond: ExprId, + body: StmtId, + }, + Case { + discr: ExprId, + case_arms: &'a [Case], + }, // TODO lint on unreachable } impl Stmt<'_> { #[inline] diff --git a/openvaf/hir/src/lib.rs b/openvaf/hir/src/lib.rs index 788548d6..5cd3919d 100644 --- a/openvaf/hir/src/lib.rs +++ b/openvaf/hir/src/lib.rs @@ -21,7 +21,7 @@ pub use hir_def::expr::CaseCond; pub use hir_def::nameres::diagnostics::PathResolveError; use hir_def::nameres::{DefMap, LocalScopeId, ScopeDefItem}; use hir_def::{ - AliasParamId, BlockId, BlockLoc, BranchId, DefWithBodyId, DisciplineId, FunctionId, + AliasParamId, BlockId, BlockLoc, BranchId, DefWithBodyId, DisciplineId, EventId, FunctionId, LocalFunctionArgId, Lookup, ModuleBodyKind, ModuleId, ModuleLoc, NatureAttrId, NatureId, NodeId, ParamId, VarId, }; @@ -212,6 +212,25 @@ impl Block { } } +/// A named event (VAMS-2023 5.10.4): `event ana_event;` +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct NamedEvent { + pub(crate) id: EventId, +} + +stdx::impl_debug! { + match NamedEvent{ + NamedEvent{ id } => "{id:?}"; + } +} + +impl NamedEvent { + pub fn name(self, db: &CompilationDB) -> String { + let loc = self.id.lookup(db); + loc.item_tree(db)[loc.id].name.to_string() + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Function { id: FunctionId, @@ -364,6 +383,7 @@ impl Scope { ScopeDef::AliasParameter(AliasParameter { id }) } ScopeDefItem::BranchId(id) => ScopeDef::Branch(Branch { id }), + ScopeDefItem::EventId(id) => ScopeDef::NamedEvent(NamedEvent { id }), ScopeDefItem::FunctionId(id) => ScopeDef::Function(Function { id }), // implementation details ScopeDefItem::BuiltIn(_) @@ -649,5 +669,6 @@ pub enum ScopeDef { Parameter(Parameter), AliasParameter(AliasParameter), Branch(Branch), + NamedEvent(NamedEvent), Function(Function), } diff --git a/openvaf/hir_def/src/body/lower.rs b/openvaf/hir_def/src/body/lower.rs index f8798ec9..29af676b 100644 --- a/openvaf/hir_def/src/body/lower.rs +++ b/openvaf/hir_def/src/body/lower.rs @@ -175,6 +175,10 @@ impl LowerCtx<'_> { } ast::Stmt::CaseStmt(stmt) => self.collect_case_stmt(stmt), ast::Stmt::EventStmt(stmt) => return self.collect_event_stmt(stmt), + // VAMS-2023 5.10.4: `-> event_identifier;` + ast::Stmt::EventTriggerStmt(stmt) => { + Stmt::EventTrigger { event: self.collect_opt_expr(stmt.expr()) } + } ast::Stmt::BlockStmt(stmt) => self.collect_block(stmt), }; self.alloc_stmt(s, AstPtr::new(&stmt), stmt.attrs()) @@ -186,10 +190,17 @@ impl LowerCtx<'_> { } else if event_stmt.final_step_token().is_some() { GlobalEvent::FinalStep } else { - // Monitored event (`@(cross(...))` / `@(timer(...))`): preserve it so MIR + // A bare path is a named event (VAMS-2023 5.10.4); everything else is a + // monitored event (`@(cross(...))` / `@(timer(...))`), preserved so MIR // lowering can give the variables it assigns cross-timestep retention. + let event = match event_stmt.event() { + Some(ast::Expr::PathExpr(path)) => { + Event::Named { event: self.collect_expr(ast::Expr::PathExpr(path)) } + } + _ => Event::Cross, + }; let body = self.collect_opt_stmt(event_stmt.stmt()); - let stmt = Stmt::EventControl { event: Event::Cross, body }; + let stmt = Stmt::EventControl { event, body }; return self.alloc_stmt( stmt, AstPtr::new(event_stmt).cast().unwrap(), diff --git a/openvaf/hir_def/src/body/pretty.rs b/openvaf/hir_def/src/body/pretty.rs index 9e153b99..a38c3897 100644 --- a/openvaf/hir_def/src/body/pretty.rs +++ b/openvaf/hir_def/src/body/pretty.rs @@ -68,6 +68,11 @@ impl Printer<'_> { wln!(self, "@({:?})", event); self.pretty_print_stmt(body) } + Stmt::EventTrigger { event } => { + w!(self, "-> "); + self.pretty_print_expr(event); + wln!(self, ";"); + } Stmt::Assignment { dst, val, assignment_kind } => { self.pretty_print_expr(dst); w!(self, "{:?}", assignment_kind); diff --git a/openvaf/hir_def/src/db.rs b/openvaf/hir_def/src/db.rs index cc03cdd3..824c3228 100644 --- a/openvaf/hir_def/src/db.rs +++ b/openvaf/hir_def/src/db.rs @@ -13,9 +13,9 @@ use crate::item_tree::ItemTree; use crate::nameres::{DefMap, ScopeOrigin}; use crate::{ AliasParamId, AliasParamLoc, BlockId, BlockLoc, BranchId, BranchLoc, DefWithBodyId, - DisciplineAttrId, DisciplineAttrLoc, DisciplineId, DisciplineLoc, FunctionArgId, - FunctionArgLoc, FunctionId, FunctionLoc, ModuleId, ModuleLoc, NatureAttrId, NatureAttrLoc, - NatureId, NatureLoc, NodeId, NodeLoc, ParamId, ParamLoc, VarId, VarLoc, + DisciplineAttrId, DisciplineAttrLoc, DisciplineId, DisciplineLoc, EventId, EventLoc, + FunctionArgId, FunctionArgLoc, FunctionId, FunctionLoc, ModuleId, ModuleLoc, NatureAttrId, + NatureAttrLoc, NatureId, NatureLoc, NodeId, NodeLoc, ParamId, ParamLoc, VarId, VarLoc, }; #[salsa::query_group(InternDatabase)] @@ -35,6 +35,8 @@ pub trait InternDB: BaseDB { #[salsa::interned] fn intern_branch(&self, loc: BranchLoc) -> BranchId; #[salsa::interned] + fn intern_event(&self, loc: EventLoc) -> EventId; + #[salsa::interned] fn intern_function(&self, loc: FunctionLoc) -> FunctionId; #[salsa::interned] fn intern_nature_attr(&self, loc: NatureAttrLoc) -> NatureAttrId; diff --git a/openvaf/hir_def/src/expr.rs b/openvaf/hir_def/src/expr.rs index 0e617a9d..6a68d497 100644 --- a/openvaf/hir_def/src/expr.rs +++ b/openvaf/hir_def/src/expr.rs @@ -133,13 +133,42 @@ pub enum Stmt { Missing, Empty, Expr(ExprId), - EventControl { event: Event, body: StmtId }, - Assignment { dst: ExprId, val: ExprId, assignment_kind: ast::AssignOp }, - Block { /*scope: Option,*/ body: Vec }, - If { cond: ExprId, then_branch: StmtId, else_branch: StmtId }, - ForLoop { init: StmtId, cond: ExprId, incr: StmtId, body: StmtId }, - WhileLoop { cond: ExprId, body: StmtId }, - Case { discr: ExprId, case_arms: Vec }, // TODO lint on unreachable + EventControl { + event: Event, + body: StmtId, + }, + Assignment { + dst: ExprId, + val: ExprId, + assignment_kind: ast::AssignOp, + }, + Block { + /*scope: Option,*/ body: Vec, + }, + If { + cond: ExprId, + then_branch: StmtId, + else_branch: StmtId, + }, + ForLoop { + init: StmtId, + cond: ExprId, + incr: StmtId, + body: StmtId, + }, + WhileLoop { + cond: ExprId, + body: StmtId, + }, + Case { + discr: ExprId, + case_arms: Vec, + }, // TODO lint on unreachable + /// VAMS-2023 5.10.4: `-> event_identifier;`. `event` is the path expression + /// naming the event so it is resolved like any other reference. + EventTrigger { + event: ExprId, + }, } #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] @@ -159,6 +188,11 @@ pub enum Event { /// A monitored analog event such as `@(cross(...))` / `@(timer(...))`. Variables /// assigned inside its body are given cross-timestep retention during lowering. Cross, + /// A named event (VAMS-2023 5.10.4): `@(ana_event)`. `event` is the path + /// expression naming the event. + Named { + event: ExprId, + }, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -177,7 +211,13 @@ impl Stmt { #[inline] pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { match *self { - Stmt::Empty | Stmt::Missing | Stmt::Block { .. } | Stmt::EventControl { .. } => (), + Stmt::Empty | Stmt::Missing | Stmt::Block { .. } => (), + Stmt::EventControl { ref event, .. } => { + if let Event::Named { event } = *event { + f(event) + } + } + Stmt::EventTrigger { event } => f(event), Stmt::If { cond: expr, .. } | Stmt::ForLoop { cond: expr, .. } | Stmt::WhileLoop { cond: expr, .. } @@ -202,7 +242,11 @@ impl Stmt { #[inline] pub fn walk_child_stmts(&self, mut f: impl FnMut(StmtId)) { match *self { - Stmt::Expr(_) | Stmt::Assignment { .. } | Stmt::Missing | Stmt::Empty => (), + Stmt::Expr(_) + | Stmt::Assignment { .. } + | Stmt::Missing + | Stmt::Empty + | Stmt::EventTrigger { .. } => (), Stmt::WhileLoop { body, .. } | Stmt::EventControl { body, .. } => f(body), Stmt::If { then_branch: true_stmt, else_branch: false_stmt, .. } => { f(true_stmt); diff --git a/openvaf/hir_def/src/item_tree.rs b/openvaf/hir_def/src/item_tree.rs index f281308a..6a586d98 100644 --- a/openvaf/hir_def/src/item_tree.rs +++ b/openvaf/hir_def/src/item_tree.rs @@ -73,6 +73,7 @@ impl ItemTree { ports, branches, functions, + events, } = &mut self.data; modules.shrink_to_fit(); disciplines.shrink_to_fit(); @@ -85,6 +86,7 @@ impl ItemTree { ports.shrink_to_fit(); branches.shrink_to_fit(); functions.shrink_to_fit(); + events.shrink_to_fit(); nature_attrs.shrink_to_fit(); discipline_attrs.shrink_to_fit(); } @@ -109,6 +111,7 @@ pub struct ItemTreeData { pub ports: Arena, pub branches: Arena, pub functions: Arena, + pub events: Arena, } /// Trait implemented by all item nodes in the item tree. @@ -227,6 +230,7 @@ item_tree_nodes! { Net in nets -> ast::NetDecl, Port in ports -> ast::PortDecl, Branch in branches -> ast::BranchDecl, + Event in events -> ast::EventDecl, Function in functions -> ast::Function, NatureAttr in nature_attrs -> ast::NatureAttr, DisciplineAttr in discipline_attrs -> ast::DisciplineAttr, @@ -250,6 +254,7 @@ pub enum ModuleItem { Branch(ItemTreeId), Node(LocalNodeId), Function(ItemTreeId), + Event(ItemTreeId), } impl_from_typed! ( @@ -259,7 +264,8 @@ impl_from_typed! ( Variable(ItemTreeId), Branch(ItemTreeId), Node(LocalNodeId), - Function(ItemTreeId) for ModuleItem + Function(ItemTreeId), + Event(ItemTreeId) for ModuleItem ); #[derive(Debug, Eq, PartialEq, Clone)] @@ -391,6 +397,14 @@ pub struct Branch { pub ast_id: AstId, } +/// A named event declaration (VAMS-2023 5.10.4): `event ana_event;` +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct Event { + pub name: Name, + pub name_idx: usize, + pub ast_id: AstId, +} + #[derive(Debug, Eq, PartialEq, Clone)] pub struct Block { pub name: Option, diff --git a/openvaf/hir_def/src/item_tree/lower.rs b/openvaf/hir_def/src/item_tree/lower.rs index c13eaa0d..2697d76a 100644 --- a/openvaf/hir_def/src/item_tree/lower.rs +++ b/openvaf/hir_def/src/item_tree/lower.rs @@ -10,9 +10,9 @@ use syntax::{match_ast, AstNode, ConstExprValue, WalkEvent}; use typed_index_collections::TiVec; use super::{ - Block, Branch, BranchKind, Discipline, DisciplineAttr, DisciplineAttrKind, Domain, Function, - FunctionArg, FunctionItem, ItemTree, ItemTreeId, Module, ModuleItem, Nature, NatureAttr, - NatureRef, NatureRefKind, Net, Node, Param, Port, RootItem, Var, + Block, Branch, BranchKind, Discipline, DisciplineAttr, DisciplineAttrKind, Domain, Event, + Function, FunctionArg, FunctionItem, ItemTree, ItemTreeId, Module, ModuleItem, Nature, + NatureAttr, NatureRef, NatureRefKind, Net, Node, Param, Port, RootItem, Var, }; // use tracing::trace; use crate::db::HirDefDB; @@ -309,6 +309,7 @@ impl Ctx { // entity. The genvar `for` loop is unrolled during body lowering, so // there is nothing to lower here. ast::ModuleItem::GenvarDecl(_) => {} + ast::ModuleItem::EventDecl(decl) => self.lower_event_decl(decl, dst), }; } } @@ -369,6 +370,16 @@ impl Ctx { } } + /// VAMS-2023 5.10.4: `event ana_event, dig_event;` + fn lower_event_decl(&mut self, decl: ast::EventDecl, dst: &mut Vec) { + let ast_id = self.source_ast_id_map.ast_id(&decl); + for (name_idx, name) in decl.names().enumerate() { + let event = Event { name: name.as_name(), name_idx, ast_id }; + let event = self.tree.data.events.push_and_get_key(event); + dst.push(event.into()) + } + } + fn lower_branch(&mut self, decl: ast::BranchDecl, dst: &mut Vec) { let ast_id = self.source_ast_id_map.ast_id(&decl); let kind = decl diff --git a/openvaf/hir_def/src/item_tree/pretty.rs b/openvaf/hir_def/src/item_tree/pretty.rs index 5bbc4590..98d75643 100644 --- a/openvaf/hir_def/src/item_tree/pretty.rs +++ b/openvaf/hir_def/src/item_tree/pretty.rs @@ -102,6 +102,9 @@ impl<'a> Printer<'a> { let branch = &self.tree[branch]; wln!(self, "branch {} = {:?}", branch.name, branch.kind) } + ModuleItem::Event(event) => { + wln!(self, "event {}", self.tree[event].name) + } ModuleItem::Node(node) => { let node = &module.nodes[node]; let (is_input, is_output) = node.direction(self.tree); diff --git a/openvaf/hir_def/src/lib.rs b/openvaf/hir_def/src/lib.rs index 42e16a28..61038904 100644 --- a/openvaf/hir_def/src/lib.rs +++ b/openvaf/hir_def/src/lib.rs @@ -29,8 +29,9 @@ pub use crate::data::FunctionArg; use crate::db::HirDefDB; pub use crate::expr::{Case, Expr, ExprId, Literal, Stmt, StmtId}; pub use crate::item_tree::{ - AliasParam, Branch, BranchKind, Discipline, DisciplineAttr, Function, ItemTree, ItemTreeId, - ItemTreeNode, Module, Nature, NatureAttr, NatureRef, NatureRefKind, NodeTypeDecl, Param, Var, + AliasParam, Branch, BranchKind, Discipline, DisciplineAttr, Event, Function, ItemTree, + ItemTreeId, ItemTreeNode, Module, Nature, NatureAttr, NatureRef, NatureRefKind, NodeTypeDecl, + Param, Var, }; use crate::nameres::ScopeDefItem; pub use crate::path::Path; @@ -270,6 +271,9 @@ impl_intern!(NatureId, NatureLoc, intern_nature, lookup_intern_nature); pub type BranchLoc = ItemLoc; impl_intern!(BranchId, BranchLoc, intern_branch, lookup_intern_branch); +pub type EventLoc = ItemLoc; +impl_intern!(EventId, EventLoc, intern_event, lookup_intern_event); + pub type VarLoc = ItemLoc; impl_intern!(VarId, VarLoc, intern_var, lookup_intern_var); diff --git a/openvaf/hir_def/src/nameres.rs b/openvaf/hir_def/src/nameres.rs index a49591c3..903ff799 100644 --- a/openvaf/hir_def/src/nameres.rs +++ b/openvaf/hir_def/src/nameres.rs @@ -16,8 +16,8 @@ use crate::builtin::{insert_builtin_scope, BuiltIn, ParamSysFun}; use crate::db::HirDefDB; use crate::nameres::diagnostics::PathResolveError; use crate::{ - AliasParamId, BlockId, BranchId, DisciplineId, FunctionArgId, FunctionId, Lookup, ModuleId, - NatureAttrId, NatureId, NodeId, ParamId, VarId, + AliasParamId, BlockId, BranchId, DisciplineId, EventId, FunctionArgId, FunctionId, Lookup, + ModuleId, NatureAttrId, NatureId, NodeId, ParamId, VarId, }; mod collect; @@ -86,6 +86,7 @@ pub enum ScopeDefItem { ParamSysFun(ParamSysFun), AliasParamId(AliasParamId), BranchId(BranchId), + EventId(EventId), FunctionId(FunctionId), BuiltIn(BuiltIn), FunctionReturn(FunctionId), @@ -104,6 +105,7 @@ impl ScopeDefItem { ScopeDefItem::VarId(var) => var.lookup(db).ast_id(db).into(), ScopeDefItem::ParamId(param) => param.lookup(db).ast_id(db).into(), ScopeDefItem::BranchId(branch) => branch.lookup(db).ast_id(db).into(), + ScopeDefItem::EventId(event) => event.lookup(db).ast_id(db).into(), ScopeDefItem::FunctionReturn(fun) | ScopeDefItem::FunctionId(fun) => { fun.lookup(db).ast_id(db).into() } @@ -166,6 +168,17 @@ impl ScopeDefItem { .syntax() .text_range() } + ScopeDefItem::EventId(event) => { + let event = event.lookup(db); + let pos = event.item_tree(db)[event.id].name_idx; + ast_id_map + .get(event.ast_id(db)) + .to_node(parse.tree().syntax()) + .names() + .nth(pos)? + .syntax() + .text_range() + } ScopeDefItem::FunctionReturn(fun) | ScopeDefItem::FunctionId(fun) => ast_id_map .get(fun.lookup(db).ast_id(db)) .to_node(parse.tree().syntax()) @@ -204,6 +217,7 @@ impl_from! { VarId, ParamId, BranchId, + EventId, FunctionId, NatureAttrId, AliasParamId, @@ -247,6 +261,7 @@ scope_item_kinds! { ParamSysFun => "hierarchical parameter system function", AliasParamId => "parameter", BranchId => "branch", + EventId => "named event", FunctionId => "function", BuiltIn => "function", FunctionArgId => "function argument" diff --git a/openvaf/hir_def/src/nameres/collect.rs b/openvaf/hir_def/src/nameres/collect.rs index 320586cf..d4964d36 100644 --- a/openvaf/hir_def/src/nameres/collect.rs +++ b/openvaf/hir_def/src/nameres/collect.rs @@ -270,6 +270,9 @@ impl DefCollector<'_> { ModuleItem::Branch(id) => { self.insert_item_decl(scope, self.tree[id].name.clone(), id) } + ModuleItem::Event(id) => { + self.insert_item_decl(scope, self.tree[id].name.clone(), id) + } ModuleItem::Parameter(id) => { self.insert_item_decl(scope, self.tree[id].name.clone(), id) } diff --git a/openvaf/hir_lower/src/body.rs b/openvaf/hir_lower/src/body.rs index 48b4f085..e002782d 100644 --- a/openvaf/hir_lower/src/body.rs +++ b/openvaf/hir_lower/src/body.rs @@ -98,7 +98,10 @@ impl<'c1, 'c2> BodyLoweringCtx<'_, 'c1, 'c2> { AssignmentLhs::ArrayElement { var, .. } => dst.push(var), _ => {} }, - Stmt::Assignment { .. } | Stmt::Expr(_) | Stmt::Contribute { .. } => {} + Stmt::Assignment { .. } + | Stmt::Expr(_) + | Stmt::Contribute { .. } + | Stmt::EventTrigger { .. } => {} Stmt::EventControl { event, body } => { let inner = in_cross || matches!(event, Event::Cross); self.collect_cross_assigned(body, inner, dst); diff --git a/openvaf/hir_lower/src/ctx.rs b/openvaf/hir_lower/src/ctx.rs index bf51deaf..d8ac9094 100644 --- a/openvaf/hir_lower/src/ctx.rs +++ b/openvaf/hir_lower/src/ctx.rs @@ -102,6 +102,8 @@ impl<'a, 'c> LoweringCtx<'a, 'c> { PlaceKind::ImplicitResidual { .. } | PlaceKind::Contribute { .. } => F_ZERO, PlaceKind::CollapseImplicitEquation(_) => TRUE, PlaceKind::IsVoltageSrc(_) => FALSE, + // no named event has been triggered yet when the block starts + PlaceKind::NamedEvent(_) => FALSE, PlaceKind::BoundStep => INFINITY, }; let entry = self.func.func.layout.entry_block().unwrap(); diff --git a/openvaf/hir_lower/src/lib.rs b/openvaf/hir_lower/src/lib.rs index 91b0b2b5..a3b0e4d6 100644 --- a/openvaf/hir_lower/src/lib.rs +++ b/openvaf/hir_lower/src/lib.rs @@ -185,6 +185,10 @@ pub enum PlaceKind { ParamMin(Parameter), ParamMax(Parameter), BoundStep, + /// Whether a named event (VAMS-2023 5.10.4) has been triggered during this + /// evaluation of the analog block. `false` at the start of the block, set by + /// `-> ev;` and read by `@(ev)`. + NamedEvent(hir::NamedEvent), } impl PlaceKind { @@ -204,7 +208,9 @@ impl PlaceKind { PlaceKind::ParamMin(param) | PlaceKind::ParamMax(param) | PlaceKind::Param(param) => { param.ty(db) } - PlaceKind::IsVoltageSrc(_) | PlaceKind::CollapseImplicitEquation(_) => Type::Bool, + PlaceKind::IsVoltageSrc(_) + | PlaceKind::CollapseImplicitEquation(_) + | PlaceKind::NamedEvent(_) => Type::Bool, } } diff --git a/openvaf/hir_lower/src/stmt.rs b/openvaf/hir_lower/src/stmt.rs index a35b29cf..9bafcd34 100644 --- a/openvaf/hir_lower/src/stmt.rs +++ b/openvaf/hir_lower/src/stmt.rs @@ -28,10 +28,30 @@ impl BodyLoweringCtx<'_, '_, '_> { self.ctx.in_initial_step = true; self.lower_stmt(body); self.ctx.in_initial_step = prev; + } else if let hir::Event::Named { event } = *event { + // `@(ev)` runs its body only if `ev` was triggered earlier in + // this evaluation of the analog block (VAMS-2023 5.10.4). + match self.body.resolve_event(event) { + Some(event) => { + let cond = self.ctx.use_place(PlaceKind::NamedEvent(event)); + self.ctx.make_cond(cond, |ctx, branch| { + if branch { + BodyLoweringCtx { body: self.body, path: self.path, ctx } + .lower_stmt(body) + } + }); + } + // unresolved event; already diagnosed + None => self.lower_stmt(body), + } } else { self.lower_stmt(body); } } + // `-> ev;` records that the event occurred in this evaluation + Stmt::EventTrigger { event } => { + self.ctx.def_place(PlaceKind::NamedEvent(event), mir::TRUE); + } Stmt::Assignment { lhs, rhs } => { // A retained variable's `@(initial_step)` reset is its initial value // (already loaded from the retained state); skip it so it is not diff --git a/openvaf/hir_ty/src/inference.rs b/openvaf/hir_ty/src/inference.rs index 6c8c34c1..36b0d39d 100755 --- a/openvaf/hir_ty/src/inference.rs +++ b/openvaf/hir_ty/src/inference.rs @@ -6,7 +6,7 @@ use ahash::AHashMap; use arena::ArenaMap; use hir_def::body::Body; use hir_def::db::HirDefDB; -use hir_def::expr::{CaseCond, Literal}; +use hir_def::expr::{CaseCond, Event, Literal}; use hir_def::nameres::diagnostics::PathResolveError; use hir_def::nameres::{NatureAccess, ResolvedPath, ScopeDefItem, ScopeDefItemKind}; use hir_def::{ @@ -170,6 +170,13 @@ impl Ctx<'_> { } } } + // VAMS-2023 5.10.4: `-> ev;` and `@(ev)` both name a declared event + Stmt::EventTrigger { event } + | Stmt::EventControl { event: Event::Named { event }, .. } => { + if let Some(ty) = self.infere_expr(stmt, event) { + self.expect::(event, None, ty, Cow::Borrowed(&[TyRequirement::Event])); + } + } _ => (), }; @@ -348,6 +355,7 @@ impl Ctx<'_> { } }, ScopeDefItem::BranchId(branch) => Ty::Branch(branch), + ScopeDefItem::EventId(event) => Ty::Event(event), ScopeDefItem::BuiltIn(_) | ScopeDefItem::NatureAccess(_) => Ty::BuiltInFunction, ScopeDefItem::FunctionId(fun) => Ty::UserFunction(fun), diff --git a/openvaf/hir_ty/src/types.rs b/openvaf/hir_ty/src/types.rs index cb803403..c1b7fa3c 100644 --- a/openvaf/hir_ty/src/types.rs +++ b/openvaf/hir_ty/src/types.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::ops::Deref; use hir_def::{ - BranchId, DisciplineId, FunctionId, LocalFunctionArgId, NatureAttrId, NatureId, NodeId, - ParamId, Type, VarId, + BranchId, DisciplineId, EventId, FunctionId, LocalFunctionArgId, NatureAttrId, NatureId, + NodeId, ParamId, Type, VarId, }; use stdx::{impl_display, impl_idx_from, pretty}; @@ -20,6 +20,7 @@ pub enum TyRequirement { Param(Type), AnyParam, Branch, + Event, Literal(Type), Function, } @@ -65,6 +66,7 @@ impl_display! { TyRequirement::Literal(ty) => "{} literal", ty; TyRequirement::PortFlow => "port-flow reference"; TyRequirement::Branch => "branch reference"; + TyRequirement::Event => "named event"; TyRequirement::Function => "function"; } @@ -84,6 +86,7 @@ pub enum Ty { Literal(Type), InfLiteral, Branch(BranchId), + Event(EventId), Scope, BuiltInFunction, UserFunction(FunctionId), @@ -104,6 +107,7 @@ impl_display! { Ty::Param(ty,_) => "{} parameter ref", ty; Ty::Literal(ty) => "{} literal", ty; Ty::Branch(_) => "branch reference"; + Ty::Event(_) => "named event"; Ty::BuiltInFunction => "(builtin) function"; Ty::UserFunction(_) => "(user-defined) function"; Ty::Scope => "scope"; @@ -178,7 +182,8 @@ impl Ty { | (Ty::Nature(_), TyRequirement::Nature) | (Ty::Param(_, _), TyRequirement::AnyParam) | (Ty::UserFunction(_), TyRequirement::Function) - | (Ty::Branch(_), TyRequirement::Branch) => true, + | (Ty::Branch(_), TyRequirement::Branch) + | (Ty::Event(_), TyRequirement::Event) => true, ( Ty::Val(ty1) diff --git a/openvaf/hir_ty/src/validation.rs b/openvaf/hir_ty/src/validation.rs index c2ff481a..c10d31ea 100644 --- a/openvaf/hir_ty/src/validation.rs +++ b/openvaf/hir_ty/src/validation.rs @@ -226,6 +226,26 @@ impl Diagnostic for BodyValidationDiagnosticWrapped<'_> { .to_owned(), ]) } + BodyValidationDiagnostic::IllegalEventTrigger { stmt, ctx } => { + let FileSpan { range, file } = self.parse.to_file_span( + self.body_sm.stmt_map_back[stmt].as_ref().unwrap().range(), + self.sm, + ); + + Report::error() + .with_message(format!("event triggers are not allowed in {}", ctx)) + .with_labels(vec![Label { + style: LabelStyle::Secondary, + file_id: file, + range: range.into(), + message: "not allowed here".to_owned(), + }]) + .with_notes(vec![ + "help: in an analog block an event may only be triggered from an event \ + statement such as '@(timer(1n)) -> ev;'" + .to_owned(), + ]) + } BodyValidationDiagnostic::WriteToInputArg { expr, arg } => { let FileSpan { range, file } = self.expr_src(expr); let arg_name = arg.name(self.db.upcast()); diff --git a/openvaf/hir_ty/src/validation/body.rs b/openvaf/hir_ty/src/validation/body.rs index 5ed80c18..087bf957 100644 --- a/openvaf/hir_ty/src/validation/body.rs +++ b/openvaf/hir_ty/src/validation/body.rs @@ -55,6 +55,13 @@ pub enum BodyValidationDiagnostic { ctx: BodyCtx, }, + /// VAMS-2023 5.10.4: in an analog context `-> ev;` is an + /// `analog_event_statement`, so it may only appear under an event control. + IllegalEventTrigger { + stmt: StmtId, + ctx: BodyCtx, + }, + WriteToInputArg { expr: ExprId, arg: FunctionArgLoc, @@ -122,6 +129,7 @@ impl BodyValidationDiagnostic { infer: &infere, diagnostics: Vec::new(), ctx, + in_event_control: false, non_const_dominator: Box::default(), non_trivial_branches: HashSet::default(), trivial_probes: HashMap::default(), @@ -199,6 +207,9 @@ struct BodyValidator<'a> { infer: &'a InferenceResult, diagnostics: Vec, ctx: BodyCtx, + /// Whether the statement being validated is (transitively) the body of an + /// event control. Unlike `ctx` this survives entering a conditional. + in_event_control: bool, non_const_dominator: Box<[ExprId]>, non_trivial_branches: HashSet, trivial_probes: HashMap>, @@ -225,7 +236,9 @@ impl BodyValidator<'_> { } Stmt::EventControl { body, .. } => { let old = replace(&mut self.ctx, BodyCtx::EventControl); + let old_event = replace(&mut self.in_event_control, true); self.validate_stmt(body); + self.in_event_control = old_event; self.ctx = old; return; } @@ -236,6 +249,17 @@ impl BodyValidator<'_> { Stmt::Missing | Stmt::Empty => return, + // VAMS-2023 5.10.4: `-> ev;` is an `analog_event_statement`; it is not + // part of `analog_statement`, so in the analog context it may only + // appear inside an event control (`@(timer(1n)) -> ev;`). + Stmt::EventTrigger { .. } => { + if !self.in_event_control && self.ctx != BodyCtx::ProceduralBlock { + self.diagnostics + .push(BodyValidationDiagnostic::IllegalEventTrigger { stmt, ctx: self.ctx }) + } + return; + } + Stmt::Expr(e) => { self.validate_expr(e, stmt); return; diff --git a/openvaf/lexer/src/lib.rs b/openvaf/lexer/src/lib.rs index cfd076b9..d02f5b76 100644 --- a/openvaf/lexer/src/lib.rs +++ b/openvaf/lexer/src/lib.rs @@ -186,6 +186,12 @@ impl Cursor<'_> { Contribute } + // VAMS-2023 5.10.4: `-> event_identifier;` triggers a named event + '-' if self.first() == '>' => { + self.bump(); + Trigger + } + '*' if self.first() == '*' => { self.bump(); Pow diff --git a/openvaf/parser/src/grammar/items/module.rs b/openvaf/parser/src/grammar/items/module.rs index d6788d4f..d295e379 100644 --- a/openvaf/parser/src/grammar/items/module.rs +++ b/openvaf/parser/src/grammar/items/module.rs @@ -11,6 +11,7 @@ const MODULE_ITEM_RECOVERY: TokenSet = DIRECTION_TS.union(TokenSet::new(&[ REAL_KW, INTEGER_KW, GENVAR_KW, + EVENT_KW, PARAMETER_KW, LOCALPARAM_KW, ENDMODULE_KW, @@ -174,6 +175,7 @@ fn module_items(p: &mut Parser) { } INTEGER_KW | REAL_KW | STRING_KW => var_decl(p, m), GENVAR_KW => genvar_decl(p, m), + EVENT_KW => event_decl(p, m), INPUT_KW | OUTPUT_KW | INOUT_KW => port_decl::(p, m), _ => { error_range = if let Some(error_range) = error_range { @@ -210,6 +212,14 @@ fn genvar_decl(p: &mut Parser, m: Marker) { m.complete(p, GENVAR_DECL); } +/// VAMS-2023 5.10.4: `event ana_event, dig_event;` +fn event_decl(p: &mut Parser, m: Marker) { + p.bump(EVENT_KW); + decl_list(p, T![;], decl_name, MODULE_ITEM_OR_ATTR_RECOVERY); + p.eat(T![;]); + m.complete(p, EVENT_DECL); +} + fn net_decl(p: &mut Parser, m: Marker) { //direction and type ar both optional since only one is required if NET_TYPE_FIRST { diff --git a/openvaf/parser/src/grammar/stmts.rs b/openvaf/parser/src/grammar/stmts.rs index 42a89380..b63f5e01 100644 --- a/openvaf/parser/src/grammar/stmts.rs +++ b/openvaf/parser/src/grammar/stmts.rs @@ -1,7 +1,18 @@ use super::*; - -pub(super) const STMT_TS: TokenSet = - TokenSet::new(&[IF_KW, WHILE_KW, FOR_KW, CASE_KW, BEGIN_KW, T![;], IDENT, SYSFUN, T![@]]); +use crate::grammar::paths::{path, PATH_SEGMENT_TS}; + +pub(super) const STMT_TS: TokenSet = TokenSet::new(&[ + IF_KW, + WHILE_KW, + FOR_KW, + CASE_KW, + BEGIN_KW, + T![;], + IDENT, + SYSFUN, + T![@], + T![->], +]); pub(super) const STMT_RECOVER: TokenSet = TokenSet::new(&[EOF, ENDMODULE_KW, T![;]]); pub(super) const STMT_ATTR_RECOVER: TokenSet = @@ -21,6 +32,7 @@ pub(super) fn stmt(p: &mut Parser, m: Marker, expected: TokenSet, recover: Token CASE_KW => case_stmt(p, m), BEGIN_KW => block_stmt(p, m), T![@] => event_stmt(p, m), + T![->] => event_trigger_stmt(p, m), IDENT | SYSFUN => expr_or_assign_stmt::(p, m), _ => { m.abandon(p); @@ -81,8 +93,11 @@ fn event_stmt(p: &mut Parser, m: Marker) { } else { // Monitored events: `@(cross(expr, dir, tol))`, `@(timer(...))`, ... parsed as // a call expression. Currently the event condition is not used for scheduling - // (the guarded body is always evaluated, see hir_lower EventControl), so we - // only need to accept and consume it. + // (the guarded body is always evaluated, see hir_lower EventControl). + // + // A bare identifier here is a named event (VAMS-2023 5.10.4) and *is* + // resolved; both forms are parsed as an expression and told apart during + // body lowering. expr(p); } p.expect(T![')']); @@ -90,6 +105,22 @@ fn event_stmt(p: &mut Parser, m: Marker) { m.complete(p, EVENT_STMT); } +/// VAMS-2023 5.10.4: `-> event_identifier;` +fn event_trigger_stmt(p: &mut Parser, m: Marker) { + p.bump(T![->]); + if p.at_ts(PATH_SEGMENT_TS) { + // wrapped in a path expression so the name is resolved by the same + // machinery as every other reference + let path = path(p); + path.precede(p).complete(p, PATH_EXPR); + } else { + let err = p.unexpected_tokens_msg(vec![PATH]); + p.err_recover(err, STMT_RECOVER); + } + p.expect(T![;]); + m.complete(p, EVENT_TRIGGER_STMT); +} + fn if_stmt(p: &mut Parser, m: Marker) { p.bump(IF_KW); p.expect(T!['(']); diff --git a/openvaf/syntax/src/ast/generated/nodes.rs b/openvaf/syntax/src/ast/generated/nodes.rs index 127af9e8..b22d1a9a 100644 --- a/openvaf/syntax/src/ast/generated/nodes.rs +++ b/openvaf/syntax/src/ast/generated/nodes.rs @@ -148,9 +148,20 @@ impl EventStmt { support::token(&self.syntax, T![final_step]) } pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + pub fn event(&self) -> Option { support::child(&self.syntax) } pub fn stmt(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EventTriggerStmt { + pub(crate) syntax: SyntaxNode, +} +impl ast::AttrsOwner for EventTriggerStmt {} +impl EventTriggerStmt { + pub fn trigger_token(&self) -> Option { support::token(&self.syntax, T![->]) } + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct BlockStmt { pub(crate) syntax: SyntaxNode, } @@ -489,6 +500,16 @@ impl GenvarDecl { pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EventDecl { + pub(crate) syntax: SyntaxNode, +} +impl ast::AttrsOwner for EventDecl {} +impl EventDecl { + pub fn event_token(&self) -> Option { support::token(&self.syntax, T![event]) } + pub fn names(&self) -> AstChildren { support::children(&self.syntax) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ModulePort { pub(crate) syntax: SyntaxNode, } @@ -610,6 +631,7 @@ pub enum Stmt { ForStmt(ForStmt), CaseStmt(CaseStmt), EventStmt(EventStmt), + EventTriggerStmt(EventTriggerStmt), BlockStmt(BlockStmt), } impl ast::AttrsOwner for Stmt {} @@ -644,6 +666,7 @@ pub enum ModuleItem { ParamDecl(ParamDecl), AliasParam(AliasParam), GenvarDecl(GenvarDecl), + EventDecl(EventDecl), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ModulePortKind { @@ -818,6 +841,17 @@ impl AstNode for EventStmt { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for EventTriggerStmt { + fn can_cast(kind: SyntaxKind) -> bool { kind == EVENT_TRIGGER_STMT } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for BlockStmt { fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK_STMT } fn cast(syntax: SyntaxNode) -> Option { @@ -1181,6 +1215,17 @@ impl AstNode for GenvarDecl { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for EventDecl { + fn can_cast(kind: SyntaxKind) -> bool { kind == EVENT_DECL } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for ModulePort { fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE_PORT } fn cast(syntax: SyntaxNode) -> Option { @@ -1383,6 +1428,9 @@ impl From for Stmt { impl From for Stmt { fn from(node: EventStmt) -> Stmt { Stmt::EventStmt(node) } } +impl From for Stmt { + fn from(node: EventTriggerStmt) -> Stmt { Stmt::EventTriggerStmt(node) } +} impl From for Stmt { fn from(node: BlockStmt) -> Stmt { Stmt::BlockStmt(node) } } @@ -1390,7 +1438,7 @@ impl AstNode for Stmt { fn can_cast(kind: SyntaxKind) -> bool { match kind { EMPTY_STMT | ASSIGN_STMT | EXPR_STMT | IF_STMT | WHILE_STMT | FOR_STMT | CASE_STMT - | EVENT_STMT | BLOCK_STMT => true, + | EVENT_STMT | EVENT_TRIGGER_STMT | BLOCK_STMT => true, _ => false, } } @@ -1404,6 +1452,7 @@ impl AstNode for Stmt { FOR_STMT => Stmt::ForStmt(ForStmt { syntax }), CASE_STMT => Stmt::CaseStmt(CaseStmt { syntax }), EVENT_STMT => Stmt::EventStmt(EventStmt { syntax }), + EVENT_TRIGGER_STMT => Stmt::EventTriggerStmt(EventTriggerStmt { syntax }), BLOCK_STMT => Stmt::BlockStmt(BlockStmt { syntax }), _ => return None, }; @@ -1419,6 +1468,7 @@ impl AstNode for Stmt { Stmt::ForStmt(it) => &it.syntax, Stmt::CaseStmt(it) => &it.syntax, Stmt::EventStmt(it) => &it.syntax, + Stmt::EventTriggerStmt(it) => &it.syntax, Stmt::BlockStmt(it) => &it.syntax, } } @@ -1546,11 +1596,14 @@ impl From for ModuleItem { impl From for ModuleItem { fn from(node: GenvarDecl) -> ModuleItem { ModuleItem::GenvarDecl(node) } } +impl From for ModuleItem { + fn from(node: EventDecl) -> ModuleItem { ModuleItem::EventDecl(node) } +} impl AstNode for ModuleItem { fn can_cast(kind: SyntaxKind) -> bool { match kind { BODY_PORT_DECL | NET_DECL | ANALOG_BEHAVIOUR | PROCEDURAL_BLOCK | FUNCTION - | BRANCH_DECL | VAR_DECL | PARAM_DECL | ALIAS_PARAM | GENVAR_DECL => true, + | BRANCH_DECL | VAR_DECL | PARAM_DECL | ALIAS_PARAM | GENVAR_DECL | EVENT_DECL => true, _ => false, } } @@ -1566,6 +1619,7 @@ impl AstNode for ModuleItem { PARAM_DECL => ModuleItem::ParamDecl(ParamDecl { syntax }), ALIAS_PARAM => ModuleItem::AliasParam(AliasParam { syntax }), GENVAR_DECL => ModuleItem::GenvarDecl(GenvarDecl { syntax }), + EVENT_DECL => ModuleItem::EventDecl(EventDecl { syntax }), _ => return None, }; Some(res) @@ -1582,6 +1636,7 @@ impl AstNode for ModuleItem { ModuleItem::ParamDecl(it) => &it.syntax, ModuleItem::AliasParam(it) => &it.syntax, ModuleItem::GenvarDecl(it) => &it.syntax, + ModuleItem::EventDecl(it) => &it.syntax, } } } @@ -1798,6 +1853,11 @@ impl std::fmt::Display for EventStmt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for EventTriggerStmt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for BlockStmt { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -1963,6 +2023,11 @@ impl std::fmt::Display for GenvarDecl { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for EventDecl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ModulePort { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/openvaf/syntax/veriloga.ungram b/openvaf/syntax/veriloga.ungram index cc50fd56..ccecb7d4 100644 --- a/openvaf/syntax/veriloga.ungram +++ b/openvaf/syntax/veriloga.ungram @@ -50,6 +50,7 @@ EmptyStmt | ForStmt | CaseStmt | EventStmt +| EventTriggerStmt | BlockStmt EmptyStmt = AttrList* ';' @@ -88,7 +89,10 @@ Case = EventStmt = - AttrList* '@' '(' ('initial_step' | 'final_step') ('(' sim_phases: ('str_lit' (',' 'str_lit')*) ')')? ')' Stmt + AttrList* '@' '(' ('initial_step' | 'final_step') ('(' sim_phases: ('str_lit' (',' 'str_lit')*) ')')? event:Expr? ')' Stmt + +EventTriggerStmt = + AttrList* '->' Expr ';' BlockStmt = @@ -201,6 +205,7 @@ ModuleItem = | ParamDecl | AliasParam | GenvarDecl +| EventDecl ModulePorts = '('ports: (ModulePort (',' ModulePort)*)? ')' ModulePort = kind: ModulePortKind @@ -212,6 +217,9 @@ PortRef = GenvarDecl = AttrList* 'genvar' (Name (',' Name)*) ';' +EventDecl = + AttrList* 'event' (Name (',' Name)*) ';' + AnalogBehaviour = AttrList* 'analog' 'initial'? Stmt diff --git a/openvaf/test_data/mir/named_events.mir b/openvaf/test_data/mir/named_events.mir new file mode 100644 index 00000000..32e6b555 --- /dev/null +++ b/openvaf/test_data/mir/named_events.mir @@ -0,0 +1,57 @@ +function %(v16, v17, v18, v19) { + // v1 = bconst false + // v2 = bconst true + v4 = iconst 0 + v5 = iconst 1 + v20 = fconst 0x1.0000000000000p-2 + block0: + br v1, block2, block3 + + block2: + jmp block4 + + block3: + jmp block4 + + block4: + v55 = phi [v5, block2], [v4, block3] +@000c v21 = fgt v19, v20 + br v21, block5, block6 + + block5: + jmp block7 + + block6: + jmp block7 + + block7: + v22 = phi [v2, block5], [v1, block6] +@0013 br v22, block8, block9 + + block8: + v34 = iadd v4, v5 + jmp block10 + + block9: + jmp block10 + + block10: + v63 = phi [v34, block8], [v4, block9] +@0018 br v2, block11, block12 + + block11: + v48 = iadd v4, v5 + jmp block13 + + block12: + jmp block13 + + block13: + v67 = phi [v48, block11], [v4, block12] + v60 = optbarrier v55 + v66 = optbarrier v63 + v69 = optbarrier v67 + jmp block1 + + block1: +} diff --git a/openvaf/test_data/mir/named_events.va b/openvaf/test_data/mir/named_events.va new file mode 100644 index 00000000..00915b46 --- /dev/null +++ b/openvaf/test_data/mir/named_events.va @@ -0,0 +1,36 @@ +// VAMS-2023 5.10.4 (Mantis 7809): named events are declared with `event`, +// triggered with `-> name;` from an analog event statement and detected with +// `@(name)`. +module named_events; + parameter real thresh = 0.5; + + event sampled; + event armed; + + integer early; + integer hits; + integer arm_count; + + analog begin + early = 0; + hits = 0; + arm_count = 0; + + // detected before anything can trigger it: the guard is false, so the + // body never runs + @(sampled) early = 1; + + // triggered from an event statement, under a condition + @(timer(1n)) begin + if (thresh > 0.25) + -> sampled; + end + + // unconditional trigger + @(timer(2n)) -> armed; + + // detected afterwards: runs iff the trigger executed + @(sampled) hits = hits + 1; + @(armed) arm_count = arm_count + 1; + end +endmodule diff --git a/openvaf/test_data/osdi/vams2023_events.snap b/openvaf/test_data/osdi/vams2023_events.snap new file mode 100644 index 00000000..fad50fbc --- /dev/null +++ b/openvaf/test_data/osdi/vams2023_events.snap @@ -0,0 +1,16 @@ +param "$mfactor" +units = "", desc = "Multiplier (Verilog-A $mfactor)", flags = ParameterFlags(PARA_KIND_INST) +param "r" +units = "", desc = "", flags = ParameterFlags(0x0) +param "period" +units = "", desc = "", flags = ParameterFlags(0x0) + +2 terminals +node "a" units = "V", runits = "A" +node "c" units = "V", runits = "A" +jacobian (a, a) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (a, c) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (c, a) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +jacobian (c, c) JacobianFlags(JACOBIAN_ENTRY_RESIST | JACOBIAN_ENTRY_REACT_CONST) +0 states +has bound_step false diff --git a/openvaf/test_data/ui/named_events.va b/openvaf/test_data/ui/named_events.va new file mode 100644 index 00000000..9a4a8c6b --- /dev/null +++ b/openvaf/test_data/ui/named_events.va @@ -0,0 +1,33 @@ +// VAMS-2023 5.10.4 (Mantis 7809): declaring, triggering and detecting analog +// named events. +module named_events; + parameter real period = 1e-9; + + // several events may be declared in one statement + event ana_event, other_event; + event third_event; + + integer count; + real held; + + analog begin + count = 0; + held = 0.0; + + // triggered from an analog event statement + @(timer(period)) -> ana_event; + + @(timer(2.0 * period)) begin + -> other_event; + -> third_event; + end + + // detected in the analog block + @(ana_event) count = count + 1; + @(other_event) held = period; + @(third_event) begin + count = count + 1; + held = 2.0 * period; + end + end +endmodule diff --git a/openvaf/test_data/ui/named_events_err.log b/openvaf/test_data/ui/named_events_err.log new file mode 100644 index 00000000..d82676c8 --- /dev/null +++ b/openvaf/test_data/ui/named_events_err.log @@ -0,0 +1,26 @@ +error: type mismatch: expected named event but found integer variable reference + --> /named_events_err.va:15:25 + | +15 | @(timer(1n)) -> not_an_event; + | ^^^^^^^^^^^^ expected named event + +error: type mismatch: expected named event but found integer variable reference + --> /named_events_err.va:18:11 + | +18 | @(not_an_event) not_an_event = 1; + | ^^^^^^^^^^^^ expected named event + +error: 'missing_event' was not found in the current scope + --> /named_events_err.va:21:25 + | +21 | @(timer(1n)) -> missing_event; + | ^^^^^^^^^^^^^ not found + +error: event triggers are not allowed in analog block + --> /named_events_err.va:12:9 + | +12 | -> ana_event; + | ------------- not allowed here + | + = help: in an analog block an event may only be triggered from an event statement such as '@(timer(1n)) -> ev;' + diff --git a/openvaf/test_data/ui/named_events_err.va b/openvaf/test_data/ui/named_events_err.va new file mode 100644 index 00000000..766cffc9 --- /dev/null +++ b/openvaf/test_data/ui/named_events_err.va @@ -0,0 +1,23 @@ +// Error cases for VAMS-2023 5.10.4 named events. +module named_events_err; + event ana_event; + + integer not_an_event; + + analog begin + not_an_event = 0; + + // `event_trigger` is an analog_event_statement: in the analog block an + // event may only be triggered from an event control statement + -> ana_event; + + // the target of a trigger must be a declared event + @(timer(1n)) -> not_an_event; + + // ... and so must the subject of a detection + @(not_an_event) not_an_event = 1; + + // undeclared events are reported by name resolution + @(timer(1n)) -> missing_event; + end +endmodule diff --git a/openvaf/tokens/src/lexer.rs b/openvaf/tokens/src/lexer.rs index 145e0bde..361e8e7a 100644 --- a/openvaf/tokens/src/lexer.rs +++ b/openvaf/tokens/src/lexer.rs @@ -137,6 +137,9 @@ pub enum TokenKind { /// <+ Contribute, + /// -> + Trigger, + /// ** Pow, diff --git a/openvaf/tokens/src/lib.rs b/openvaf/tokens/src/lib.rs index e682e596..3d739256 100644 --- a/openvaf/tokens/src/lib.rs +++ b/openvaf/tokens/src/lib.rs @@ -77,6 +77,7 @@ impl lexer::TokenKind { ShlA => T![<<<], ShrA => T![>>>], Contribute => T![<+], + Trigger => T![->], Pow => T![**], NXorL => T![~^], NXorR => T![^~], diff --git a/openvaf/tokens/src/parser/generated.rs b/openvaf/tokens/src/parser/generated.rs index f230276e..3491cc97 100644 --- a/openvaf/tokens/src/parser/generated.rs +++ b/openvaf/tokens/src/parser/generated.rs @@ -51,6 +51,7 @@ pub enum SyntaxKind { R_ATTR_PAREN, ARR_START, CONTR, + TRIGGER, POW, L_NXOR, R_NXOR, @@ -92,6 +93,7 @@ pub enum SyntaxKind { FINAL_STEP_KW, FINAL_KW, ALIASPARAM_KW, + EVENT_KW, INT_NUMBER, STD_REAL_NUMBER, SI_REAL_NUMBER, @@ -126,6 +128,7 @@ pub enum SyntaxKind { DISCIPLINE_DECL, DISCIPLINE_ATTR, EVENT_STMT, + EVENT_TRIGGER_STMT, FOR_STMT, FUNCTION, FUNCTION_ARG, @@ -136,6 +139,7 @@ pub enum SyntaxKind { MODULE_PORTS, PORT_REF, GENVAR_DECL, + EVENT_DECL, NAME, NAME_REF, SYS_FUN, @@ -176,7 +180,7 @@ impl SyntaxKind { | GENVAR_KW | IF_KW | INF_KW | INOUT_KW | INPUT_KW | INTEGER_KW | MODULE_KW | NATURE_KW | OUTPUT_KW | PARAMETER_KW | LOCALPARAM_KW | REAL_KW | STRING_KW | WHILE_KW | ROOT_KW | INITIAL_STEP_KW | INITIAL_KW | FINAL_STEP_KW | FINAL_KW - | ALIASPARAM_KW => true, + | ALIASPARAM_KW | EVENT_KW => true, _ => false, } } @@ -186,7 +190,7 @@ impl SyntaxKind { | L_ANGLE | R_ANGLE | AT | POUND | TILDE | QUESTION | DOLLAR | AMP | PIPE | PLUS | STAR | SLASH | CARET | PERCENT | UNDERSCORE | DOT | COLON | EQ | EQ2 | BANG | NEQ | MINUS | LTEQ | GTEQ | AMP2 | PIPE2 | ASHL | ASHR | SHL | SHR | L_ATTR_PAREN - | R_ATTR_PAREN | ARR_START | CONTR | POW | L_NXOR | R_NXOR => true, + | R_ATTR_PAREN | ARR_START | CONTR | TRIGGER | POW | L_NXOR | R_NXOR => true, _ => false, } } @@ -236,6 +240,7 @@ impl SyntaxKind { "final_step" => FINAL_STEP_KW, "final" => FINAL_KW, "aliasparam" => ALIASPARAM_KW, + "event" => EVENT_KW, "reg" | "wreal" | "wire" | "uwire" | "wand" | "wor" | "ground" => NET_TYPE, _ => return None, }; @@ -321,6 +326,7 @@ impl std::fmt::Display for SyntaxKind { Self::R_ATTR_PAREN => "'*)'", Self::ARR_START => "''{'", Self::CONTR => "'<+'", + Self::TRIGGER => "'->'", Self::POW => "'**'", Self::L_NXOR => "'~^'", Self::R_NXOR => "'^~'", @@ -362,6 +368,7 @@ impl std::fmt::Display for SyntaxKind { Self::FINAL_STEP_KW => "'final_step'", Self::FINAL_KW => "'final'", Self::ALIASPARAM_KW => "'aliasparam'", + Self::EVENT_KW => "'event'", Self::INT_NUMBER => "integer", Self::STD_REAL_NUMBER | Self::SI_REAL_NUMBER => "real number", Self::STR_LIT => "string literal", @@ -380,4 +387,4 @@ impl std::fmt::Display for SyntaxKind { } } #[macro_export] -macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; ["_"] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<<] => { $ crate :: SyntaxKind :: ASHL } ; [>>>] => { $ crate :: SyntaxKind :: ASHR } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; ["(*"] => { $ crate :: SyntaxKind :: L_ATTR_PAREN } ; ["*)"] => { $ crate :: SyntaxKind :: R_ATTR_PAREN } ; ["'{"] => { $ crate :: SyntaxKind :: ARR_START } ; [<+] => { $ crate :: SyntaxKind :: CONTR } ; [**] => { $ crate :: SyntaxKind :: POW } ; [~^] => { $ crate :: SyntaxKind :: L_NXOR } ; [^~] => { $ crate :: SyntaxKind :: R_NXOR } ; [analog] => { $ crate :: SyntaxKind :: ANALOG_KW } ; [begin] => { $ crate :: SyntaxKind :: BEGIN_KW } ; [branch] => { $ crate :: SyntaxKind :: BRANCH_KW } ; [case] => { $ crate :: SyntaxKind :: CASE_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [disable] => { $ crate :: SyntaxKind :: DISABLE_KW } ; [discipline] => { $ crate :: SyntaxKind :: DISCIPLINE_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [end] => { $ crate :: SyntaxKind :: END_KW } ; [endcase] => { $ crate :: SyntaxKind :: ENDCASE_KW } ; [enddiscipline] => { $ crate :: SyntaxKind :: ENDDISCIPLINE_KW } ; [endfunction] => { $ crate :: SyntaxKind :: ENDFUNCTION_KW } ; [endmodule] => { $ crate :: SyntaxKind :: ENDMODULE_KW } ; [endnature] => { $ crate :: SyntaxKind :: ENDNATURE_KW } ; [exclude] => { $ crate :: SyntaxKind :: EXCLUDE_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [from] => { $ crate :: SyntaxKind :: FROM_KW } ; [function] => { $ crate :: SyntaxKind :: FUNCTION_KW } ; [genvar] => { $ crate :: SyntaxKind :: GENVAR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [inf] => { $ crate :: SyntaxKind :: INF_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [input] => { $ crate :: SyntaxKind :: INPUT_KW } ; [integer] => { $ crate :: SyntaxKind :: INTEGER_KW } ; [module] => { $ crate :: SyntaxKind :: MODULE_KW } ; [nature] => { $ crate :: SyntaxKind :: NATURE_KW } ; [output] => { $ crate :: SyntaxKind :: OUTPUT_KW } ; [parameter] => { $ crate :: SyntaxKind :: PARAMETER_KW } ; [localparam] => { $ crate :: SyntaxKind :: LOCALPARAM_KW } ; [real] => { $ crate :: SyntaxKind :: REAL_KW } ; [string] => { $ crate :: SyntaxKind :: STRING_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [root] => { $ crate :: SyntaxKind :: ROOT_KW } ; [initial_step] => { $ crate :: SyntaxKind :: INITIAL_STEP_KW } ; [initial] => { $ crate :: SyntaxKind :: INITIAL_KW } ; [final_step] => { $ crate :: SyntaxKind :: FINAL_STEP_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [aliasparam] => { $ crate :: SyntaxKind :: ALIASPARAM_KW } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [net_type] => { $ crate :: SyntaxKind :: NET_TYPE } ; [sysfun] => { $ crate :: SyntaxKind :: SYSFUN } ; } +macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; ["_"] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<<] => { $ crate :: SyntaxKind :: ASHL } ; [>>>] => { $ crate :: SyntaxKind :: ASHR } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; ["(*"] => { $ crate :: SyntaxKind :: L_ATTR_PAREN } ; ["*)"] => { $ crate :: SyntaxKind :: R_ATTR_PAREN } ; ["'{"] => { $ crate :: SyntaxKind :: ARR_START } ; [<+] => { $ crate :: SyntaxKind :: CONTR } ; [->] => { $ crate :: SyntaxKind :: TRIGGER } ; [**] => { $ crate :: SyntaxKind :: POW } ; [~^] => { $ crate :: SyntaxKind :: L_NXOR } ; [^~] => { $ crate :: SyntaxKind :: R_NXOR } ; [analog] => { $ crate :: SyntaxKind :: ANALOG_KW } ; [begin] => { $ crate :: SyntaxKind :: BEGIN_KW } ; [branch] => { $ crate :: SyntaxKind :: BRANCH_KW } ; [case] => { $ crate :: SyntaxKind :: CASE_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [disable] => { $ crate :: SyntaxKind :: DISABLE_KW } ; [discipline] => { $ crate :: SyntaxKind :: DISCIPLINE_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [end] => { $ crate :: SyntaxKind :: END_KW } ; [endcase] => { $ crate :: SyntaxKind :: ENDCASE_KW } ; [enddiscipline] => { $ crate :: SyntaxKind :: ENDDISCIPLINE_KW } ; [endfunction] => { $ crate :: SyntaxKind :: ENDFUNCTION_KW } ; [endmodule] => { $ crate :: SyntaxKind :: ENDMODULE_KW } ; [endnature] => { $ crate :: SyntaxKind :: ENDNATURE_KW } ; [exclude] => { $ crate :: SyntaxKind :: EXCLUDE_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [from] => { $ crate :: SyntaxKind :: FROM_KW } ; [function] => { $ crate :: SyntaxKind :: FUNCTION_KW } ; [genvar] => { $ crate :: SyntaxKind :: GENVAR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [inf] => { $ crate :: SyntaxKind :: INF_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [input] => { $ crate :: SyntaxKind :: INPUT_KW } ; [integer] => { $ crate :: SyntaxKind :: INTEGER_KW } ; [module] => { $ crate :: SyntaxKind :: MODULE_KW } ; [nature] => { $ crate :: SyntaxKind :: NATURE_KW } ; [output] => { $ crate :: SyntaxKind :: OUTPUT_KW } ; [parameter] => { $ crate :: SyntaxKind :: PARAMETER_KW } ; [localparam] => { $ crate :: SyntaxKind :: LOCALPARAM_KW } ; [real] => { $ crate :: SyntaxKind :: REAL_KW } ; [string] => { $ crate :: SyntaxKind :: STRING_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [root] => { $ crate :: SyntaxKind :: ROOT_KW } ; [initial_step] => { $ crate :: SyntaxKind :: INITIAL_STEP_KW } ; [initial] => { $ crate :: SyntaxKind :: INITIAL_KW } ; [final_step] => { $ crate :: SyntaxKind :: FINAL_STEP_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [aliasparam] => { $ crate :: SyntaxKind :: ALIASPARAM_KW } ; [event] => { $ crate :: SyntaxKind :: EVENT_KW } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [net_type] => { $ crate :: SyntaxKind :: NET_TYPE } ; [sysfun] => { $ crate :: SyntaxKind :: SYSFUN } ; } diff --git a/sourcegen/src/ast.rs b/sourcegen/src/ast.rs index 101fbc9b..07f90b72 100644 --- a/sourcegen/src/ast.rs +++ b/sourcegen/src/ast.rs @@ -500,6 +500,7 @@ impl Field { "\"*)\"" => "r_attr_paren", "\"'{\"" => "l_curly_arr", "<+" => "contr", + "->" => "trigger", _ => name, }; let ident = format_ident!("{}_token", name); diff --git a/sourcegen/src/ast/src.rs b/sourcegen/src/ast/src.rs index 5b233fa6..8b3238b5 100644 --- a/sourcegen/src/ast/src.rs +++ b/sourcegen/src/ast/src.rs @@ -54,6 +54,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { ("*)", "R_ATTR_PAREN"), ("'{", "ARR_START"), ("<+", "CONTR"), + ("->", "TRIGGER"), ("**", "POW"), ("~^", "L_NXOR"), ("^~", "R_NXOR"), @@ -97,6 +98,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "final_step", "final", "aliasparam", + "event", ], literals: &["INT_NUMBER", "STD_REAL_NUMBER", "SI_REAL_NUMBER", "STR_LIT"], tokens: &["ERROR", "IDENT", "SYSFUN", "NET_TYPE", "WHITESPACE", "COMMENT"], @@ -125,6 +127,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "DISCIPLINE_DECL", "DISCIPLINE_ATTR", "EVENT_STMT", + "EVENT_TRIGGER_STMT", "FOR_STMT", "FUNCTION", "FUNCTION_ARG", @@ -135,6 +138,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "MODULE_PORTS", "PORT_REF", "GENVAR_DECL", + "EVENT_DECL", "NAME", "NAME_REF", "SYS_FUN",