[FIRRTL] Add generic intrinsic op. (#6874)

Add generic intrinsic operation to FIRRTL. Not used anywhere, just adding to the IR to build on.
Re-use module parameter printing/parsing for use as custom printer on the op.

See PR for more context.
This commit is contained in:
Will Dietz 2024-03-27 09:17:38 -05:00 committed by GitHub
parent 87020b2ed4
commit 0cff5d90dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 30 deletions

View File

@ -13,36 +13,24 @@
#ifndef CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD #ifndef CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD
#define CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD #define CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD
def IsXIntrinsicOp : FIRRTLOp<"int.isX", include "FIRRTLDialect.td"
[HasCustomSSAName, Pure]> {
let summary = "Test for 'x";
let description = [{
The `int.isX` expression checks that the operand is not a verilog literal
'x. FIRRTL doesn't have a notion of 'x per-se, but x can come in to the
system from external modules and from SV constructs. Verification
constructs need to explicitly test for 'x.
}];
let arguments = (ins FIRRTLBaseType:$arg); //===----------------------------------------------------------------------===//
let results = (outs NonConstUInt1Type:$result); // Generic intrinsic operation for parsing into before lowering.
let hasFolder = 1; //===----------------------------------------------------------------------===//
let assemblyFormat = "$arg attr-dict `:` type($arg)";
}
def HasBeenResetIntrinsicOp : FIRRTLOp<"int.has_been_reset", [Pure]> { def GenericIntrinsicOp : FIRRTLOp<"int.generic",
let summary = "Check that a proper reset has been seen."; [HasCustomSSAName]> {
let description = [{ let summary = "Generic intrinsic operation for FIRRTL intrinsics.";
The result of `firrtl.int.has_been_reset` reads as 0 immediately after simulation
startup and after each power-cycle in a power-aware simulation. The result
remains 0 before and during reset and only switches to 1 after the reset is
deasserted again.
See the corresponding `verif.has_been_reset` operation. let arguments = (
}]; ins StrAttr:$intrinsic,
let arguments = (ins NonConstClockType:$clock, AnyResetType:$reset); Variadic<PassiveType>:$operands,
let results = (outs NonConstUInt1Type:$result); DefaultValuedAttr<ParamDeclArrayAttr, "{}">:$parameters
let hasFolder = 1; );
let assemblyFormat = "$clock `,` $reset attr-dict `:` type($reset)";
let results = (outs Optional<PassiveType>:$result);
let assemblyFormat = "$intrinsic custom<ParameterList>($parameters) ($operands^)? attr-dict-with-keyword `:` functional-type($operands, $result)";
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -135,4 +123,41 @@ def ClockInverterIntrinsicOp : FIRRTLOp<"int.clock_inv", []> {
let assemblyFormat = "$input attr-dict"; let assemblyFormat = "$input attr-dict";
} }
//===----------------------------------------------------------------------===//
// Other intrinsics
//===----------------------------------------------------------------------===//
def IsXIntrinsicOp : FIRRTLOp<"int.isX",
[HasCustomSSAName, Pure]> {
let summary = "Test for 'x";
let description = [{
The `int.isX` expression checks that the operand is not a verilog literal
'x. FIRRTL doesn't have a notion of 'x per-se, but x can come in to the
system from external modules and from SV constructs. Verification
constructs need to explicitly test for 'x.
}];
let arguments = (ins FIRRTLBaseType:$arg);
let results = (outs NonConstUInt1Type:$result);
let hasFolder = 1;
let assemblyFormat = "$arg attr-dict `:` type($arg)";
}
def HasBeenResetIntrinsicOp : FIRRTLOp<"int.has_been_reset", [Pure]> {
let summary = "Check that a proper reset has been seen.";
let description = [{
The result of `firrtl.int.has_been_reset` reads as 0 immediately after simulation
startup and after each power-cycle in a power-aware simulation. The result
remains 0 before and during reset and only switches to 1 after the reset is
deasserted again.
See the corresponding `verif.has_been_reset` operation.
}];
let arguments = (ins NonConstClockType:$clock, AnyResetType:$reset);
let results = (outs NonConstUInt1Type:$result);
let hasFolder = 1;
let assemblyFormat = "$clock `,` $reset attr-dict `:` type($reset)";
}
#endif // CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD #endif // CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD

View File

@ -53,7 +53,7 @@ public:
LTLConcatIntrinsicOp, LTLNotIntrinsicOp, LTLImplicationIntrinsicOp, LTLConcatIntrinsicOp, LTLNotIntrinsicOp, LTLImplicationIntrinsicOp,
LTLEventuallyIntrinsicOp, LTLClockIntrinsicOp, LTLEventuallyIntrinsicOp, LTLClockIntrinsicOp,
LTLDisableIntrinsicOp, Mux2CellIntrinsicOp, Mux4CellIntrinsicOp, LTLDisableIntrinsicOp, Mux2CellIntrinsicOp, Mux4CellIntrinsicOp,
HasBeenResetIntrinsicOp, FPGAProbeIntrinsicOp, HasBeenResetIntrinsicOp, FPGAProbeIntrinsicOp, GenericIntrinsicOp,
// Miscellaneous. // Miscellaneous.
BitsPrimOp, HeadPrimOp, MuxPrimOp, PadPrimOp, ShlPrimOp, ShrPrimOp, BitsPrimOp, HeadPrimOp, MuxPrimOp, PadPrimOp, ShlPrimOp, ShrPrimOp,
TailPrimOp, VerbatimExprOp, HWStructCastOp, BitCastOp, RefSendOp, TailPrimOp, VerbatimExprOp, HWStructCastOp, BitCastOp, RefSendOp,
@ -180,6 +180,7 @@ public:
HANDLE(Mux2CellIntrinsicOp, Unhandled); HANDLE(Mux2CellIntrinsicOp, Unhandled);
HANDLE(HasBeenResetIntrinsicOp, Unhandled); HANDLE(HasBeenResetIntrinsicOp, Unhandled);
HANDLE(FPGAProbeIntrinsicOp, Unhandled); HANDLE(FPGAProbeIntrinsicOp, Unhandled);
HANDLE(GenericIntrinsicOp, Unhandled);
// Miscellaneous. // Miscellaneous.
HANDLE(BitsPrimOp, Unhandled); HANDLE(BitsPrimOp, Unhandled);

View File

@ -1255,7 +1255,8 @@ parseModulePorts(OpAsmParser &parser, bool hasSSAIdentifiers,
} }
/// Print a paramter list for a module or instance. /// Print a paramter list for a module or instance.
static void printParameterList(ArrayAttr parameters, OpAsmPrinter &p) { static void printParameterList(OpAsmPrinter &p, Operation *op,
ArrayAttr parameters) {
if (!parameters || parameters.empty()) if (!parameters || parameters.empty())
return; return;
@ -1283,7 +1284,7 @@ static void printFModuleLikeOp(OpAsmPrinter &p, FModuleLike op) {
p.printSymbolName(op.getModuleName()); p.printSymbolName(op.getModuleName());
// Print the parameter list (if non-empty). // Print the parameter list (if non-empty).
printParameterList(op->getAttrOfType<ArrayAttr>("parameters"), p); printParameterList(p, op, op->getAttrOfType<ArrayAttr>("parameters"));
// Both modules and external modules have a body, but it is always empty for // Both modules and external modules have a body, but it is always empty for
// external modules. // external modules.
@ -1372,6 +1373,18 @@ parseOptionalParameters(OpAsmParser &parser,
}); });
} }
/// Shim to use with assemblyFormat, custom<ParameterList>.
static ParseResult parseParameterList(OpAsmParser &parser,
ArrayAttr &parameters) {
SmallVector<Attribute> parseParameters;
if (failed(parseOptionalParameters(parser, parseParameters)))
return failure();
parameters = ArrayAttr::get(parser.getContext(), parseParameters);
return success();
}
static ParseResult parseFModuleLikeOp(OpAsmParser &parser, static ParseResult parseFModuleLikeOp(OpAsmParser &parser,
OperationState &result, OperationState &result,
bool hasSSAIdentifiers) { bool hasSSAIdentifiers) {
@ -5764,6 +5777,9 @@ void GEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
void GTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { void GTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
genericAsmResultNames(*this, setNameFn); genericAsmResultNames(*this, setNameFn);
} }
void GenericIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
genericAsmResultNames(*this, setNameFn);
}
void HeadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { void HeadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
genericAsmResultNames(*this, setNameFn); genericAsmResultNames(*this, setNameFn);
} }

View File

@ -21,6 +21,17 @@ firrtl.module @Intrinsics(in %ui : !firrtl.uint, in %clock: !firrtl.clock, in %u
// CHECK-NEXT: firrtl.int.clock_gate %clock, %ui1, %ui1 // CHECK-NEXT: firrtl.int.clock_gate %clock, %ui1, %ui1
%cg0 = firrtl.int.clock_gate %clock, %ui1 %cg0 = firrtl.int.clock_gate %clock, %ui1
%cg1 = firrtl.int.clock_gate %clock, %ui1, %ui1 %cg1 = firrtl.int.clock_gate %clock, %ui1, %ui1
// CHECK-NEXT: firrtl.int.generic "clock_gate" %clock, %ui1 : (!firrtl.clock, !firrtl.uint<1>)
// CHECK-NEXT: firrtl.int.generic "noargs" : () -> !firrtl.uint<32>
// CHECK-NEXT: firrtl.int.generic "params" <FORMAT: none = "foobar"> : () -> !firrtl.bundle<x: uint<1>>
// CHECK-NEXT: firrtl.int.generic "params_and_operand" <X: i64 = 123> %ui1 : (!firrtl.uint<1>) -> !firrtl.clock
// CHECK-NEXT: firrtl.int.generic "inputs" %clock, %ui1, %clock : (!firrtl.clock, !firrtl.uint<1>, !firrtl.clock) -> ()
%cg2 = firrtl.int.generic "clock_gate" %clock, %ui1 : (!firrtl.clock, !firrtl.uint<1>) -> !firrtl.clock
%noargs = firrtl.int.generic "noargs" : () -> !firrtl.uint<32>
%p = firrtl.int.generic "params" <FORMAT: none = "foobar"> : () -> !firrtl.bundle<x: uint<1>>
%po = firrtl.int.generic "params_and_operand" <X: i64 = 123> %ui1 : (!firrtl.uint<1>) -> !firrtl.clock
firrtl.int.generic "inputs" %clock, %ui1, %clock : (!firrtl.clock, !firrtl.uint<1>, !firrtl.clock) -> ()
} }
// CHECK-LABEL: firrtl.module @FPGAProbe // CHECK-LABEL: firrtl.module @FPGAProbe