Skip to content

Commit

Permalink
Add Yosys integration
Browse files Browse the repository at this point in the history
This adds Yosys integration
  • Loading branch information
uenoku committed Oct 4, 2024
1 parent bb94693 commit 356d3e5
Show file tree
Hide file tree
Showing 23 changed files with 2,684 additions and 0 deletions.
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 <memory>
#include <string>
#include "llvm/ADT/ArrayRef.h"

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 @@ -25,6 +25,7 @@
#include "circt/Conversion/DCToHW.h"
#include "circt/Conversion/ExportChiselInterface.h"
#include "circt/Conversion/ExportVerilog.h"
#include "circt/Conversion/ExportRTLIL.h"
#include "circt/Conversion/FIRRTLToHW.h"
#include "circt/Conversion/FSMToSV.h"
#include "circt/Conversion/HWArithToHW.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

0 comments on commit 356d3e5

Please sign in to comment.