Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Not-for-commit] Demonstrate Yosys integration #7663

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,39 @@ else()
set(ARCILATOR_JIT_ENABLED 0)
endif()

option(CIRCT_YOSYS_INTEGRATION_ENABLED "Enables the Yosys integration." OFF)
llvm_canonicalize_cmake_booleans(CIRCT_YOSYS_INTEGRATION_ENABLED)

if(CIRCT_YOSYS_INTEGRATION_ENABLED)
# TODO: Switch to use the installed yosys
# FIXME: Yosys doesn't use cmake so need to manually configure
message(STATUS "Yosys integration is enabled")
include(ExternalProject)
ExternalProject_Add(yosys
URL https://github.com/YosysHQ/yosys/archive/refs/tags/yosys-0.40.zip
URL_HASH SHA256=3f20dcaeee9be882a818885d857ff476e8cc491657447f2c3629da82723ccd1f
SOURCE_DIR "${CMAKE_BINARY_DIR}/third-party/yosys"
BINARY_DIR "${CMAKE_BINARY_DIR}/third-party/yosys"
INSTALL_DIR "${CMAKE_BINARY_DIR}/third-party/yosys"
# CONFIGURE_COMMAND sed -i "s/ENABLE_LIBYOSYS := 0/ENABLE_LIBYOSYS := 1/" Makefile && make config-gcc # TODO: Switch `make config-clang` or `make config-gcc` based on CXX_COMPILER
CONFIGURE_COMMAND make config-gcc && echo "ENABLE_LIBYOSYS := 1" >> Makefile.conf && echo "PREFIX := ${CMAKE_BINARY_DIR}" >> Makefile.conf
BUILD_COMMAND make # TODO: Make the number of workers configurable.
INSTALL_COMMAND make install
TEST_COMMAND ""
BUILD_BYPRODUCTS "${CMAKE_BINARY_DIR}/third-party/yosys/libyosys.so"
)
ExternalProject_Get_Property(yosys INSTALL_DIR)
include_directories(${INSTALL_DIR}/share/include)
add_library (libyosys SHARED IMPORTED)
add_dependencies(libyosys yosys)
set_target_properties(libyosys PROPERTIES
IMPORTED_LOCATION "${INSTALL_DIR}/libyosys.so"
INSTALL_RPATH "${INSTALL_RPATH}"
INTERFACE_INCLUDE_DIRECTORIES "${INSTALL_DIR}/share/include/"
)
set(CMAKE_BUILD_RPATH "${INSTALL_DIR}")
endif()

#-------------------------------------------------------------------------------
# Directory setup
#-------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions include/circt/Conversion/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
set(LLVM_TARGET_DEFINITIONS Passes.td)
if(CIRCT_YOSYS_INTEGRATION_ENABLED)
mlir_tablegen(Passes.h.inc -gen-pass-decls -name Conversion -DCIRCT_YOSYS_INTEGRATION_ENABLED)
else()
mlir_tablegen(Passes.h.inc -gen-pass-decls -name Conversion)
endif()

mlir_tablegen(Conversion.capi.h.inc -gen-pass-capi-header --prefix Conversion)
mlir_tablegen(Conversion.capi.cpp.inc -gen-pass-capi-impl --prefix Conversion)
add_public_tablegen_target(CIRCTConversionPassIncGen)
Expand Down
41 changes: 41 additions & 0 deletions include/circt/Conversion/ExportRTLIL.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===- ExportRTLIL.h - Export core dialects to Yosys RTLIL --===-*- 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 passes which lower core dialects to RTLIL in-memory IR.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_CONVERSION_EXPORTRTLIL_H
#define CIRCT_CONVERSION_EXPORTRTLIL_H

#include "circt/Support/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include <memory>
#include <string>

namespace mlir {
class Pass;
} // namespace mlir

namespace circt {

#define GEN_PASS_DECL_EXPORTYOSYS
#define GEN_PASS_DECL_EXPORTYOSYSPARALLEL
#include "circt/Conversion/Passes.h.inc"

std::unique_ptr<mlir::Pass> createYosysOptimizer();
std::unique_ptr<mlir::Pass> createYosysOptimizerParallel();

/// Register the `(import|export)-rtlil` MLIR translation.
void registerRTLILImport();
void registerRTLILExport();
void registerRTLILTranslation();

} // namespace circt

#endif // CIRCT_CONVERSION_EXPORTRTLIL_H
1 change: 1 addition & 0 deletions include/circt/Conversion/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "circt/Conversion/ConvertToArcs.h"
#include "circt/Conversion/DCToHW.h"
#include "circt/Conversion/ExportChiselInterface.h"
#include "circt/Conversion/ExportRTLIL.h"
#include "circt/Conversion/ExportVerilog.h"
#include "circt/Conversion/FIRRTLToHW.h"
#include "circt/Conversion/FSMToSV.h"
Expand Down
44 changes: 44 additions & 0 deletions include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -790,4 +790,48 @@ def LowerSimToSV: Pass<"lower-sim-to-sv", "mlir::ModuleOp"> {
];
}

//===----------------------------------------------------------------------===//
// Yosys Integration
//===----------------------------------------------------------------------===//

#ifdef CIRCT_YOSYS_INTEGRATION_ENABLED
def YosysOptimizer: Pass<"yosys-optimizer", "mlir::ModuleOp"> {
let summary = "Translate core dialects into yosys's RTL IR";
let constructor = "createYosysOptimizer()";
let options = [
ListOption<"passes", "passes", "std::string",
"Specify Yosys passes to run">,
Option<"topModule", "top-module", "std::string",
"", "top module name">,
Option<"redirectLog", "redirect-log", "bool",
"false", "">
];
let dependentDialects = [
"circt::comb::CombDialect",
"circt::seq::SeqDialect",
"circt::sv::SVDialect",
"circt::hw::HWDialect"
];
}

def YosysOptimizerParallel: Pass<"yosys-optimizer-parallel", "mlir::ModuleOp"> {
let summary = "Translate core dialects into yosys's RTL IR";
let constructor = "createYosysOptimizerParallel()";
let options = [
ListOption<"passes", "passes", "std::string",
"Specify Yosys passes to run">,
Option<"topModule", "top-module", "std::string",
"", "top module name">,
Option<"workspace", "workspace", "std::string",
"", "workspace to run yosys">
];
let dependentDialects = [
"circt::comb::CombDialect",
"circt::seq::SeqDialect",
"circt::sv::SVDialect",
"circt::hw::HWDialect"
];
}
#endif

#endif // CIRCT_CONVERSION_PASSES_TD
102 changes: 102 additions & 0 deletions include/circt/Dialect/Seq/SeqVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//===- SeqVisitors.h - Seq Dialect Visitors ---------------------*- 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 visitors for Seq dialect operations.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_DIALECT_SEQ_SEQVISITORS_H
#define CIRCT_DIALECT_SEQ_SEQVISITORS_H

#include "circt/Dialect/Seq/SeqOps.h"
#include "llvm/ADT/TypeSwitch.h"

namespace circt {
namespace seq {

/// This helps visit TypeOp nodes.
template <typename ConcreteType, typename ResultType = void,
typename... ExtraArgs>
class SeqOpVisitor {
public:
ResultType dispatchSeqOpVisitor(Operation *op, ExtraArgs... args) {
auto *thisCast = static_cast<ConcreteType *>(this);
return TypeSwitch<Operation *, ResultType>(op)
.template Case<
// Registers.
CompRegOp, CompRegClockEnabledOp, ShiftRegOp, FirRegOp, FIFOOp,
// Memories.
HLMemOp, ReadPortOp, WritePortOp, FirMemOp, FirMemReadOp,
FirMemWriteOp, FirMemReadWriteOp,
// Clock.
ClockGateOp, ClockMuxOp, ClockDividerOp, ClockInverterOp,
ConstClockOp, ToClockOp, FromClockOp>([&](auto expr) -> ResultType {
return thisCast->visitSeq(expr, args...);
})
.Default([&](auto expr) -> ResultType {
return thisCast->visitInvalidSeqOp(op, args...);
});
}

/// This callback is invoked on any non-expression operations.
ResultType visitInvalidSeqOp(Operation *op, ExtraArgs... args) {
op->emitOpError("unknown seq op");
abort();
}

/// This callback is invoked on any combinational operations that are not
/// handled by the concrete visitor.
ResultType visitUnhandledSeqOp(Operation *op, ExtraArgs... args) {
return ResultType();
}

#define HANDLE(OPTYPE, OPKIND) \
ResultType visitSeq(OPTYPE op, ExtraArgs... args) { \
return static_cast<ConcreteType *>(this)->visit##OPKIND##SeqOp(op, \
args...); \
}

// Registers.
HANDLE(CompRegOp, Unhandled);
HANDLE(CompRegClockEnabledOp, Unhandled);
HANDLE(ShiftRegOp, Unhandled);

HANDLE(FirRegOp, Unhandled);
HANDLE(FIFOOp, Unhandled);

// Memories.
HANDLE(HLMemOp, Unhandled);
HANDLE(ReadPortOp, Unhandled);
HANDLE(WritePortOp, Unhandled);

// FIRRTL memory ops.
HANDLE(FirMemOp, Unhandled);
HANDLE(FirMemReadOp, Unhandled);
HANDLE(FirMemWriteOp, Unhandled);
HANDLE(FirMemReadWriteOp, Unhandled);

// Clock gate.
HANDLE(ClockGateOp, Unhandled);
HANDLE(ClockMuxOp, Unhandled);
HANDLE(ClockDividerOp, Unhandled);
HANDLE(ClockInverterOp, Unhandled);

// Tied-off clock
HANDLE(ConstClockOp, Unhandled);

// Clock casts.
HANDLE(ToClockOp, Unhandled);
HANDLE(FromClockOp, Unhandled);

#undef HANDLE
};

} // namespace seq
} // namespace circt

#endif // CIRCT_DIALECT_SEQ_SEQVISITORS_H
84 changes: 84 additions & 0 deletions integration_test/YosysIntegration/round-trip.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// REQUIRES: yosys-integration

// RUN: circt-translate --export-rtlil %s | circt-translate --import-rtlil | circt-opt -canonicalize | FileCheck %s

// CHECK-LABEL: hw.module @Arith(in %in1 : i2, in %in2 : i2, in %in3 : i2, out add : i2, out sub : i2, out mul : i2, out and : i2, out or : i2, out xor : i2)
hw.module @Arith(in %in1 : i2, in %in2 : i2, in %in3: i2, out add : i2, out sub: i2, out mul: i2, out and: i2, out or: i2, out xor: i2 ) {
%0 = comb.add %in1, %in2, %in3: i2
%1 = comb.sub %in1, %in2: i2
%2 = comb.mul %in1, %in2, %in3: i2
%3 = comb.and %in1, %in2, %in3: i2
%4 = comb.or %in1, %in2, %in3: i2
%5 = comb.xor %in1, %in2, %in3: i2
// CHECK-NEXT: %0 = comb.add %in1, %in2, %in3 : i2
// CHECK-NEXT: %1 = comb.sub %in1, %in2 : i2
// CHECK-NEXT: %2 = comb.mul %in1, %in2, %in3 : i2
// CHECK-NEXT: %3 = comb.and %in1, %in2, %in3 : i2
// CHECK-NEXT: %4 = comb.or %in1, %in2, %in3 : i2
// CHECK-NEXT: %5 = comb.xor %in1, %in2, %in3 : i2
// CHECK-NEXT: hw.output %0, %1, %2, %3, %4, %5 : i2, i2, i2, i2, i2, i2
hw.output %0, %1, %2, %3, %4, %5 : i2, i2, i2, i2, i2, i2
}

// CHECK-LABEL: hw.module @ICmp(in %a : i2, in %b : i2, out eq : i1, out ne : i1, out slt : i1, out sle : i1, out sgt : i1, out sge : i1, out ult : i1, out ule : i1, out ugt : i1, out uge : i1)
hw.module @ICmp(in %a : i2, in %b : i2, out eq : i1,
out ne: i1, out slt: i1, out sle: i1, out sgt: i1, out sge: i1,
out ult: i1, out ule: i1, out ugt: i1, out uge: i1
) {
%eq = comb.icmp eq %a, %b : i2
%ne = comb.icmp ne %a, %b : i2
%slt = comb.icmp slt %a, %b : i2
%sle = comb.icmp sle %a, %b : i2
%sgt = comb.icmp sgt %a, %b : i2
%sge = comb.icmp sge %a, %b : i2
%ult = comb.icmp ult %a, %b : i2
%ule = comb.icmp ule %a, %b : i2
%ugt = comb.icmp ugt %a, %b : i2
%uge = comb.icmp uge %a, %b : i2
// CHECK-NEXT: %0 = comb.icmp eq %a, %b : i2
// CHECK-NEXT: %1 = comb.icmp ne %a, %b : i2
// CHECK-NEXT: %2 = comb.icmp slt %a, %b : i2
// CHECK-NEXT: %3 = comb.icmp sle %a, %b : i2
// CHECK-NEXT: %4 = comb.icmp sgt %a, %b : i2
// CHECK-NEXT: %5 = comb.icmp sge %a, %b : i2
// CHECK-NEXT: %6 = comb.icmp ult %a, %b : i2
// CHECK-NEXT: %7 = comb.icmp ule %a, %b : i2
// CHECK-NEXT: %8 = comb.icmp ugt %a, %b : i2
// CHECK-NEXT: %9 = comb.icmp uge %a, %b : i2
// CHECK-NEXT: hw.output %0, %1, %2, %3, %4, %5, %6, %7, %8, %9 : i1, i1, i1, i1, i1, i1, i1, i1, i1, i1
hw.output %eq, %ne, %slt, %sle, %sgt, %sge, %ult, %ule, %ugt, %uge : i1, i1, i1, i1, i1, i1, i1, i1, i1, i1
}


// CHECK-LABEL: hw.module @counter(in %clk : i1, out o : i8)
hw.module @counter(in %clk: i1, out o: i8) {
// CHECK-NEXT: %c1_i8 = hw.constant 1 : i8
// CHECK-NEXT: %0 = seq.to_clock %clk
// CHECK-NEXT: %reg = seq.compreg %1, %0 : i8
// CHECK-NEXT: %1 = comb.add %reg, %c1_i8 : i8
// CHECK-NEXT: hw.output %reg : i8
%seq_clk = seq.to_clock %clk
%reg = seq.compreg %added, %seq_clk : i8
%one = hw.constant 1 : i8
%added = comb.add %reg, %one : i8
hw.output %reg : i8
}

// CHECK-LABEL: hw.module @misc(in %cond : i1, in %in1 : i2, in %in2 : i2, in %in3 : i5, out mux : i2, out extract : i2, out concat : i9, out replicate : i6, out shl : i5, out parity : i1)
hw.module @misc(in %cond:i1, in %in1 : i2, in %in2 : i2, in %in3: i5,
out mux : i2, out extract: i2, out concat: i9, out replicate: i6, out shl: i5, out parity: i1 ) {
// CHECK-NEXT: %0 = comb.extract %in3 from 3 : (i5) -> i2
// CHECK-NEXT: %1 = comb.concat %in1, %in2, %in3 : i2, i2, i5
// CHECK-NEXT: %2 = comb.replicate %in1 : (i2) -> i6
// CHECK-NEXT: %3 = comb.mux %cond, %in1, %in2 : i2
// CHECK-NEXT: %4 = comb.shl %in3, %in3 : i5
// CHECK-NEXT: %5 = comb.parity %in3 : i5
// CHECK-NEXT: hw.output %3, %0, %1, %2, %4, %5 : i2, i2, i9, i6, i5, i1
%mux = comb.mux %cond, %in1, %in2 : i2
%extract = comb.extract %in3 from 3 : (i5) -> i2
%concat = comb.concat %in1, %in2, %in3 : i2, i2, i5
%replicate = comb.replicate %in1 : (i2) -> i6
%shl = comb.shl %in3, %in3 : i5
%partiy = comb.parity %in3 : i5
hw.output %mux, %extract, %concat, %replicate, %shl, %partiy: i2, i2, i9, i6, i5, i1
}
60 changes: 60 additions & 0 deletions integration_test/YosysIntegration/synth.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// REQUIRES: libz3
// REQUIRES: circt-lec-jit
// REQUIRES: yosys-integration

// Run synthesis and check the LEC.
// RUN: circt-opt --pass-pipeline='builtin.module(yosys-optimizer{passes=synth},canonicalize)' -o %t.mlir %s

// RUN: circt-lec %s %t.mlir -c1=Arith -c2=Arith --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB
// COMB: c1 == c2

hw.module @Arith(in %in1 : i2, in %in2 : i2, out add : i2, out sub: i2, out mul: i2, out and: i2, out or: i2, out xor: i2 ) {
%0 = comb.add %in1, %in2: i2
%1 = comb.sub %in1, %in2: i2
%2 = comb.mul %in1, %in2: i2
%3 = comb.and %in1, %in2: i2
%4 = comb.or %in1, %in2: i2
%5 = comb.xor %in1, %in2: i2
hw.output %0, %1, %2, %3, %4, %5 : i2, i2, i2, i2, i2, i2
}

// RUN: circt-lec %s %t.mlir -c1=ICmp -c2=ICmp --shared-libs=%libz3 | FileCheck %s --check-prefix=ICMP
// ICMP: c1 == c2
hw.module @ICmp(in %a : i2, in %b : i2, out eq : i1,
out ne: i1, out slt: i1, out sle: i1, out sgt: i1, out sge: i1,
out ult: i1, out ule: i1, out ugt: i1, out uge: i1
) {
%eq = comb.icmp eq %a, %b : i2
%ne = comb.icmp ne %a, %b : i2
%slt = comb.icmp slt %a, %b : i2
%sle = comb.icmp sle %a, %b : i2
%sgt = comb.icmp sgt %a, %b : i2
%sge = comb.icmp sge %a, %b : i2
%ult = comb.icmp ult %a, %b : i2
%ule = comb.icmp ule %a, %b : i2
%ugt = comb.icmp ugt %a, %b : i2
%uge = comb.icmp uge %a, %b : i2
hw.output %eq, %ne, %slt, %sle, %sgt, %sge, %ult, %ule, %ugt, %uge : i1, i1, i1, i1, i1, i1, i1, i1, i1, i1
}

// RUN: circt-lec %s %t.mlir -c1=misc -c2=misc --shared-libs=%libz3 | FileCheck %s --check-prefix=MISC
// MISC: c1 == c2
hw.module @misc(in %cond:i1, in %in1 : i2, in %in2 : i2, in %in3: i5,
out mux : i2, out extract: i2, out concat: i9, out replicate: i6, out shl: i5, out parity: i1 ) {
%mux = comb.mux %cond, %in1, %in2 : i2
%extract = comb.extract %in3 from 3 : (i5) -> i2
%concat = comb.concat %in1, %in2, %in3 : i2, i2, i5
%replicate = comb.replicate %in1 : (i2) -> i6
%shl = comb.shl %in3, %in3 : i5
%partiy = comb.parity %in3 : i5
hw.output %mux, %extract, %concat, %replicate, %shl, %partiy: i2, i2, i9, i6, i5, i1
}

// These are incorrectly lowered now(LEC failure).
// * comb.shrs
// * hw.array_create + hw.array_get
// hw.module @MultibitMux(in %a_0 : i1, in %a_1 : i1, in %a_2 : i1, in %sel : i2, out b : i1) {
// %0 = hw.array_create %a_0, %a_2, %a_1, %a_0 : i1
// %1 = hw.array_get %0[%sel] : !hw.array<4xi1>, i2
// hw.output %1 : i1
// }
Loading
Loading