[Moore] Add evenOp to handle event controls. (#7154)

This commit is contained in:
Hailong Sun 2024-06-14 09:05:33 +08:00 committed by GitHub
parent 11fb804813
commit f26f534d60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 180 additions and 1 deletions

View File

@ -323,6 +323,65 @@ def NonBlockingAssignOp : AssignOpBase<"nonblocking_assign"> {
}];
}
//===----------------------------------------------------------------------===//
// Statements
//===----------------------------------------------------------------------===//
def None: I32EnumAttrCase<"None", 0, "none">;
/// Transit from 0 to x/z/1, and from x/z to 1.
def PosEdge: I32EnumAttrCase<"PosEdge", 1, "posedge">;
/// Transit from 1 to x/z/0, and from x/z to 0.
def NegEdge: I32EnumAttrCase<"NegEdge", 2, "negedge">;
/// Include the negedge and posedge.
def BothEdges: I32EnumAttrCase<"BothEdges", 3, "edge">;
def EdgeAtrr: I32EnumAttr<"Edge", "Edge kind",
[None, PosEdge, NegEdge, BothEdges]>{
let cppNamespace = "circt::moore";
}
def EventOp : MooreOp<"wait_event", [
HasParent<"ProcedureOp">
]> {
let summary = "Detecting posedge and negedge";
let description = [{
It is introduced by the symbol `@`, and it allows statement execution to
be delayed until the occurrence of some simulation event occurring in a
procedure executing concurrently with this procedure.
For the implicit event control(`@(*)`), there are two situations that are
not automatically added to event expression:
1. Identifiers that only appear in wait or event expressions.
```
always @(*) begin // equivalent to @(b)
@(n) kid = b; // n is not added to @(*)
end
```
2. Identifiers that only appear as a hierarchical_variable_identifier
in the variable_lvalue of the left-hand side of assignments.
```
always @(*) begin
a = b + c; // a is not added to @(*)
end
```
Example:
```
@(a, b, c) // none
@(posedge clk) // positive edge
@(negedge clk) // negative edge
@(edge clk) // both edge
@(*) // implicit event control
```
See IEEE 1800-2017 § 9.4.2 "Event control".
}];
let arguments = (ins EdgeAtrr:$edge, UnpackedType:$input);
let results = (outs);
let assemblyFormat = [{
$edge $input attr-dict `:` type($input)
}];
}
//===----------------------------------------------------------------------===//
// Expressions
//===----------------------------------------------------------------------===//

View File

@ -34,6 +34,7 @@ add_circt_translation_library(CIRCTImportVerilog
Statements.cpp
Structure.cpp
Types.cpp
TimingControls.cpp
DEPENDS
slang_slang

View File

@ -79,6 +79,10 @@ struct Context {
Value convertRvalueExpression(const slang::ast::Expression &expr);
Value convertLvalueExpression(const slang::ast::Expression &expr);
// Convert a slang timing control into an MLIR timing control.
LogicalResult
convertTimingControl(const slang::ast::TimingControl &timingControl);
mlir::ModuleOp intoModuleOp;
const slang::SourceManager &sourceManager;
SmallDenseMap<slang::BufferID, StringRef> &bufferFilePaths;

View File

@ -302,8 +302,12 @@ struct StmtVisitor {
return success();
}
// Ignore timing control for now.
// Handle timing control.
LogicalResult visit(const slang::ast::TimedStatement &stmt) {
if (failed(context.convertTimingControl(stmt.timing)))
return failure();
if (failed(context.convertStatement(stmt.stmt)))
return failure();
return success();
}

View File

@ -0,0 +1,64 @@
//===- TimingControl.cpp - Slang timing control conversion ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ImportVerilogInternals.h"
#include "slang/ast/TimingControl.h"
using namespace circt;
using namespace ImportVerilog;
namespace {
struct TimingCtrlVisitor {
Context &context;
Location loc;
OpBuilder &builder;
TimingCtrlVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
LogicalResult visit(const slang::ast::SignalEventControl &ctrl) {
// TODO: When updating slang to the latest version, we will handle
// "iffCondition".
auto loc = context.convertLocation(ctrl.sourceRange.start());
auto input = context.convertRvalueExpression(ctrl.expr);
builder.create<moore::EventOp>(loc, static_cast<moore::Edge>(ctrl.edge),
input);
return success();
}
LogicalResult visit(const slang::ast::ImplicitEventControl &ctrl) {
return success();
}
LogicalResult visit(const slang::ast::EventListControl &ctrl) {
for (auto *event : ctrl.as<slang::ast::EventListControl>().events) {
if (failed(context.convertTimingControl(*event)))
return failure();
}
return success();
}
/// Emit an error for all other timing controls.
template <typename T>
LogicalResult visit(T &&node) {
mlir::emitError(loc, "unspported timing control: ")
<< slang::ast::toString(node.kind);
return failure();
}
LogicalResult visitInvalid(const slang::ast::TimingControl &ctrl) {
mlir::emitError(loc, "invalid timing control");
return failure();
}
};
} // namespace
LogicalResult
Context::convertTimingControl(const slang::ast::TimingControl &timingControl) {
auto loc = convertLocation(timingControl.sourceRange.start());
TimingCtrlVisitor visitor{*this, loc};
return timingControl.visit(visitor);
}

View File

@ -1161,3 +1161,50 @@ module MultiPorts(
// CHECK: [[C1_READ:%.+]] = moore.read %c1
// CHECK: moore.output [[V1_READ]], [[C1_READ]]
endmodule
// CHECK-LABEL: moore.module @EventControl(in %clk : !moore.l1)
module EventControl(input clk);
// CHECK: %clk_0 = moore.net name "clk" wire : <l1>
int a1, a2, b, c;
// CHECK: moore.procedure always
// CHECK: [[CLK_READ:%.+]] = moore.read %clk_0 : l1
// CHECK: moore.wait_event posedge [[CLK_READ]] : l1
always @(posedge clk) begin end;
// CHECK: moore.procedure always
// CHECK: [[CLK_READ:%.+]] = moore.read %clk_0 : l1
// CHECK: moore.wait_event negedge [[CLK_READ]] : l1
always @(negedge clk) begin end;
// CHECK: moore.procedure always
// CHECK: [[CLK_READ:%.+]] = moore.read %clk_0 : l1
// CHECK: moore.wait_event edge [[CLK_READ]] : l1
always @(edge clk) begin end;
// CHECK: moore.procedure always {
// CHECK: [[B_READ:%.+]] = moore.read %b : i32
// CHECK: moore.wait_event none [[B_READ]] : i32
// CHECK: [[C_READ:%.+]] = moore.read %c : i32
// CHECK: moore.wait_event none [[C_READ]] : i32
always @(b, c) begin
// CHECK: [[B_READ:%.+]] = moore.read %b : i32
// CHECK: [[C_READ:%.+]] = moore.read %c : i32
// CHECK: [[ADD:%.+]] = moore.add [[B_READ]], [[C_READ]] : i32
// CHECK: moore.blocking_assign %a1, [[ADD]] : i32
a1 = b + c;
end;
// CHECK: moore.procedure always
always @(*) begin
// CHECK: [[B_READ:%.+]] = moore.read %b : i32
// CHECK: [[C_READ:%.+]] = moore.read %c : i32
// CHECK: [[ADD:%.+]] = moore.add [[B_READ]], [[C_READ]] : i32
// CHECK: moore.blocking_assign %a2, [[ADD]] : i32
a2 = b + c;
end
// CHECK: moore.assign %clk_0, %clk : l1
// CHECK: moore.output
endmodule