diff --git a/include/circt/Dialect/BLIF/BLIF.td b/include/circt/Dialect/BLIF/BLIF.td new file mode 100644 index 000000000000..674f4c410bc4 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIF.td @@ -0,0 +1,21 @@ +//===- BLIF.td - BLIF dialect definition -------------------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This is the top level file for the BLIF dialect. This dialect represents +// the (extended) Berkely Logic Interchange Format +// [http://bear.ces.cwru.edu/eecs_cad/sis_blif.pdf]. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIF_TD +#define CIRCT_DIALECT_BLIF_BLIF_TD + +include "circt/Dialect/BLIF/BLIFDialect.td" +include "circt/Dialect/BLIF/BLIFOps.td" + +#endif // CIRCT_DIALECT_BLIF_BLIF_TD diff --git a/include/circt/Dialect/BLIF/BLIFDialect.h b/include/circt/Dialect/BLIF/BLIFDialect.h new file mode 100644 index 000000000000..4cf98d7d63a3 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFDialect.h @@ -0,0 +1,24 @@ +//===- BLIFDialect.h - BLIF dialect declaration -----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines an BLIF MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFDIALECT_H +#define CIRCT_DIALECT_BLIF_BLIFDIALECT_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Dialect.h" + +// Pull in the dialect definition. +#include "circt/Dialect/BLIF/BLIFDialect.h.inc" +#include "circt/Dialect/BLIF/BLIFEnums.h.inc" + +#endif // CIRCT_DIALECT_BLIF_BLIFDIALECT_H diff --git a/include/circt/Dialect/BLIF/BLIFDialect.td b/include/circt/Dialect/BLIF/BLIFDialect.td new file mode 100644 index 000000000000..3b94ac29077c --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFDialect.td @@ -0,0 +1,33 @@ +//===- BLIFDialect.td - BLIF dialect definition ------------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This contains the BLIFDialect definition to be included in other files. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFDIALECT +#define CIRCT_DIALECT_BLIF_BLIFDIALECT + +include "mlir/IR/OpBase.td" + +def BLIFDialect : Dialect { + let name = "blif"; + let cppNamespace = "::circt::blif"; + + let summary = "Types and operations for the BLIF format dialect"; + let description = [{ + This dialect defines the `BLIF` dialect, which is a representation of + the Berkely Logic Interchange Format. + }]; +} + +// Base class for the operation in this dialect. +class BLIFOp traits = []> : + Op; + +#endif // CIRCT_DIALECT_BLIF_BLIFDIALECT diff --git a/include/circt/Dialect/BLIF/BLIFEmitter.h b/include/circt/Dialect/BLIF/BLIFEmitter.h new file mode 100644 index 000000000000..a4ef6f1b61d3 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFEmitter.h @@ -0,0 +1,30 @@ +//===- BLIFEmitter.h - BLIF dialect to .blif emitter ------------*- C++ -*-===// +// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the interface to the .blif file emitter. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFEMITTER_H +#define CIRCT_DIALECT_BLIF_BLIFEMITTER_H + +#include "circt/Support/LLVM.h" + +namespace circt { +namespace blif { + +mlir::LogicalResult exportBLIFFile(mlir::ModuleOp module, + llvm::raw_ostream &os); + +void registerToBLIFFileTranslation(); + +} // namespace blif +} // namespace circt + +#endif // CIRCT_DIALECT_BLIF_BLIFEMITTER_H diff --git a/include/circt/Dialect/BLIF/BLIFOps.h b/include/circt/Dialect/BLIF/BLIFOps.h new file mode 100644 index 000000000000..04dbca975cfe --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFOps.h @@ -0,0 +1,27 @@ +//===- BLIFOps.h - Declare BLIF dialect operations --------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the operation classes for the BLIF dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFOPS_H +#define CIRCT_DIALECT_BLIF_BLIFOPS_H + +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/HW/HWOpInterfaces.h" +#include "mlir/Bytecode/BytecodeOpInterface.h" +#include "mlir/IR/OpImplementation.h" + +#define GET_OP_CLASSES +#include "circt/Dialect/BLIF/BLIF.h.inc" + +#endif // CIRCT_DIALECT_BLIF_BLIFOPS_H + +using namespace circt; +using namespace blif; diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td new file mode 100644 index 000000000000..6d0989dfbe41 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -0,0 +1,86 @@ +//===- BLIFOps.td - BLIF ops ============-------------------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This defines the BLIF ops. This defines module-like operations, connections, +// and logic. +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFOPS_TD +#define CIRCT_DIALECT_BLIF_BLIFOPS_TD + +include "circt/Dialect/BLIF/BLIFDialect.td" +include "circt/Dialect/HW/HWTypes.td" +include "mlir/IR/EnumAttr.td" +include "mlir/IR/RegionKindInterface.td" + +def ModelOp : BLIFOp<"model", [IsolatedFromAbove, RegionKindInterface, SingleBlockImplicitTerminator<"OutputOp">]> { + let summary = "A model, which is a module"; + let description = [{ + The basic container. Is like a module. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name, + TypeAttrOf:$module_type, + APIntAttr:$clocks); + let results = (outs); + let regions = (region SizedRegion<1>:$body); + + let builders = [ + OpBuilder<(ins "StringRef":$name, + "ArrayRef":$inputs, + "ArrayRef":$outputs, + "ArrayRef":$clocks)> + ]; + + + let extraClassDeclaration = [{ + static mlir::RegionKind getRegionKind(unsigned index) { + return mlir::RegionKind::Graph; + } + }]; +} + +def OutputOp : BLIFOp<"output", [Terminator]> { + let arguments = (ins Variadic:$inputs); +} + +def I8Property : IntProperty<"int8_t">; + +def LogicGateOp: BLIFOp<"logic_gate", []> { + let summary = "Combinatorial logic"; + let description = [{ + A logic gate represents a logic function in sum-of-products + form. Each entry in $func is a vector applied to the inputs + where 0 means invert the input, 1 means use the input, and 2 + means don't use the input. + }]; + + let arguments = (ins ArrayProperty>:$func, + Variadic:$inputs); + let results = (outs I1:$result); +} + +def LatchMode : I32EnumAttr< + "LatchModeEnum", "Latch Mode", [ + I32EnumAttrCase<"Unspecified", 0, "un">, + I32EnumAttrCase<"FallingEdge", 1, "fe">, + I32EnumAttrCase<"RisingEdge", 2, "re">, + I32EnumAttrCase<"ActiveHigh", 3, "ah">, + I32EnumAttrCase<"ActiveLow", 4, "al">, + I32EnumAttrCase<"Asynchronous", 5, "as"> + ]> { + let cppNamespace = "circt::blif"; + } + +def LatchGateOp: BLIFOp<"latch_gate", []> { + + let arguments = (ins I1:$input, LatchMode:$mode, Optional:$clock, I32Property:$initVal); + let results = (outs I1:$output); +} + +#endif // CIRCT_DIALECT_BLIF_BLIFOPS_TD diff --git a/include/circt/Dialect/BLIF/BLIFParser.h b/include/circt/Dialect/BLIF/BLIFParser.h new file mode 100644 index 000000000000..3ea8051e6921 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFParser.h @@ -0,0 +1,61 @@ +//===- BLIFParser.h - .blif to BLIF dialect parser --------------*- C++ -*-===// +// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the interface to the .blif file parser. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFPARSER_H +#define CIRCT_DIALECT_BLIF_BLIFPARSER_H + +#include "circt/Support/LLVM.h" +#include +#include +#include + +namespace llvm { +class SourceMgr; +} // namespace llvm + +namespace mlir { +class LocationAttr; +class TimingScope; +} // namespace mlir + +namespace circt { +namespace blif { + +struct BLIFParserOptions { + /// Specify how @info locators should be handled. + enum class InfoLocHandling { + /// If this is set to true, the @info locators are ignored, and the + /// locations are set to the location in the .BLIF file. + IgnoreInfo, + /// Prefer @info locators, fallback to .BLIF locations. + PreferInfo, + /// Attach both @info locators (when present) and .BLIF locations. + FusedInfo + }; + + InfoLocHandling infoLocatorHandling = InfoLocHandling::PreferInfo; + + /// parse strict blif instead of extended blif + bool strictBLIF = false; +}; + +mlir::OwningOpRef +importBLIFFile(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, + mlir::TimingScope &ts, BLIFParserOptions options = {}); + +void registerFromBLIFFileTranslation(); + +} // namespace blif +} // namespace circt + +#endif // CIRCT_DIALECT_BLIF_BLIFPARSER_H diff --git a/include/circt/Dialect/BLIF/CMakeLists.txt b/include/circt/Dialect/BLIF/CMakeLists.txt new file mode 100644 index 000000000000..dda7b7fe9c89 --- /dev/null +++ b/include/circt/Dialect/BLIF/CMakeLists.txt @@ -0,0 +1,7 @@ +add_circt_dialect(BLIF blif) + +set(LLVM_TARGET_DEFINITIONS BLIF.td) +mlir_tablegen(BLIFEnums.h.inc -gen-enum-decls) +mlir_tablegen(BLIFEnums.cpp.inc -gen-enum-defs) +add_public_tablegen_target(CIRCTBLIFEnumsIncGen) +add_dependencies(circt-headers CIRCTBLIFEnumsIncGen) diff --git a/include/circt/Dialect/CMakeLists.txt b/include/circt/Dialect/CMakeLists.txt index e5b55dcb068e..78a8f0eb0059 100644 --- a/include/circt/Dialect/CMakeLists.txt +++ b/include/circt/Dialect/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(AIG) add_subdirectory(Arc) +add_subdirectory(BLIF) add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) diff --git a/include/circt/InitAllDialects.h b/include/circt/InitAllDialects.h index 8e53c5fe211a..a6ed7eb8e23b 100644 --- a/include/circt/InitAllDialects.h +++ b/include/circt/InitAllDialects.h @@ -16,6 +16,7 @@ #include "circt/Dialect/AIG/AIGDialect.h" #include "circt/Dialect/Arc/ArcDialect.h" +#include "circt/Dialect/BLIF/BLIFDialect.h" #include "circt/Dialect/Calyx/CalyxDialect.h" #include "circt/Dialect/Comb/CombDialect.h" #include "circt/Dialect/DC/DCDialect.h" @@ -58,6 +59,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) { registry.insert< aig::AIGDialect, arc::ArcDialect, + blif::BLIFDialect, calyx::CalyxDialect, chirrtl::CHIRRTLDialect, comb::CombDialect, diff --git a/include/circt/InitAllTranslations.h b/include/circt/InitAllTranslations.h index 314faa8b175e..4a96563d6d73 100644 --- a/include/circt/InitAllTranslations.h +++ b/include/circt/InitAllTranslations.h @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/Arc/ModelInfoExport.h" +#include "circt/Dialect/BLIF/BLIFEmitter.h" +#include "circt/Dialect/BLIF/BLIFParser.h" #include "circt/Dialect/Calyx/CalyxEmitter.h" #include "circt/Dialect/ESI/ESIDialect.h" #include "circt/Dialect/FIRRTL/FIREmitter.h" @@ -32,6 +34,8 @@ namespace circt { inline void registerAllTranslations() { static bool initOnce = []() { arc::registerArcModelInfoTranslation(); + blif::registerFromBLIFFileTranslation(); + blif::registerToBLIFFileTranslation(); calyx::registerToCalyxTranslation(); firrtl::registerFromFIRFileTranslation(); firrtl::registerToFIRFileTranslation(); diff --git a/lib/Dialect/BLIF/BLIFDialect.cpp b/lib/Dialect/BLIF/BLIFDialect.cpp new file mode 100644 index 000000000000..1de1e0e35961 --- /dev/null +++ b/lib/Dialect/BLIF/BLIFDialect.cpp @@ -0,0 +1,36 @@ +//===- BLIFDialect.cpp - Implement the BLIF dialect -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the BLIF dialect. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace blif; + +//===----------------------------------------------------------------------===// +// Dialect specification. +//===----------------------------------------------------------------------===// + +void BLIFDialect::initialize() { + // Register operations. + addOperations< +#define GET_OP_LIST +#include "circt/Dialect/BLIF/BLIF.cpp.inc" + >(); +} + +// Provide implementations for the enums we use. +#include "circt/Dialect/BLIF/BLIFEnums.cpp.inc" + +#include "circt/Dialect/BLIF/BLIFDialect.cpp.inc" diff --git a/lib/Dialect/BLIF/BLIFOps.cpp b/lib/Dialect/BLIF/BLIFOps.cpp new file mode 100644 index 000000000000..f4c02746cb70 --- /dev/null +++ b/lib/Dialect/BLIF/BLIFOps.cpp @@ -0,0 +1,81 @@ +//===- BLIFOps.cpp - Implement the BLIF operations ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements BLIF ops. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" + +using namespace circt; +using namespace blif; + +void ModelOp::build(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, + StringRef name, ArrayRef inputs, + ArrayRef outputs, ArrayRef clocks) { + SmallVector modulePorts; + auto I1 = odsBuilder.getIntegerType(1); + auto uLoc = odsBuilder.getUnknownLoc(); + auto *r = odsState.addRegion(); + Block *b = new Block(); + r->push_back(b); + + for (auto input : inputs) { + modulePorts.push_back( + {odsBuilder.getStringAttr(input), I1, hw::ModulePort::Input}); + b->addArgument(I1, uLoc); + } + for (auto output : outputs) + modulePorts.push_back( + {odsBuilder.getStringAttr(output), I1, hw::ModulePort::Output}); + for (auto clock : clocks) { + modulePorts.push_back( + {odsBuilder.getStringAttr(clock), I1, hw::ModulePort::Input}); + b->addArgument(I1, uLoc); + } + auto moduleType = hw::ModuleType::get(odsBuilder.getContext(), modulePorts); + APInt clockVec(inputs.size() + outputs.size() + clocks.size(), 0); + for (size_t i = inputs.size() + outputs.size(); + i < inputs.size() + outputs.size() + clocks.size(); i++) + clockVec.setBit(i); + auto clockAttr = odsBuilder.getIntegerAttr( + odsBuilder.getIntegerType(clockVec.getBitWidth()), clockVec); + odsState.addAttribute("sym_name", odsBuilder.getStringAttr(name)); + odsState.addAttribute("module_type", TypeAttr::get(moduleType)); + odsState.addAttribute("clocks", clockAttr); +} + +namespace mlir { +LogicalResult +convertFromAttribute(SmallVectorImpl &storage, Attribute attr, + function_ref emitError) { + auto valueAttr = dyn_cast(attr); + if (!valueAttr) { + emitError() << "expected dense array of int8_t for key `value`"; + return failure(); + } + storage.resize_for_overwrite(valueAttr.size()); + llvm::copy(valueAttr.asArrayRef(), storage.begin()); + return success(); +} + +Attribute convertToAttribute(MLIRContext *ctx, ArrayRef storage) { + return DenseI8ArrayAttr::get(ctx, storage); +} + +} // namespace mlir + +//===----------------------------------------------------------------------===// +// TableGen generated logic. +//===----------------------------------------------------------------------===// + +// Provide the autogenerated implementation guts for the Op classes. +#define GET_OP_CLASSES +#include "circt/Dialect/BLIF/BLIF.cpp.inc" diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt new file mode 100644 index 000000000000..bd4ed5bebc7a --- /dev/null +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -0,0 +1,27 @@ +add_circt_dialect_library(CIRCTBLIF + BLIFOps.cpp + BLIFDialect.cpp + + ADDITIONAL_HEADER_DIRS + ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/BLIF + + DEPENDS + MLIRBLIFIncGen + CIRCTBLIFEnumsIncGen + + LINK_COMPONENTS + Support + + LINK_LIBS PUBLIC + CIRCTHW + MLIRIR + MLIRInferTypeOpInterface + ) + +add_dependencies(circt-headers + MLIRBLIFIncGen + CIRCTBLIFEnumsIncGen + ) + + add_subdirectory(Export) + add_subdirectory(Import) diff --git a/lib/Dialect/BLIF/Export/BLIFEmitter.cpp b/lib/Dialect/BLIF/Export/BLIFEmitter.cpp new file mode 100644 index 000000000000..010bb01242a7 --- /dev/null +++ b/lib/Dialect/BLIF/Export/BLIFEmitter.cpp @@ -0,0 +1,225 @@ +//===- BLIFEmitter.cpp - BLIF dialect to .blif emitter --------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements a .blif file emitter. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFEmitter.h" +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "circt/Dialect/HW/HWDialect.h" +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Tools/mlir-translate/Translation.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "export-blif" + +using namespace circt; +using namespace blif; + +//===----------------------------------------------------------------------===// +// Emitter +//===----------------------------------------------------------------------===// + +// NOLINTBEGIN(misc-no-recursion) +namespace { + +/// An emitter for FIRRTL dialect operations to .fir output. +struct Emitter { + Emitter(llvm::raw_ostream &os) : os(os) {} + LogicalResult finalize(); + + // Circuit/module emission + void emitModel(ModelOp op); + void emitModuleParameters(Operation *op, ArrayAttr parameters); + + // Statement emission + void emitCommand(LogicGateOp op); + void emitCommand(LatchGateOp op); + +private: + /// Emit an error and remark that emission failed. + InFlightDiagnostic emitError(Operation *op, const Twine &message) { + encounteredError = true; + return op->emitError(message); + } + + /// Emit an error and remark that emission failed. + InFlightDiagnostic emitOpError(Operation *op, const Twine &message) { + encounteredError = true; + return op->emitOpError(message); + } + +private: + /// Pretty printer. + llvm::raw_ostream &os; + + /// Whether we have encountered any errors during emission. + bool encounteredError = false; + + /// The names used to emit values already encountered. Anything that gets a + /// name in the output FIR is listed here, such that future expressions can + /// reference it. + DenseMap valueNames; + StringSet<> valueNamesStorage; + int nameCounter = 0; + + /// Return the name used during emission of a `Value`, or none if the value + /// has not yet been emitted or it was emitted inline. + StringRef lookupEmittedName(Value value) { + auto it = valueNames.find(value); + if (it != valueNames.end()) + return {it->second}; + std::string name = "v" + std::to_string(nameCounter++); + while (valueNamesStorage.count(name)) { + name = "v" + std::to_string(nameCounter++); + } + addValueName(value, name); + return valueNames.find(value)->second; + } + + void addValueName(Value value, StringAttr attr) { + valueNames.insert({value, attr.getValue()}); + } + void addValueName(Value value, StringRef str) { + auto it = valueNamesStorage.insert(str); + valueNames.insert({value, it.first->getKey()}); + } +}; +} // namespace + +LogicalResult Emitter::finalize() { return failure(encounteredError); } + +/// Emit an entire circuit. +void Emitter::emitModel(ModelOp op) { + os << ".model " << op.getSymName() << "\n"; + auto mType = op.getModuleType(); + auto clockMask = op.getClocks(); + + int inIdx = 0; + int outIdx = 0; + auto &body = op.getBody().front(); + auto term = body.getTerminator(); + for (auto [idx, port] : llvm::enumerate(mType.getPorts())) { + switch (port.dir) { + case hw::ModulePort::Direction::Input: + if (clockMask.extractBits(idx, 1).isZero()) + os << ".input " << port.name.getValue() << "\n"; + else + os << ".clock " << port.name.getValue() << "\n"; + addValueName(body.getArgument(inIdx++), port.name); + break; + case hw::ModulePort::Direction::Output: + os << ".output " << port.name.getValue() << "\n"; + addValueName(term->getOperand(outIdx++), port.name); + break; + case hw::ModulePort::Direction::InOut: + emitOpError(op, "inout ports are not expected"); + break; + } + } + + // Emit the model body. + for (auto &bodyOp : body) { + if (encounteredError) + return; + TypeSwitch(&bodyOp) + .Case([&](auto op) { emitCommand(op); }) + .Case([&](auto op) { emitCommand(op); }) + .Case([&](auto op) {}) + .Default([&](auto op) { + emitOpError(op, "not supported for emission inside model definition"); + }); + } + + os << ".end\n"; +} + +void Emitter::emitCommand(LogicGateOp op) { + os << ".names"; + for (auto input : op.getInputs()) + os << " " << lookupEmittedName(input); + os << " " << lookupEmittedName(op.getResult()) << "\n"; + + for (auto row : op.getFunc()) { + const char *names[] = {"0", "1", "-"}; + for (auto input : ArrayRef(row).drop_back()) + os << names[input]; + if (row.size() > 1) + os << " "; + os << names[row.back()] << "\n"; + } +} + +void Emitter::emitCommand(LatchGateOp op) { + os << ".latch"; + os << " " << lookupEmittedName(op.getInput()); + os << " " << lookupEmittedName(op.getOutput()); + bool emitClk = true; + switch (op.getMode()) { + case LatchModeEnum::Unspecified: + emitClk = false; + break; + case LatchModeEnum::FallingEdge: + os << " fe "; + break; + case LatchModeEnum::RisingEdge: + os << " re "; + break; + case LatchModeEnum::ActiveHigh: + os << " ah "; + break; + case LatchModeEnum::ActiveLow: + os << " al "; + break; + case LatchModeEnum::Asynchronous: + os << " as "; + break; + } + if (emitClk) { + if (op.getClock()) + os << lookupEmittedName(op.getClock()); + else + os << "NIL"; + } + if (op.getInitVal() != 3) + os << " " << op.getInitVal(); + os << "\n"; +} + +//===----------------------------------------------------------------------===// +// Driver +//===----------------------------------------------------------------------===// + +// Emit the specified BLIF circuit into the given output stream. +mlir::LogicalResult circt::blif::exportBLIFFile(mlir::ModuleOp module, + llvm::raw_ostream &os) { + Emitter emitter(os); + for (auto &op : *module.getBody()) { + if (auto circuitOp = dyn_cast(op)) + emitter.emitModel(circuitOp); + } + return emitter.finalize(); +} + +void circt::blif::registerToBLIFFileTranslation() { + static mlir::TranslateFromMLIRRegistration toBLIF( + "export-blif", "emit BLIF dialect operations to .blif output", + [](ModuleOp module, llvm::raw_ostream &os) { + return exportBLIFFile(module, os); + }, + [](mlir::DialectRegistry ®istry) { + registry.insert(); + registry.insert(); + }); +} diff --git a/lib/Dialect/BLIF/Export/CMakeLists.txt b/lib/Dialect/BLIF/Export/CMakeLists.txt new file mode 100644 index 000000000000..eb340d1626a5 --- /dev/null +++ b/lib/Dialect/BLIF/Export/CMakeLists.txt @@ -0,0 +1,7 @@ +add_circt_translation_library(CIRCTExportBLIF + BLIFEmitter.cpp + + LINK_LIBS PUBLIC + CIRCTBLIF + MLIRTranslateLib +) diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp new file mode 100644 index 000000000000..881628533242 --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -0,0 +1,343 @@ +//===- BLIFLexer.cpp - .blif file lexer implementation --------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements a .blif file lexer. +// +//===----------------------------------------------------------------------===// + +#include "BLIFLexer.h" +#include "mlir/IR/Diagnostics.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" + +using namespace circt; +using namespace blif; +using llvm::SMLoc; +using llvm::SMRange; +using llvm::SourceMgr; + +#define isdigit(x) DO_NOT_USE_SLOW_CTYPE_FUNCTIONS +#define isalpha(x) DO_NOT_USE_SLOW_CTYPE_FUNCTIONS + +//===----------------------------------------------------------------------===// +// BLIFToken +//===----------------------------------------------------------------------===// + +SMLoc BLIFToken::getLoc() const { + return SMLoc::getFromPointer(spelling.data()); +} + +SMLoc BLIFToken::getEndLoc() const { + return SMLoc::getFromPointer(spelling.data() + spelling.size()); +} + +SMRange BLIFToken::getLocRange() const { + return SMRange(getLoc(), getEndLoc()); +} + +/// Return true if this is one of the keyword token kinds (e.g. kw_wire). +bool BLIFToken::isKeyword() const { + switch (kind) { + default: + return false; +#define TOK_KEYWORD(SPELLING) \ + case kw_##SPELLING: \ + return true; +#include "BLIFTokenKinds.def" + } +} + +/// Given a token containing a string literal, return its value, including +/// removing the quote characters and unescaping the contents of the string. The +/// lexer has already verified that this token is valid. +std::string BLIFToken::getStringValue(StringRef spelling) { + // Start by dropping the quotes. + StringRef bytes = spelling.drop_front().drop_back(); + + std::string result; + result.reserve(bytes.size()); + for (size_t i = 0, e = bytes.size(); i != e;) { + auto c = bytes[i++]; + if (c != '\\') { + result.push_back(c); + continue; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c1 = bytes[i++]; + switch (c1) { + case '\\': + case '"': + case '\'': + result.push_back(c1); + continue; + case 'b': + result.push_back('\b'); + continue; + case 'n': + result.push_back('\n'); + continue; + case 't': + result.push_back('\t'); + continue; + case 'f': + result.push_back('\f'); + continue; + case 'r': + result.push_back('\r'); + continue; + // TODO: Handle the rest of the escapes (octal and unicode). + default: + break; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c2 = bytes[i++]; + + assert(llvm::isHexDigit(c1) && llvm::isHexDigit(c2) && "invalid escape"); + result.push_back((llvm::hexDigitValue(c1) << 4) | llvm::hexDigitValue(c2)); + } + + return result; +} + +std::string BLIFToken::getVerbatimStringValue(StringRef spelling) { + // Start by dropping the quotes. + StringRef bytes = spelling.drop_front().drop_back(); + + std::string result; + result.reserve(bytes.size()); + for (size_t i = 0, e = bytes.size(); i != e;) { + auto c = bytes[i++]; + if (c != '\\') { + result.push_back(c); + continue; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c1 = bytes[i++]; + if (c1 != '\'') { + result.push_back(c); + } + result.push_back(c1); + } + + return result; +} + +//===----------------------------------------------------------------------===// +// BLIFLexer +//===----------------------------------------------------------------------===// + +static StringAttr getMainBufferNameIdentifier(const llvm::SourceMgr &sourceMgr, + MLIRContext *context) { + auto mainBuffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID()); + StringRef bufferName = mainBuffer->getBufferIdentifier(); + if (bufferName.empty()) + bufferName = ""; + return StringAttr::get(context, bufferName); +} + +BLIFLexer::BLIFLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context) + : sourceMgr(sourceMgr), + bufferNameIdentifier(getMainBufferNameIdentifier(sourceMgr, context)), + curBuffer( + sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()), + curPtr(curBuffer.begin()), + // Prime the BLIFst token. + curToken(lexTokenImpl()) {} + +/// Encode the specified source location information into a Location object +/// for attachment to the IR or error reporting. +Location BLIFLexer::translateLocation(llvm::SMLoc loc) { + assert(loc.isValid()); + unsigned mainFileID = sourceMgr.getMainFileID(); + auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID); + return FileLineColLoc::get(bufferNameIdentifier, lineAndColumn.first, + lineAndColumn.second); +} + +/// Emit an error message and return a BLIFToken::error token. +BLIFToken BLIFLexer::emitError(const char *loc, const Twine &message) { + mlir::emitError(translateLocation(SMLoc::getFromPointer(loc)), message); + return formToken(BLIFToken::error, loc); +} + +//===----------------------------------------------------------------------===// +// Lexer Implementation Methods +//===----------------------------------------------------------------------===// + +BLIFToken BLIFLexer::lexTokenImpl() { + while (true) { + const char *tokStart = curPtr; + switch (*curPtr++) { + default: + // Handle identifiers. + if (llvm::isAlpha(curPtr[-1])) + return lexIdentifier(tokStart); + + // Unknown character, emit an error. + return emitError(tokStart, "unexpected character"); + + case 0: + // This may either be a nul character in the source file or may be the EOF + // marker that llvm::MemoryBuffer guarantees will be there. + if (curPtr - 1 == curBuffer.end()) + return formToken(BLIFToken::eof, tokStart); + + [[fallthrough]]; // Treat as whitespace. + + case '\\': + // Handle line continuations. + if (*curPtr == '\r') { + ++curPtr; + } + if (*curPtr == '\n') { + ++curPtr; + continue; + } + + case '\n': + // return formToken(BLIFToken::newline, tokStart); + + case ' ': + case '\t': + case '\r': + // Handle whitespace. + continue; + + case '.': + return lexCommand(tokStart); + + case '#': + skipComment(); + continue; + + case '-': + case '0': + case '1': + return lexNumberOrCover(tokStart); + + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(tokStart); + } + } +} + +/// Lex a period or a keyword that starts with a period. +/// +/// Command :== '.' [a-zA-Z_]+ +/// +BLIFToken BLIFLexer::lexCommand(const char *tokStart) { + + // Match the rest of the command regex: [a-zA-Z_]* + while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_') + ++curPtr; + + StringRef spelling(tokStart, curPtr - tokStart); + + // See if the identifier is a keyword. + BLIFToken::Kind kind = llvm::StringSwitch(spelling) +#define TOK_KEYWORD_DOT(SPELLING) .Case("." #SPELLING, BLIFToken::kw_##SPELLING) +#include "BLIFTokenKinds.def" + .Default(BLIFToken::error); + if (kind != BLIFToken::error) { + ++curPtr; + return formToken(kind, tokStart); + } + + // Otherwise, this is a period. + return emitError(tokStart, "unexpected character after period"); +} + +/// Lex an identifier. +/// +/// LegalStartChar ::= [a-zA-Z] +/// LegalIdChar ::= LegalStartChar | [0-9] | '$' | '_' +/// +/// Id ::= LegalStartChar (LegalIdChar)* +/// +BLIFToken BLIFLexer::lexIdentifier(const char *tokStart) { + + // Match the rest of the identifier regex: [0-9a-zA-Z$_]* + while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_' || + *curPtr == '$') + ++curPtr; + + return formToken(BLIFToken::identifier, tokStart); +} + +/// Skip a comment line, starting with a '#' and going to end of line. +void BLIFLexer::skipComment() { + while (true) { + switch (*curPtr++) { + case '\n': + case '\r': + // Newline is end of comment. + return; + case 0: + // If this is the end of the buffer, end the comment. + if (curPtr - 1 == curBuffer.end()) { + --curPtr; + return; + } + [[fallthrough]]; + default: + // Skip over other characters. + break; + } + } +} + +/// Lex a number literal. +/// +/// UnsignedInt ::= '0' | PosInt +/// PosInt ::= [1-9] ([0-9])* +/// +BLIFToken BLIFLexer::lexNumber(const char *tokStart) { + assert(llvm::isDigit(curPtr[-1]) || curPtr[-1] == '-'); + + // There needs to be at least one digit. + if (!llvm::isDigit(*curPtr) && !llvm::isDigit(curPtr[-1])) + return emitError(tokStart, "unexpected character after sign"); + + while (llvm::isDigit(*curPtr)) + ++curPtr; + + return formToken(BLIFToken::integer, tokStart); +} + +/// Lex a number literal or a cover literal +/// +/// +/// Cover ::= [0-9\-]* +/// +BLIFToken BLIFLexer::lexNumberOrCover(const char *tokStart) { + while (llvm::isDigit(*curPtr) || *curPtr == '-') + ++curPtr; + + StringRef spelling(tokStart, curPtr - tokStart); + if (spelling.contains('2') || spelling.contains('3') || + spelling.contains('4') || spelling.contains('5') || + spelling.contains('6') || spelling.contains('7') || + spelling.contains('8') || spelling.contains('9') || + !spelling.contains('-')) { + curPtr = tokStart + 1; + return lexNumber(tokStart); + } + return formToken(BLIFToken::integer, tokStart); +} diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h new file mode 100644 index 000000000000..0742c2d8746a --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -0,0 +1,177 @@ +//===- BLIFLexer.h - .blif lexer and token definitions ----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the a Lexer and Token interface for .blif files. +// +//===----------------------------------------------------------------------===// + +#ifndef BLIFTOMLIR_BLIFLEXER_H +#define BLIFTOMLIR_BLIFLEXER_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "llvm/Support/SourceMgr.h" + +namespace mlir { +class MLIRContext; +class Location; +} // namespace mlir + +namespace circt { +namespace blif { + +/// This represents a specific token for .fir files. +class BLIFToken { +public: + enum Kind { +#define TOK_MARKER(NAME) NAME, +#define TOK_IDENTIFIER(NAME) NAME, +#define TOK_LITERAL(NAME) NAME, +#define TOK_PUNCTUATION(NAME, SPELLING) NAME, +#define TOK_KEYWORD(SPELLING) kw_##SPELLING, +#define TOK_KEYWORD_DOT(SPELLING) kw_##SPELLING, +#include "BLIFTokenKinds.def" + }; + + BLIFToken(Kind kind, StringRef spelling) : kind(kind), spelling(spelling) {} + + // Return the bytes that make up this token. + StringRef getSpelling() const { return spelling; } + + // Token classification. + Kind getKind() const { return kind; } + bool is(Kind K) const { return kind == K; } + + bool isAny(Kind k1, Kind k2) const { return is(k1) || is(k2); } + + /// Return true if this token is one of the specified kinds. + template + bool isAny(Kind k1, Kind k2, Kind k3, T... others) const { + if (is(k1)) + return true; + return isAny(k2, k3, others...); + } + + bool isNot(Kind k) const { return kind != k; } + + /// Return true if this token isn't one of the specified kinds. + template + bool isNot(Kind k1, Kind k2, T... others) const { + return !isAny(k1, k2, others...); + } + + /// Return true if this is one of the keyword token kinds (e.g. kw_wire). + bool isKeyword() const; + + bool isModelHeaderKeyword() const { + return isAny(kw_inputs, kw_outputs, kw_clocks, kw_input, kw_output, + kw_clock); + } + + /// Given a token containing a string literal, return its value, including + /// removing the quote characters and unescaping the contents of the string. + /// The lexer has already verified that this token is valid. + std::string getStringValue() const; + static std::string getStringValue(StringRef spelling); + + /// Given a token containing a verbatim string, return its value, including + /// removing the quote characters and unescaping the quotes of the string. The + /// lexer has already verified that this token is valid. + std::string getVerbatimStringValue() const; + static std::string getVerbatimStringValue(StringRef spelling); + + // Location processing. + llvm::SMLoc getLoc() const; + llvm::SMLoc getEndLoc() const; + llvm::SMRange getLocRange() const; + +private: + /// Discriminator that indicates the sort of token this is. + Kind kind; + + /// A reference to the entire token contents; this is always a pointer into + /// a memory buffer owned by the source manager. + StringRef spelling; +}; + +class BLIFLexerCursor; + +/// This implements a lexer for .fir files. +class BLIFLexer { +public: + BLIFLexer(const llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context); + + const llvm::SourceMgr &getSourceMgr() const { return sourceMgr; } + + /// Move to the next valid token. + void lexToken() { curToken = lexTokenImpl(); } + + const BLIFToken &getToken() const { return curToken; } + + mlir::Location translateLocation(llvm::SMLoc loc); + + /// Get an opaque pointer into the lexer state that can be restored later. + BLIFLexerCursor getCursor() const; + +private: + BLIFToken lexTokenImpl(); + + // Helpers. + BLIFToken formToken(BLIFToken::Kind kind, const char *tokStart) { + return BLIFToken(kind, StringRef(tokStart, curPtr - tokStart)); + } + + BLIFToken emitError(const char *loc, const Twine &message); + + // Lexer implementation methods. + BLIFToken lexFileInfo(const char *tokStart); + BLIFToken lexIdentifier(const char *tokStart); + BLIFToken lexNumber(const char *tokStart); + BLIFToken lexNumberOrCover(const char *tokStart); + void skipComment(); + BLIFToken lexString(const char *tokStart, bool isVerbatim); + BLIFToken lexCommand(const char *tokStart); + + const llvm::SourceMgr &sourceMgr; + const mlir::StringAttr bufferNameIdentifier; + + StringRef curBuffer; + const char *curPtr; + + /// This is the next token that hasn't been consumed yet. + BLIFToken curToken; + + BLIFLexer(const BLIFLexer &) = delete; + void operator=(const BLIFLexer &) = delete; + friend class BLIFLexerCursor; +}; + +/// This is the state captured for a lexer cursor. +class BLIFLexerCursor { +public: + BLIFLexerCursor(const BLIFLexer &lexer) + : state(lexer.curPtr), curToken(lexer.getToken()) {} + + void restore(BLIFLexer &lexer) { + lexer.curPtr = state; + lexer.curToken = curToken; + } + +private: + const char *state; + BLIFToken curToken; +}; + +inline BLIFLexerCursor BLIFLexer::getCursor() const { + return BLIFLexerCursor(*this); +} + +} // namespace blif +} // namespace circt + +#endif // BLIFTOMLIR_BLIFLEXER_H diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp new file mode 100644 index 000000000000..643f2b772493 --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -0,0 +1,781 @@ +//===- FIRParser.cpp - .fir to FIRRTL dialect parser ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements a .fir file parser. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFParser.h" +#include "BLIFLexer.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "circt/Dialect/HW/HWAttributes.h" +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Threading.h" +#include "mlir/IR/Verifier.h" +#include "mlir/Support/Timing.h" +#include "mlir/Tools/mlir-translate/Translation.h" +#include "llvm/ADT/PointerEmbeddedInt.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace circt; +using namespace blif; + +using llvm::SMLoc; +using llvm::SourceMgr; +using mlir::LocationAttr; + +//===----------------------------------------------------------------------===// +// BLIFParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements logic common to all levels of the parser, including +/// things like types and helper logic. +struct BLIFParser { + BLIFParser(MLIRContext *ctx, BLIFLexer &lexer) : context(ctx), lexer(lexer) {} + + MLIRContext *getContext() const { return context; } + + BLIFLexer &getLexer() { return lexer; } + + /// Return the current token the parser is inspecting. + const BLIFToken &getToken() const { return lexer.getToken(); } + StringRef getTokenSpelling() const { return getToken().getSpelling(); } + + //===--------------------------------------------------------------------===// + // Error Handling + //===--------------------------------------------------------------------===// + + /// Emit an error and return failure. + InFlightDiagnostic emitError(const Twine &message = {}) { + return emitError(getToken().getLoc(), message); + } + + InFlightDiagnostic emitError(SMLoc loc, const Twine &message = {}); + + /// Emit a warning. + InFlightDiagnostic emitWarning(const Twine &message = {}) { + return emitWarning(getToken().getLoc(), message); + } + + InFlightDiagnostic emitWarning(SMLoc loc, const Twine &message = {}); + + //===--------------------------------------------------------------------===// + // Location Handling + //===--------------------------------------------------------------------===// + + class LocWithInfo; + + /// Encode the specified source location information into an attribute for + /// attachment to the IR. + Location translateLocation(llvm::SMLoc loc) { + return lexer.translateLocation(loc); + } + + //===--------------------------------------------------------------------===// + // Token Parsing + //===--------------------------------------------------------------------===// + + /// If the current token has the specified kind, consume it and return true. + /// If not, return false. + bool consumeIf(BLIFToken::Kind kind) { + if (getToken().isNot(kind)) + return false; + consumeToken(kind); + return true; + } + + /// Advance the current lexer onto the next token. + /// + /// This returns the consumed token. + BLIFToken consumeToken() { + BLIFToken consumedToken = getToken(); + assert(consumedToken.isNot(BLIFToken::eof, BLIFToken::error) && + "shouldn't advance past EOF or errors"); + lexer.lexToken(); + return consumedToken; + } + + /// Advance the current lexer onto the next token, asserting what the expected + /// current token is. This is preferred to the above method because it leads + /// to more self-documenting code with better checking. + /// + /// This returns the consumed token. + BLIFToken consumeToken(BLIFToken::Kind kind) { + BLIFToken consumedToken = getToken(); + assert(consumedToken.is(kind) && "consumed an unexpected token"); + consumeToken(); + return consumedToken; + } + + /// Capture the current token's spelling into the specified value. This + /// always succeeds. + ParseResult parseGetSpelling(StringRef &spelling) { + spelling = getTokenSpelling(); + return success(); + } + + /// Consume the specified token if present and return success. On failure, + /// output a diagnostic and return failure. + ParseResult parseToken(BLIFToken::Kind expectedToken, const Twine &message); + + /// Parse a comma-separated list of elements, terminated with an arbitrary + /// token. + ParseResult parseListUntil(BLIFToken::Kind rightToken, + const std::function &parseElement); + + //===--------------------------------------------------------------------===// + // Common Parser Rules + //===--------------------------------------------------------------------===// + + // Parse the 'id' grammar, which is an identifier or an allowed keyword. + ParseResult parseId(StringRef &result, const Twine &message); + ParseResult parseId(StringAttr &result, const Twine &message); + + // Parse the 'id' grammar or the string literal "NIL" + ParseResult parseIdOrNil(StringRef &result, const Twine &message); + ParseResult parseIdOrNil(StringAttr &result, const Twine &message); + + ParseResult parseIdList(SmallVectorImpl &result, + const Twine &message, unsigned minCount = 0); + + ParseResult parseOptionalInt(int &result); + +private: + BLIFParser(const BLIFParser &) = delete; + void operator=(const BLIFParser &) = delete; + + /// The context in which we are parsing. + MLIRContext *context; + + /// BLIFParser is subclassed and reinstantiated. Do not add additional + /// non-trivial state here, add it to SharedParserConstants. + BLIFLexer &lexer; +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Error Handling +//===----------------------------------------------------------------------===// + +InFlightDiagnostic BLIFParser::emitError(SMLoc loc, const Twine &message) { + auto diag = mlir::emitError(translateLocation(loc), message); + + // If we hit a parse error in response to a lexer error, then the lexer + // already reported the error. + if (getToken().is(BLIFToken::error)) + diag.abandon(); + return diag; +} + +InFlightDiagnostic BLIFParser::emitWarning(SMLoc loc, const Twine &message) { + return mlir::emitWarning(translateLocation(loc), message); +} + +//===----------------------------------------------------------------------===// +// Token Parsing +//===----------------------------------------------------------------------===// + +/// Consume the specified token if present and return success. On failure, +/// output a diagnostic and return failure. +ParseResult BLIFParser::parseToken(BLIFToken::Kind expectedToken, + const Twine &message) { + if (consumeIf(expectedToken)) + return success(); + return emitError(message); +} + +//===--------------------------------------------------------------------===// +// Common Parser Rules +//===--------------------------------------------------------------------===// + +/// id ::= Id +/// +/// Parse the 'id' grammar, which is a trivial string. On +/// success, this returns the identifier in the result attribute. +ParseResult BLIFParser::parseId(StringRef &result, const Twine &message) { + switch (getToken().getKind()) { + // The most common case is an identifier. + case BLIFToken::identifier: + result = getTokenSpelling(); + consumeToken(); + return success(); + + default: + emitError(message); + return failure(); + } +} + +ParseResult BLIFParser::parseId(StringAttr &result, const Twine &message) { + StringRef name; + if (parseId(name, message)) + return failure(); + + result = StringAttr::get(getContext(), name); + return success(); +} + +ParseResult BLIFParser::parseIdList(SmallVectorImpl &result, + const Twine &message, unsigned minCount) { + while (true) { + switch (getToken().getKind()) { + // The most common case is an identifier. + case BLIFToken::identifier: + result.push_back(getTokenSpelling()); + consumeToken(); + if (minCount) + --minCount; + break; + default: + if (minCount) { + emitError(message); + return failure(); + } + return success(); + } + } +} + +/// id ::= Id +/// +/// Parse the 'id' grammar, which is a trivial string. On +/// success, this returns the identifier in the result attribute. +ParseResult BLIFParser::parseOptionalInt(int &result) { + if (getToken().getKind() == BLIFToken::integer) { + if (getTokenSpelling().getAsInteger(10, result)) { + emitError("invalid integer value"); + return failure(); + } + consumeToken(); + return success(); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// BLIFModelParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements logic and state for parsing statements, suites, and +/// similar module body constructs. +struct BLIFModelParser : public BLIFParser { + explicit BLIFModelParser(Block &blockToInsertInto, BLIFLexer &lexer) + : BLIFParser(blockToInsertInto.getParentOp()->getContext(), lexer), + builder(UnknownLoc::get(getContext()), getContext()) { + builder.setInsertionPoint(&blockToInsertInto, blockToInsertInto.begin()); + unresolved = builder + .create( + builder.getIntegerType(1), ValueRange()) + .getResult(0); + ModelOp model = cast(blockToInsertInto.getParentOp()); + int idx = 0; + for (auto port : model.getModuleType().getPorts()) { + if (port.dir == hw::ModulePort::Direction::Input) + setNamedValue(port.name.getValue(), + blockToInsertInto.getArgument(idx++)); + } + } + + ParseResult parseModelBody(); + ParseResult fixupUnresolvedValues(); + ParseResult fixupOutput(); + +private: + ParseResult parseSimpleStmtImpl(); + + ParseResult parseOptionalTruthLine(StringRef &input, bool &output); + ParseResult parseTruthTable(SmallVectorImpl &inputs, + SmallVectorImpl &outputs); + ParseResult parseLogicGate(); + ParseResult parseOptionalLatchType(LatchModeEnum &lType, StringRef &name); + ParseResult parseLatch(); + ParseResult parseLibraryLogicGate(); + ParseResult parseLibraryLatch(); + ParseResult parseModelReference(); + ParseResult parseModelCmdImpl(); + ParseResult parseModelCmd(); + + // Helper to fetch a module referenced by an instance-like statement. + Value getReferencedModel(SMLoc loc, StringRef modelName); + + /// Find the value corrosponding to the named identifier. + Value getNamedValue(StringRef name); + + /// Set the value corrosponding to the named identifier. + void setNamedValue(StringRef name, Value value); + + /// Remember to fix an op whose value could not be resolved + void addFixup(Operation *op, unsigned idx, StringRef name); + + // The builder to build into. + ImplicitLocOpBuilder builder; + + /// Keep track of names; + DenseMap namedValues; + + struct fixup { + Operation *op; + unsigned idx; + }; + + DenseMap> fixups; + + Value unresolved; +}; + +} // end anonymous namespace + +Value BLIFModelParser::getNamedValue(StringRef name) { + auto val = namedValues.lookup(name); + return val ? val : unresolved; +} + +void BLIFModelParser::setNamedValue(StringRef name, Value value) { + assert(!namedValues[name] && "name already exists"); + namedValues[name] = value; +} + +void BLIFModelParser::addFixup(Operation *op, unsigned idx, StringRef name) { + fixups[name].push_back({op, idx}); +} + +Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { + return {}; +} + +ParseResult BLIFModelParser::parseOptionalTruthLine(StringRef &input, + bool &output) { + switch (getToken().getKind()) { + case BLIFToken::cover: + case BLIFToken::integer: + input = getTokenSpelling(); + consumeToken(); + break; + default: + return failure(); + } + + switch (getToken().getKind()) { + case BLIFToken::integer: + if (getTokenSpelling() == "0") { + consumeToken(); + output = false; + return success(); + } + if (getTokenSpelling() == "1") { + consumeToken(); + output = true; + return success(); + } + break; + default: + break; + } + + return failure(); +} + +ParseResult BLIFModelParser::parseTruthTable(SmallVectorImpl &inputs, + SmallVectorImpl &outputs) { + StringRef input; + bool output; + while (succeeded(parseOptionalTruthLine(input, output))) { + inputs.push_back(input); + outputs.push_back(output); + } + + return success(); +} + +/// logicgate ::= '.names' in* out NEWLINE single_output_cover* +ParseResult BLIFModelParser::parseLogicGate() { + consumeToken(BLIFToken::kw_names); + SmallVector inputs; + StringRef output; + if (parseIdList(inputs, "expected input list", 1)) + return failure(); + output = inputs.back(); + inputs.pop_back(); + // Deal with truth table + SmallVector covers; + SmallVector sets; + if (parseTruthTable(covers, sets)) + return failure(); + + SmallVector> truthTable; + for (auto [i, o] : llvm::zip_equal(covers, sets)) { + SmallVector row; + for (auto c : i) + if (c == '0') + row.push_back(0); + else if (c == '1') + row.push_back(1); + else if (c == '-') + row.push_back(2); + else + return emitError("invalid truth table value '") << c << "'"; + row.push_back(o); + truthTable.push_back(row); + } + + // Resolve Inputs + SmallVector inputValues; + for (auto input : inputs) + inputValues.push_back(getNamedValue(input)); + // BuildOp + auto op = builder.create(builder.getIntegerType(1), truthTable, + inputValues); + // Record output name + setNamedValue(output, op.getResult()); + // Record Fixups + for (auto [idx, name, val] : llvm::enumerate(inputs, inputValues)) { + if (val == unresolved) + addFixup(op, idx, name); + } + return success(); +} + +ParseResult BLIFModelParser::parseOptionalLatchType(LatchModeEnum &lType, + StringRef &clkName) { + if (getToken().getKind() == BLIFToken::identifier) { + lType = llvm::StringSwitch(getToken().getSpelling()) + .Case("fe", LatchModeEnum::FallingEdge) + .Case("re", LatchModeEnum::RisingEdge) + .Case("ah", LatchModeEnum::ActiveHigh) + .Case("al", LatchModeEnum::ActiveLow) + .Case("as", LatchModeEnum::Asynchronous) + .Default(LatchModeEnum::Unspecified); + if (lType == LatchModeEnum::Unspecified) { + return success(); + } + consumeToken(); + return parseId(clkName, "Expected clock signal"); + } + return success(); +} + +/// latch ::= '.latch' id_in id_out [latch_type [id | "NIL"]]? [ '0' | '1' | '2' +/// | '3']? +ParseResult BLIFModelParser::parseLatch() { + consumeToken(BLIFToken::kw_latch); + StringRef input; + StringRef output; + // latchType lType; + std::string clock; + int init_val = 3; // default + if (parseId(input, "Expected input signal") || + parseId(output, "Expected output signal")) + return failure(); + + LatchModeEnum lType = LatchModeEnum::Unspecified; + StringRef clkName; + if (parseOptionalLatchType(lType, clkName) || parseOptionalInt(init_val)) + return emitError("invalid latch type"), failure(); + + if (init_val < 0 || init_val > 3) + return emitError("invalid initial latch value '") << init_val << "'", + failure(); + + Value inVal = getNamedValue(input); + Value clkVal = ((clkName == "NIL") | (lType == LatchModeEnum::Unspecified)) + ? Value() + : getNamedValue(clkName); + + auto op = builder.create(builder.getIntegerType(1), inVal, lType, + clkVal, init_val); + // Record output name + setNamedValue(output, op.getResult()); + // Record Fixups + if (inVal == unresolved) + addFixup(op, 0, input); + + return success(); +} + +// liblogic ::= '.gate' id formal-actual-list +ParseResult BLIFModelParser::parseLibraryLogicGate() { return failure(); } + +// liblatch ::= '.mlatch' id formal-actual-list [id | "NIL"] [ '0' | '1' | '2' | +// '3']? +ParseResult BLIFModelParser::parseLibraryLatch() { return failure(); } + +// mref ::= '.subskt' id formal-actual-list +ParseResult BLIFModelParser::parseModelReference() { return failure(); } + +/// model_cmd ::= stmt +/// +/// stmt ::= .names +/// ::= .latch +/// ::= .gate +/// ::= .mlatch +/// ::= .subskt +/// +ParseResult BLIFModelParser::parseModelCmdImpl() { + auto kind = getToken().getKind(); + switch (kind) { + // Statements. + case BLIFToken::kw_names: + return parseLogicGate(); + case BLIFToken::kw_latch: + return parseLatch(); + case BLIFToken::kw_gate: + return parseLibraryLogicGate(); + case BLIFToken::kw_mlatch: + return parseLibraryLatch(); + case BLIFToken::kw_subskt: + return parseModelReference(); + default: + return emitError("unexpected token in model command"), failure(); + } +} + +ParseResult BLIFModelParser::parseModelCmd() { + // locationProcessor.startStatement(); + auto result = parseModelCmdImpl(); + // locationProcessor.endStatement(*this); + return result; +} + +// Parse the body of this module. +ParseResult BLIFModelParser::parseModelBody() { + + while (true) { + // The outer level parser can handle these tokens. + if (getToken().isAny(BLIFToken::eof, BLIFToken::error, BLIFToken::kw_end)) + return success(); + + // Let the statement parser handle this. + if (parseModelCmd()) + return failure(); + } + + return success(); +} + +ParseResult BLIFModelParser::fixupUnresolvedValues() { + for (auto &fixup : fixups) { + Value val = getNamedValue(fixup.first); + if (val == unresolved) + return emitError("unresolved value '") << fixup.first << "'"; + for (auto [op, idx] : fixup.second) { + op->setOperand(idx, val); + } + } + return success(); +} + +ParseResult BLIFModelParser::fixupOutput() { + auto model = cast(builder.getBlock()->getParentOp()); + SmallVector outputs; + for (auto port : model.getModuleType().getPorts()) { + if (port.dir == hw::ModulePort::Direction::Output) { + auto val = getNamedValue(port.name.getValue()); + if (val == unresolved) + emitWarning("unresolved output '") << port.name.getValue() << "'"; + outputs.push_back(val); + } + } + builder.create(outputs); + if (unresolved && unresolved.use_empty()) { + unresolved.getDefiningOp()->erase(); + unresolved = nullptr; + } + return success(); +} + +//===----------------------------------------------------------------------===// +// BLIFFileParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements the parser, namely models +struct BLIFFileParser : public BLIFParser { + explicit BLIFFileParser(BLIFLexer &lexer, ModuleOp mlirModule) + : BLIFParser(mlirModule.getContext(), lexer), mlirModule(mlirModule), + builder(UnknownLoc::get(getContext()), getContext()) { + builder.setInsertionPointToStart(mlirModule.getBody()); + } + + ParseResult parseFile(); + +private: + ParseResult parseModel(); + + ModuleOp mlirModule; + ImplicitLocOpBuilder builder; +}; + +} // end anonymous namespace + +/// model ::= '.model' id '\n' [input_line, output_line, clock_line]* +/// model_cmds* `.end` +ParseResult BLIFFileParser::parseModel() { + StringAttr name; + SmallVector inputs, outputs, clocks; + consumeToken(BLIFToken::kw_model); + if (parseId(name, "expected model name")) + return failure(); + while (getToken().isModelHeaderKeyword()) { + switch (getToken().getKind()) { + case BLIFToken::kw_inputs: + consumeToken(BLIFToken::kw_inputs); + if (parseIdList(inputs, "expected input list", 1)) + return failure(); + break; + case BLIFToken::kw_input: { + consumeToken(BLIFToken::kw_input); + StringRef tmp; + if (parseId(tmp, "expected input")) + return failure(); + inputs.push_back(tmp); + break; + } + case BLIFToken::kw_outputs: + consumeToken(BLIFToken::kw_outputs); + if (parseIdList(outputs, "expected output list", 1)) + return failure(); + break; + case BLIFToken::kw_output: { + consumeToken(BLIFToken::kw_output); + StringRef tmp; + if (parseId(tmp, "expected output")) + return failure(); + outputs.push_back(tmp); + break; + } + case BLIFToken::kw_clocks: + consumeToken(BLIFToken::kw_clocks); + if (parseIdList(clocks, "expected clock list", 1)) + return failure(); + break; + case BLIFToken::kw_clock: { + consumeToken(BLIFToken::kw_clock); + StringRef tmp; + if (parseId(tmp, "expected clock")) + return failure(); + clocks.push_back(tmp); + break; + } + default: + break; + } + } + // Create the model + auto m = builder.create(name, inputs, outputs, clocks); + OpBuilder::InsertionGuard guard(builder); + BLIFModelParser bodyParser(m.getBody().back(), getLexer()); + if (bodyParser.parseModelBody() || bodyParser.fixupUnresolvedValues() || + bodyParser.fixupOutput() || + parseToken(BLIFToken::kw_end, "expected .end")) + return failure(); + + // Fix up outputs + + return success(); +} + +/// file ::= model+ +ParseResult BLIFFileParser::parseFile() { + + while (!getToken().is(BLIFToken::eof)) { + // Parse the next model in the file + if (getToken().is(BLIFToken::kw_model)) { + if (parseModel()) + return failure(); + } else { + emitError("unexpected token in file"); + return failure(); + } + } + + // Parse any contained modules. + while (true) { + switch (getToken().getKind()) { + // If we got to the end of the file, then we're done. + case BLIFToken::eof: + goto DoneParsing; + + // If we got an error token, then the lexer already emitted an error, + // just stop. We could introduce error recovery if there was demand for + // it. + case BLIFToken::error: + return failure(); + + default: + emitError("unexpected token in circuit"); + return failure(); + + case BLIFToken::kw_model: + if (parseModel()) + return failure(); + break; + } + } + +DoneParsing: + // TODO: Fixup references + return success(); +} + +//===----------------------------------------------------------------------===// +// Driver +//===----------------------------------------------------------------------===// + +// Parse the specified .fir file into the specified MLIR context. +mlir::OwningOpRef +circt::blif::importBLIFFile(SourceMgr &sourceMgr, MLIRContext *context, + mlir::TimingScope &ts, BLIFParserOptions options) { + auto sourceBuf = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID()); + + context->loadDialect(); + + // This is the result module we are parsing into. + mlir::OwningOpRef module(ModuleOp::create( + FileLineColLoc::get(context, sourceBuf->getBufferIdentifier(), + /*line=*/0, + /*column=*/0))); + // SharedParserConstants state(context, options); + BLIFLexer lexer(sourceMgr, context); + if (BLIFFileParser(lexer, *module).parseFile()) + return nullptr; + + // Make sure the parse module has no other structural problems detected by + // the verifier. + auto circuitVerificationTimer = ts.nest("Verify circuit"); + if (failed(verify(*module))) + return {}; + + return module; +} + +void circt::blif::registerFromBLIFFileTranslation() { + static mlir::TranslateToMLIRRegistration fromBLIF( + "import-blif", "import .blif", + [](llvm::SourceMgr &sourceMgr, MLIRContext *context) { + mlir::TimingScope ts; + return importBLIFFile(sourceMgr, context, ts); + }); +} diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def new file mode 100644 index 000000000000..22c531bef095 --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -0,0 +1,98 @@ +//===- BLIFTokenKinds.def - .blif file Token Descriptions -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file is intended to be #include'd multiple times to extract information +// about tokens for various clients in the lexer. +// +//===----------------------------------------------------------------------===// + +#if !defined(TOK_MARKER) && !defined(TOK_IDENTIFIER) && \ + !defined(TOK_LITERAL) && !defined(TOK_PUNCTUATION) && \ + !defined(TOK_KEYWORD) && !defined(TOK_KEYWORD_DOT) +#error Must define one of the TOK_ macros. +#endif + +#ifndef TOK_MARKER +#define TOK_MARKER(X) +#endif +#ifndef TOK_IDENTIFIER +#define TOK_IDENTIFIER(NAME) +#endif +#ifndef TOK_LITERAL +#define TOK_LITERAL(NAME) +#endif +#ifndef TOK_PUNCTUATION +#define TOK_PUNCTUATION(NAME, SPELLING) +#endif +#ifndef TOK_KEYWORD +#define TOK_KEYWORD(SPELLING) +#endif +#ifndef TOK_KEYWORD_DOT +#define TOK_KEYWORD_DOT(SPELLING) TOK_KEYWORD(SPELLING) +#endif + +// Markers +TOK_MARKER(eof) +TOK_MARKER(error) + +// Identifiers. +TOK_IDENTIFIER(identifier) // foo + +// Literals +TOK_LITERAL(integer) // 42 +TOK_LITERAL(signed_integer) // -42 and +42 +TOK_LITERAL(radix_specified_integer) // 0b101010, 0o52, 0d42, 0h2a and negations +TOK_LITERAL(cover) // 0--1- +TOK_LITERAL(bit) // 0 || 1 + +// Keywords. These turn "foo" into FIRToken::kw_foo enums. + +// NOTE: Please key these alphabetized to make it easier to find something in +// this list and to cater to OCD. + +// Keywords which start with '.'. These are separated as the kw_foo must be formed from '.foo'. +TOK_KEYWORD_DOT(area) +TOK_KEYWORD_DOT(cycle) +TOK_KEYWORD_DOT(clock) +TOK_KEYWORD_DOT(clocks) +TOK_KEYWORD_DOT(clock_event) +TOK_KEYWORD_DOT(default_input_arrival) +TOK_KEYWORD_DOT(default_input_drive) +TOK_KEYWORD_DOT(default_max_input_load) +TOK_KEYWORD_DOT(default_output_load) +TOK_KEYWORD_DOT(default_output_required) +TOK_KEYWORD_DOT(delay) +TOK_KEYWORD_DOT(end) +TOK_KEYWORD_DOT(end_kiss) +TOK_KEYWORD_DOT(exdc) +TOK_KEYWORD_DOT(gate) +TOK_KEYWORD_DOT(input) +TOK_KEYWORD_DOT(inputs) +TOK_KEYWORD_DOT(input_arrival) +TOK_KEYWORD_DOT(input_drive) +TOK_KEYWORD_DOT(latch) +TOK_KEYWORD_DOT(max_input_load) +TOK_KEYWORD_DOT(mlatch) +TOK_KEYWORD_DOT(model) +TOK_KEYWORD_DOT(names) +TOK_KEYWORD_DOT(output) +TOK_KEYWORD_DOT(outputs) +TOK_KEYWORD_DOT(output_load) +TOK_KEYWORD_DOT(output_required) +TOK_KEYWORD_DOT(search) +TOK_KEYWORD_DOT(start_kiss) +TOK_KEYWORD_DOT(subskt) +TOK_KEYWORD_DOT(wire) +TOK_KEYWORD_DOT(wire_load_slope) + +#undef TOK_MARKER +#undef TOK_IDENTIFIER +#undef TOK_LITERAL +#undef TOK_PUNCTUATION +#undef TOK_KEYWORD +#undef TOK_KEYWORD_DOT diff --git a/lib/Dialect/BLIF/Import/CMakeLists.txt b/lib/Dialect/BLIF/Import/CMakeLists.txt new file mode 100644 index 000000000000..d5ee02e985fd --- /dev/null +++ b/lib/Dialect/BLIF/Import/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_circt_translation_library(CIRCTImportBLIFFile + BLIFLexer.cpp + BLIFParser.cpp + + ADDITIONAL_HEADER_DIRS + + LINK_LIBS PUBLIC + CIRCTBLIF + MLIRTranslateLib + ) diff --git a/lib/Dialect/CMakeLists.txt b/lib/Dialect/CMakeLists.txt index a7bf7be67e1f..eeb8b3d03bc0 100644 --- a/lib/Dialect/CMakeLists.txt +++ b/lib/Dialect/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(AIG) add_subdirectory(Arc) +add_subdirectory(BLIF) add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif new file mode 100644 index 000000000000..e45deab80d24 --- /dev/null +++ b/test/Dialect/BLIF/parse-basic.blif @@ -0,0 +1,30 @@ +# RUN: circt-translate -import-blif -verify-diagnostics %s | circt-opt | FileCheck %s + +# CHECK: blif.model +.model simple +.input a +.output b +.clock clk1 +.inputs c d +.outputs e f +.clocks clk2 clk3 +# CHECK: blif.logic_gate +# CHECK-SAME: func = [array, array, array] +.names a c b +11 1 +0- 1 +-0 1 +.names foo1 foo2 e # use before def +00 1 +.names c d foo1 +-0 1 +.names clk1 clk2 clk3 foo2 +1-0 0 +.latch foo2 foo3 +.latch foo3 foo4 2 +.latch foo4 foo5 al NIL +.latch foo5 foo6 fe a +.latch foo6 foo7 re a 1 +.names foo1 foo7 f +-1 1 +.end diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 6ff8c38100cd..791317c1f457 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -22,7 +22,7 @@ config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.td', '.mlir', '.ll', '.fir', '.sv'] +config.suffixes = ['.td', '.mlir', '.ll', '.blif', '.fir', '.sv'] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__)