mirror of https://github.com/llvm/circt.git
[LLHD] Remove llhd-sim (#7351)
This commit is contained in:
parent
dde9f4bc96
commit
2345382e67
|
@ -448,24 +448,6 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# llhd-sim Configuration
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if(NOT WIN32)
|
|
||||||
option(CIRCT_LLHD_SIM_ENABLED "Enables LLHD sim." ON)
|
|
||||||
else()
|
|
||||||
option(CIRCT_LLHD_SIM_ENABLED "Enables LLHD sim." OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CIRCT_LLHD_SIM_ENABLED)
|
|
||||||
message(STATUS "llhd-sim is enabled.")
|
|
||||||
else()
|
|
||||||
message(STATUS "llhd-sim is disabled.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
llvm_canonicalize_cmake_booleans(CIRCT_LLHD_SIM_ENABLED)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Z3Lib Configuration (for circt-lec and SMT dialect integration tests)
|
# Z3Lib Configuration (for circt-lec and SMT dialect integration tests)
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|
|
@ -32,7 +32,6 @@ tools/arcilator @fabianschuiki @maerhart
|
||||||
|
|
||||||
# LLHD
|
# LLHD
|
||||||
**/LLHD* @fabianschuiki @maerhart
|
**/LLHD* @fabianschuiki @maerhart
|
||||||
tools/llhd-sim @fabianschuiki @maerhart
|
|
||||||
|
|
||||||
# Pipeline
|
# Pipeline
|
||||||
**/Dialect/Pipeline @mortbopet
|
**/Dialect/Pipeline @mortbopet
|
||||||
|
|
|
@ -50,7 +50,7 @@ digraph G {
|
||||||
ESI [URL="https://circt.llvm.org/docs/Dialects/ESI/"]
|
ESI [URL="https://circt.llvm.org/docs/Dialects/ESI/"]
|
||||||
FSM [URL="https://circt.llvm.org/docs/Dialects/FSM/"]
|
FSM [URL="https://circt.llvm.org/docs/Dialects/FSM/"]
|
||||||
HWArith [URL="https://circt.llvm.org/docs/Dialects/HWArith/"]
|
HWArith [URL="https://circt.llvm.org/docs/Dialects/HWArith/"]
|
||||||
MooreMIR [URL="https://circt.llvm.org/docs/Dialects/Moore/", label="Moore MIR"]
|
Moore [URL="https://circt.llvm.org/docs/Dialects/Moore/"]
|
||||||
|
|
||||||
// Intermediate node to target when lowering to both SV and Core dialects
|
// Intermediate node to target when lowering to both SV and Core dialects
|
||||||
lower_to_sv_and_core [shape=point label="" fillcolor=black]
|
lower_to_sv_and_core [shape=point label="" fillcolor=black]
|
||||||
|
@ -79,7 +79,6 @@ digraph G {
|
||||||
subgraph backend_internal_tools{
|
subgraph backend_internal_tools{
|
||||||
node [fillcolor="#fdc086"]
|
node [fillcolor="#fdc086"]
|
||||||
Arcilator
|
Arcilator
|
||||||
llhd_sim [label="llhd-sim"]
|
|
||||||
ExportSystemC
|
ExportSystemC
|
||||||
ExportVerilog [URL="https://circt.llvm.org/docs/VerilogGeneration/"]
|
ExportVerilog [URL="https://circt.llvm.org/docs/VerilogGeneration/"]
|
||||||
}
|
}
|
||||||
|
@ -88,7 +87,7 @@ digraph G {
|
||||||
// External tools
|
// External tools
|
||||||
subgraph external_tools {
|
subgraph external_tools {
|
||||||
node [shape=octagon fillcolor="#ffff99"]
|
node [shape=octagon fillcolor="#ffff99"]
|
||||||
Moore
|
Slang
|
||||||
Calyx_native [label="Calyx native"]
|
Calyx_native [label="Calyx native"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,13 +125,14 @@ digraph G {
|
||||||
|
|
||||||
// Things that lower into a subset of the RTL-like dialects. Cluster these
|
// Things that lower into a subset of the RTL-like dialects. Cluster these
|
||||||
// together to avoid a massive clutter.
|
// together to avoid a massive clutter.
|
||||||
{Pipeline MSFT HWArith MooreMIR} -> lower_to_core [arrowhead=none]
|
{Pipeline MSFT HWArith Moore} -> lower_to_core [arrowhead=none]
|
||||||
|
Moore -> LLHD
|
||||||
{ESI FIRRTL FSM} -> lower_to_sv_and_core [arrowhead=none]
|
{ESI FIRRTL FSM} -> lower_to_sv_and_core [arrowhead=none]
|
||||||
lower_to_sv_and_core -> SV
|
lower_to_sv_and_core -> SV
|
||||||
lower_to_sv_and_core -> Comb [lhead=cluster_RTL]
|
lower_to_sv_and_core -> Comb [lhead=cluster_RTL]
|
||||||
lower_to_core -> Comb [lhead=cluster_RTL]
|
lower_to_core -> Comb [lhead=cluster_RTL]
|
||||||
Seq -> SV
|
Seq -> SV
|
||||||
{HW MooreMIR} -> LLHD
|
LLHD -> Arcilator
|
||||||
Interop -> Arc [ltail=cluster_RTL]
|
Interop -> Arc [ltail=cluster_RTL]
|
||||||
Comb -> SystemC [ltail=cluster_RTL]
|
Comb -> SystemC [ltail=cluster_RTL]
|
||||||
ExportVerilog -> SVFile
|
ExportVerilog -> SVFile
|
||||||
|
@ -140,6 +140,7 @@ digraph G {
|
||||||
// Tool flows
|
// Tool flows
|
||||||
Arc -> Arcilator
|
Arc -> Arcilator
|
||||||
Arcilator -> SimBinary
|
Arcilator -> SimBinary
|
||||||
|
Arcilator -> VCDTrace
|
||||||
Scheduling -> LoopSchedule [dir=both]
|
Scheduling -> LoopSchedule [dir=both]
|
||||||
Scheduling -> Pipeline [dir=both]
|
Scheduling -> Pipeline [dir=both]
|
||||||
Chisel -> FIRFile
|
Chisel -> FIRFile
|
||||||
|
@ -149,13 +150,10 @@ digraph G {
|
||||||
ExportSystemC -> SystemCFile
|
ExportSystemC -> SystemCFile
|
||||||
SV -> ExportVerilog
|
SV -> ExportVerilog
|
||||||
Seq -> ExportVerilog [ltail=cluster_RTL]
|
Seq -> ExportVerilog [ltail=cluster_RTL]
|
||||||
HW -> llhd_sim [ltail=cluster_RTL]
|
SVVHDL -> Slang [weight=999]
|
||||||
SVVHDL -> Moore [weight=999]
|
Slang -> Moore
|
||||||
Moore -> MooreMIR
|
|
||||||
Calyx -> Calyx_native
|
Calyx -> Calyx_native
|
||||||
Calyx_native -> lower_to_sv_and_core [arrowhead=none]
|
Calyx_native -> lower_to_sv_and_core [arrowhead=none]
|
||||||
LLHD -> llhd_sim
|
|
||||||
llhd_sim -> VCDTrace
|
|
||||||
ESI -> ServiceDesc
|
ESI -> ServiceDesc
|
||||||
MSFT -> TCL
|
MSFT -> TCL
|
||||||
PyFile -> PyCDE
|
PyFile -> PyCDE
|
||||||
|
@ -170,7 +168,7 @@ digraph G {
|
||||||
|
|
||||||
// Leave one rank free above the RTL cluster to improve routing of incoming
|
// Leave one rank free above the RTL cluster to improve routing of incoming
|
||||||
// edges.
|
// edges.
|
||||||
{FIRRTL FSM ESI MSFT HWArith MooreMIR} -> space_above_RTL [weight=999, style=invis]
|
{FIRRTL FSM ESI MSFT HWArith Moore} -> space_above_RTL [weight=999, style=invis]
|
||||||
{space_above_RTL} -> {Seq HW Comb} [lhead=cluster_RTL, weight=999, style=invis]
|
{space_above_RTL} -> {Seq HW Comb} [lhead=cluster_RTL, weight=999, style=invis]
|
||||||
|
|
||||||
// Fix the following sink nodes below the CIRCT cluster
|
// Fix the following sink nodes below the CIRCT cluster
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 180 KiB |
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 36 KiB |
|
@ -1,43 +0,0 @@
|
||||||
//===- LLHDToLLVM.h - LLHD to LLVM pass entry point -------------*- 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 header file defines prototypes that expose the LLHDToLLVM pass
|
|
||||||
// constructors.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H
|
|
||||||
#define CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H
|
|
||||||
|
|
||||||
#include "circt/Support/LLVM.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace mlir {
|
|
||||||
class LLVMTypeConverter;
|
|
||||||
} // namespace mlir
|
|
||||||
|
|
||||||
namespace circt {
|
|
||||||
|
|
||||||
#define GEN_PASS_DECL_CONVERTLLHDTOLLVM
|
|
||||||
#include "circt/Conversion/Passes.h.inc"
|
|
||||||
|
|
||||||
/// Get the LLHD to LLVM type conversions
|
|
||||||
void populateLLHDToLLVMTypeConversions(mlir::LLVMTypeConverter &converter);
|
|
||||||
|
|
||||||
/// Get the LLHD to LLVM conversion patterns.
|
|
||||||
void populateLLHDToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter,
|
|
||||||
RewritePatternSet &patterns,
|
|
||||||
size_t &sigCounter,
|
|
||||||
size_t ®Counter);
|
|
||||||
|
|
||||||
/// Create an LLHD to LLVM conversion pass.
|
|
||||||
std::unique_ptr<OperationPass<ModuleOp>> createConvertLLHDToLLVMPass();
|
|
||||||
|
|
||||||
} // namespace circt
|
|
||||||
|
|
||||||
#endif // CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H
|
|
|
@ -36,7 +36,6 @@
|
||||||
#include "circt/Conversion/HWToSystemC.h"
|
#include "circt/Conversion/HWToSystemC.h"
|
||||||
#include "circt/Conversion/HandshakeToDC.h"
|
#include "circt/Conversion/HandshakeToDC.h"
|
||||||
#include "circt/Conversion/HandshakeToHW.h"
|
#include "circt/Conversion/HandshakeToHW.h"
|
||||||
#include "circt/Conversion/LLHDToLLVM.h"
|
|
||||||
#include "circt/Conversion/LTLToCore.h"
|
#include "circt/Conversion/LTLToCore.h"
|
||||||
#include "circt/Conversion/LoopScheduleToCalyx.h"
|
#include "circt/Conversion/LoopScheduleToCalyx.h"
|
||||||
#include "circt/Conversion/MooreToCore.h"
|
#include "circt/Conversion/MooreToCore.h"
|
||||||
|
|
|
@ -519,22 +519,6 @@ def ConvertMooreToCore : Pass<"convert-moore-to-core", "mlir::ModuleOp"> {
|
||||||
"llhd::LLHDDialect"];
|
"llhd::LLHDDialect"];
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// LLHDToLLVM
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
def ConvertLLHDToLLVM : Pass<"convert-llhd-to-llvm", "mlir::ModuleOp"> {
|
|
||||||
let summary = "Convert LLHD to LLVM";
|
|
||||||
let description = [{
|
|
||||||
This pass translates LLHD to LLVM.
|
|
||||||
}];
|
|
||||||
let constructor = "circt::createConvertLLHDToLLVMPass()";
|
|
||||||
let dependentDialects = [
|
|
||||||
"mlir::arith::ArithDialect",
|
|
||||||
"mlir::LLVM::LLVMDialect"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// HWToLLVM
|
// HWToLLVM
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
//===- Engine.h - LLHD simulaton engine -------------------------*- 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 the main Engine class of the LLHD simulator.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H
|
|
||||||
#define CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H
|
|
||||||
|
|
||||||
#include "State.h"
|
|
||||||
#include "Trace.h"
|
|
||||||
|
|
||||||
#include "circt/Dialect/LLHD/IR/LLHDOps.h"
|
|
||||||
#include "mlir/IR/BuiltinOps.h"
|
|
||||||
|
|
||||||
namespace mlir {
|
|
||||||
class ExecutionEngine;
|
|
||||||
} // namespace mlir
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class Error;
|
|
||||||
class Module;
|
|
||||||
} // namespace llvm
|
|
||||||
|
|
||||||
namespace circt {
|
|
||||||
namespace llhd {
|
|
||||||
namespace sim {
|
|
||||||
|
|
||||||
class Engine {
|
|
||||||
public:
|
|
||||||
/// Initialize an LLHD simulation engine. This initializes the state, as well
|
|
||||||
/// as the mlir::ExecutionEngine with the given module.
|
|
||||||
Engine(
|
|
||||||
llvm::raw_ostream &out, ModuleOp module,
|
|
||||||
llvm::function_ref<mlir::LogicalResult(mlir::ModuleOp)> mlirTransformer,
|
|
||||||
llvm::function_ref<llvm::Error(llvm::Module *)> llvmTransformer,
|
|
||||||
std::string root, TraceMode tm, ArrayRef<StringRef> sharedLibPaths);
|
|
||||||
|
|
||||||
/// Default destructor
|
|
||||||
~Engine();
|
|
||||||
|
|
||||||
/// Run simulation up to n steps or maxTime picoseconds of simulation time.
|
|
||||||
/// n=0 and T=0 make the simulation run indefinitely.
|
|
||||||
int simulate(int n, uint64_t maxTime);
|
|
||||||
|
|
||||||
/// Build the instance layout of the design.
|
|
||||||
void buildLayout(ModuleOp module);
|
|
||||||
|
|
||||||
/// Get the MLIR module.
|
|
||||||
const ModuleOp getModule() const { return module; }
|
|
||||||
|
|
||||||
/// Get the simulation state.
|
|
||||||
const State *getState() const { return state.get(); }
|
|
||||||
|
|
||||||
/// Dump the instance layout stored in the State.
|
|
||||||
void dumpStateLayout();
|
|
||||||
|
|
||||||
/// Dump the instances each signal triggers.
|
|
||||||
void dumpStateSignalTriggers();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void walkEntity(EntityOp entity, Instance &child);
|
|
||||||
|
|
||||||
llvm::raw_ostream &out;
|
|
||||||
std::string root;
|
|
||||||
std::unique_ptr<State> state;
|
|
||||||
std::unique_ptr<mlir::ExecutionEngine> engine;
|
|
||||||
ModuleOp module;
|
|
||||||
TraceMode traceMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sim
|
|
||||||
} // namespace llhd
|
|
||||||
} // namespace circt
|
|
||||||
|
|
||||||
#endif // CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H
|
|
|
@ -1,357 +0,0 @@
|
||||||
//===- State.h - Simulation state definition --------------------*- 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 structures used to keep track of the simulation state in the LLHD
|
|
||||||
// simulator.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H
|
|
||||||
#define CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H
|
|
||||||
|
|
||||||
#include "llvm/ADT/APInt.h"
|
|
||||||
#include "llvm/ADT/SmallVector.h"
|
|
||||||
#include "llvm/ADT/StringMap.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <queue>
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace circt {
|
|
||||||
namespace llhd {
|
|
||||||
namespace sim {
|
|
||||||
|
|
||||||
/// The simulator's internal representation of time.
|
|
||||||
class Time {
|
|
||||||
public:
|
|
||||||
/// Empty (zero) time constructor. All the time values are defaulted to 0.
|
|
||||||
Time() = default;
|
|
||||||
|
|
||||||
/// Construct with given time values.
|
|
||||||
Time(uint64_t time, uint64_t delta, uint64_t eps)
|
|
||||||
: time(time), delta(delta), eps(eps) {}
|
|
||||||
|
|
||||||
/// Compare the time values in order of time, delta, eps.
|
|
||||||
bool operator<(const Time &rhs) const {
|
|
||||||
return time < rhs.time || (time == rhs.time && delta < rhs.delta) ||
|
|
||||||
(time == rhs.time && delta == rhs.delta && eps < rhs.eps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true if all the time values are equal.
|
|
||||||
bool operator==(const Time &rhs) const {
|
|
||||||
return time == rhs.time && delta == rhs.delta && eps == rhs.eps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add two time values.
|
|
||||||
Time operator+(const Time &rhs) const {
|
|
||||||
return Time(time + rhs.time, delta + rhs.delta, eps + rhs.eps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the stored time in a printable format.
|
|
||||||
std::string toString() const;
|
|
||||||
|
|
||||||
uint64_t getTime() const { return time; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Simulation real time.
|
|
||||||
uint64_t time;
|
|
||||||
uint64_t delta;
|
|
||||||
uint64_t eps;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Detail structure that can be easily accessed by the lowered code.
|
|
||||||
struct SignalDetail {
|
|
||||||
uint8_t *value;
|
|
||||||
uint64_t offset;
|
|
||||||
uint64_t instIndex;
|
|
||||||
uint64_t globalIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The simulator's internal representation of a signal.
|
|
||||||
class Signal {
|
|
||||||
public:
|
|
||||||
/// Construct an "empty" signal.
|
|
||||||
Signal(std::string name, std::string owner)
|
|
||||||
: name(name), owner(owner), size(0), value(nullptr) {}
|
|
||||||
|
|
||||||
/// Construct a signal with the given name, owner and initial value.
|
|
||||||
Signal(std::string name, std::string owner, uint8_t *value, uint64_t size)
|
|
||||||
: name(name), owner(owner), size(size), value(value) {}
|
|
||||||
|
|
||||||
/// Default move constructor.
|
|
||||||
Signal(Signal &&) = default;
|
|
||||||
|
|
||||||
/// Free 'value' since it is allocated using 'malloc' in the LLVM code
|
|
||||||
/// generated by LLHDToLLVM.
|
|
||||||
~Signal();
|
|
||||||
|
|
||||||
/// Returns true if the signals match in name, owner, size and value.
|
|
||||||
bool operator==(const Signal &rhs) const {
|
|
||||||
if (owner != rhs.owner || name != rhs.name || size != rhs.size)
|
|
||||||
return false;
|
|
||||||
return std::memcmp(value, rhs.value, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the owner name is lexically smaller than rhs's owner, or
|
|
||||||
/// the name is lexically smaller than rhs's name, in case they share the same
|
|
||||||
/// owner.
|
|
||||||
bool operator<(const Signal &rhs) const {
|
|
||||||
return (owner < rhs.owner || (owner == rhs.owner && name < rhs.name));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOwner(const std::string &rhs) const { return owner == rhs; };
|
|
||||||
|
|
||||||
std::string getOwner() const { return owner; }
|
|
||||||
|
|
||||||
bool isValidSigName() const {
|
|
||||||
return std::regex_match(name, std::regex("(sig)?[0-9]*"));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getName() const { return name; }
|
|
||||||
|
|
||||||
uint64_t getSize() const { return size; }
|
|
||||||
|
|
||||||
uint8_t *getValue() const { return value; }
|
|
||||||
|
|
||||||
const std::vector<unsigned> &getTriggeredInstanceIndices() const {
|
|
||||||
return instanceIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushInstanceIndex(unsigned i) { instanceIndices.push_back(i); }
|
|
||||||
|
|
||||||
bool hasElement() const { return elements.size() > 0; }
|
|
||||||
|
|
||||||
size_t getElementSize() const { return elements.size(); }
|
|
||||||
|
|
||||||
void pushElement(std::pair<unsigned, unsigned> val) {
|
|
||||||
elements.push_back(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store JIT allocated signal pointer and size.
|
|
||||||
void store(uint8_t *v, uint64_t s) {
|
|
||||||
value = v;
|
|
||||||
size = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update signal value when it is changed, the width of incoming signal
|
|
||||||
/// value and the stored signal value are identical.
|
|
||||||
/// As majority signals are smaller than 64 bits, this implementation
|
|
||||||
/// is much faster as it avoided memcpy in most cases.
|
|
||||||
/// @param v Pointer to signal value
|
|
||||||
/// @return true when signal is updated, false when not
|
|
||||||
bool updateWhenChanged(const uint64_t *v) {
|
|
||||||
switch (size) {
|
|
||||||
case 1: {
|
|
||||||
const uint8_t *newVal = reinterpret_cast<const uint8_t *>(v);
|
|
||||||
if (*value == *newVal)
|
|
||||||
return false;
|
|
||||||
*value = *newVal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
const uint16_t *newVal = reinterpret_cast<const uint16_t *>(v);
|
|
||||||
if (*(uint16_t *)value == *newVal)
|
|
||||||
return false;
|
|
||||||
*(uint16_t *)value = *newVal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
const uint32_t *newVal = reinterpret_cast<const uint32_t *>(v);
|
|
||||||
if (*(uint32_t *)value == *newVal)
|
|
||||||
return false;
|
|
||||||
*(uint32_t *)value = *newVal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 8: {
|
|
||||||
if (*(uint64_t *)value == *v)
|
|
||||||
return false;
|
|
||||||
*(uint64_t *)value = *v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (std::memcmp(value, v, size) == 0)
|
|
||||||
return false;
|
|
||||||
std::memcpy(value, v, size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the value of the signal in hexadecimal string format.
|
|
||||||
std::string toHexString() const;
|
|
||||||
|
|
||||||
/// Return the value of the i-th element of the signal in hexadecimal string
|
|
||||||
/// format.
|
|
||||||
std::string toHexString(unsigned) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string name;
|
|
||||||
std::string owner;
|
|
||||||
// The list of instances this signal triggers.
|
|
||||||
std::vector<unsigned> instanceIndices;
|
|
||||||
uint64_t size;
|
|
||||||
uint8_t *value;
|
|
||||||
std::vector<std::pair<unsigned, unsigned>> elements;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The simulator's internal representation of one queue slot.
|
|
||||||
struct Slot {
|
|
||||||
/// Create a new empty slot.
|
|
||||||
Slot(Time time) : time(time) {}
|
|
||||||
|
|
||||||
/// Returns true if the slot's time is smaller than the compared slot's time.
|
|
||||||
bool operator<(const Slot &rhs) const;
|
|
||||||
|
|
||||||
/// Returns true if the slot's time is greater than the compared slot's time.
|
|
||||||
bool operator>(const Slot &rhs) const;
|
|
||||||
|
|
||||||
/// Insert a change.
|
|
||||||
void insertChange(int index, int bitOffset, uint8_t *bytes, unsigned width);
|
|
||||||
|
|
||||||
/// Insert a scheduled process wakeup.
|
|
||||||
void insertChange(unsigned inst);
|
|
||||||
|
|
||||||
// A map from signal indexes to change buffers. Makes it easy to sort the
|
|
||||||
// changes such that we can process one signal at a time.
|
|
||||||
llvm::SmallVector<std::pair<unsigned, unsigned>, 32> changes;
|
|
||||||
// Buffers for the signal changes.
|
|
||||||
llvm::SmallVector<std::pair<unsigned, llvm::APInt>, 32> buffers;
|
|
||||||
// The number of used change buffers in the slot.
|
|
||||||
size_t changesSize = 0;
|
|
||||||
|
|
||||||
// Processes with scheduled wakeup.
|
|
||||||
llvm::SmallVector<unsigned, 4> scheduled;
|
|
||||||
Time time;
|
|
||||||
bool unused = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This is equivalent to and std::priorityQueue<Slot> ordered using the greater
|
|
||||||
/// operator, which adds an insertion method to add changes to a slot.
|
|
||||||
class UpdateQueue : public llvm::SmallVector<Slot, 8> {
|
|
||||||
unsigned topSlot = 0;
|
|
||||||
llvm::SmallVector<unsigned, 4> unused;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Check wheter a slot for the given time already exists. If that's the case,
|
|
||||||
/// add the new change to it, else create a new slot and push it to the queue.
|
|
||||||
void insertOrUpdate(Time time, int index, int bitOffset, uint8_t *bytes,
|
|
||||||
unsigned width);
|
|
||||||
|
|
||||||
/// Check wheter a slot for the given time already exists. If that's the case,
|
|
||||||
/// add the scheduled wakeup to it, else create a new slot and push it to the
|
|
||||||
/// queue.
|
|
||||||
void insertOrUpdate(Time time, unsigned inst);
|
|
||||||
|
|
||||||
/// Return a reference to a slot with the given timestamp. If such a slot
|
|
||||||
/// already exists, a reference to it will be returned. Otherwise a reference
|
|
||||||
/// to a fresh slot is returned.
|
|
||||||
Slot &getOrCreateSlot(Time time);
|
|
||||||
|
|
||||||
/// Get a reference to the current top of the queue (the earliest event
|
|
||||||
/// available).
|
|
||||||
const Slot &top();
|
|
||||||
|
|
||||||
/// Pop the current top of the queue. This marks the current top slot as
|
|
||||||
/// unused and resets its internal structures such that they can be reused.
|
|
||||||
void pop();
|
|
||||||
|
|
||||||
unsigned events = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// State structure for process persistence across suspension.
|
|
||||||
struct ProcState {
|
|
||||||
unsigned inst;
|
|
||||||
int resume;
|
|
||||||
bool *senses;
|
|
||||||
uint8_t *resumeState;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The simulator internal representation of an instance.
|
|
||||||
struct Instance {
|
|
||||||
Instance() = default;
|
|
||||||
|
|
||||||
Instance(std::string name)
|
|
||||||
: name(name), procState(nullptr), entityState(nullptr) {}
|
|
||||||
|
|
||||||
// The instance name.
|
|
||||||
std::string name;
|
|
||||||
// The instance's hierarchical path.
|
|
||||||
std::string path;
|
|
||||||
// The instance's base unit.
|
|
||||||
std::string unit;
|
|
||||||
bool isEntity;
|
|
||||||
size_t nArgs = 0;
|
|
||||||
// The arguments and signals of this instance.
|
|
||||||
llvm::SmallVector<SignalDetail, 0> sensitivityList;
|
|
||||||
ProcState *procState;
|
|
||||||
uint8_t *entityState;
|
|
||||||
Time expectedWakeup;
|
|
||||||
// A pointer to the base unit jitted function.
|
|
||||||
void (*unitFPtr)(void **);
|
|
||||||
|
|
||||||
/// Free procState and entityState since they are allocated using 'malloc' in
|
|
||||||
/// the LLVM code generated in LLHDToLLVM.
|
|
||||||
~Instance();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The simulator's state. It contains the current simulation time, signal
|
|
||||||
/// values and the event queue.
|
|
||||||
struct State {
|
|
||||||
/// Construct a new empty (at 0 time) state.
|
|
||||||
State() = default;
|
|
||||||
|
|
||||||
/// State destructor, ensures all malloc'd regions stored in the state are
|
|
||||||
/// correctly free'd.
|
|
||||||
~State();
|
|
||||||
|
|
||||||
/// Pop the head of the queue and update the simulation time.
|
|
||||||
Slot popQueue();
|
|
||||||
|
|
||||||
/// Push a new scheduled wakeup event in the event queue.
|
|
||||||
void pushQueue(Time time, unsigned inst);
|
|
||||||
|
|
||||||
/// Find an instance in the instances list by name and return an
|
|
||||||
/// iterator for it.
|
|
||||||
llvm::SmallVectorTemplateCommon<Instance>::iterator
|
|
||||||
getInstanceIterator(std::string instName);
|
|
||||||
|
|
||||||
/// Add a new signal to the state. Returns the index of the new signal.
|
|
||||||
int addSignal(std::string name, std::string owner);
|
|
||||||
|
|
||||||
int addSignalData(int index, std::string owner, uint8_t *value,
|
|
||||||
uint64_t size);
|
|
||||||
|
|
||||||
void addSignalElement(unsigned, unsigned, unsigned);
|
|
||||||
|
|
||||||
/// Add a pointer to the process persistence state to a process instance.
|
|
||||||
void addProcPtr(std::string name, ProcState *procStatePtr);
|
|
||||||
|
|
||||||
/// Dump a signal to the out stream. One entry is added for every instance
|
|
||||||
/// the signal appears in.
|
|
||||||
void dumpSignal(llvm::raw_ostream &out, int index);
|
|
||||||
|
|
||||||
/// Dump the instance layout. Used for testing purposes.
|
|
||||||
void dumpLayout();
|
|
||||||
|
|
||||||
/// Dump the instances each signal triggers. Used for testing purposes.
|
|
||||||
void dumpSignalTriggers();
|
|
||||||
|
|
||||||
Time time;
|
|
||||||
std::string root;
|
|
||||||
llvm::SmallVector<Instance, 0> instances;
|
|
||||||
llvm::SmallVector<Signal, 0> signals;
|
|
||||||
UpdateQueue queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sim
|
|
||||||
} // namespace llhd
|
|
||||||
} // namespace circt
|
|
||||||
|
|
||||||
#endif // CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H
|
|
|
@ -1,79 +0,0 @@
|
||||||
//===- Trace.h - Simulation trace definition --------------------*- 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 the Trace class, used to handle the signal trace generation
|
|
||||||
// for the llhd-sim tool.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H
|
|
||||||
#define CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H
|
|
||||||
|
|
||||||
#include "State.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class raw_ostream;
|
|
||||||
} // namespace llvm
|
|
||||||
|
|
||||||
namespace circt {
|
|
||||||
namespace llhd {
|
|
||||||
namespace sim {
|
|
||||||
|
|
||||||
enum class TraceMode { Full, Reduced, Merged, MergedReduce, NamedOnly, None };
|
|
||||||
|
|
||||||
class Trace {
|
|
||||||
llvm::raw_ostream &out;
|
|
||||||
std::unique_ptr<State> const &state;
|
|
||||||
TraceMode mode;
|
|
||||||
Time currentTime;
|
|
||||||
// Each entry defines if the respective signal is active for tracing.
|
|
||||||
std::vector<bool> isTraced;
|
|
||||||
// Buffer of changes ready to be flushed.
|
|
||||||
std::vector<std::pair<std::string, std::string>> changes;
|
|
||||||
// Buffer of changes for the merged formats.
|
|
||||||
std::map<std::pair<unsigned, int>, std::string> mergedChanges;
|
|
||||||
// Buffer of last dumped change for each signal.
|
|
||||||
std::map<std::pair<std::string, int>, std::string> lastValue;
|
|
||||||
|
|
||||||
/// Push one change to the changes vector.
|
|
||||||
void pushChange(unsigned inst, unsigned sigIndex, int elem);
|
|
||||||
/// Push one change for each element of a signal if it is of a structured
|
|
||||||
/// type, or the full signal otherwise.
|
|
||||||
void pushAllChanges(unsigned inst, unsigned sigIndex);
|
|
||||||
|
|
||||||
/// Add a merged change to the change buffer.
|
|
||||||
void addChangeMerged(unsigned);
|
|
||||||
|
|
||||||
/// Sorts the changes buffer lexicographically wrt. the hierarchical paths.
|
|
||||||
void sortChanges();
|
|
||||||
|
|
||||||
/// Flush the changes buffer to the output stream with full format.
|
|
||||||
void flushFull();
|
|
||||||
// Flush the changes buffer to the output stream with merged format.
|
|
||||||
void flushMerged();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Trace(std::unique_ptr<State> const &state, llvm::raw_ostream &out,
|
|
||||||
TraceMode mode);
|
|
||||||
|
|
||||||
/// Add a value change to the trace changes buffer.
|
|
||||||
void addChange(unsigned);
|
|
||||||
|
|
||||||
/// Flush the changes buffer to the output stream. The flush can be forced for
|
|
||||||
/// merged changes, flushing even if the next real-time step has not been
|
|
||||||
/// reached.
|
|
||||||
void flush(bool force = false);
|
|
||||||
};
|
|
||||||
} // namespace sim
|
|
||||||
} // namespace llhd
|
|
||||||
} // namespace circt
|
|
||||||
|
|
||||||
#endif // CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H
|
|
|
@ -25,7 +25,6 @@ add_circt_public_c_api_library(CIRCTCAPIConversion
|
||||||
CIRCTHWToSMT
|
CIRCTHWToSMT
|
||||||
CIRCTHWToSV
|
CIRCTHWToSV
|
||||||
CIRCTHWToSystemC
|
CIRCTHWToSystemC
|
||||||
CIRCTLLHDToLLVM
|
|
||||||
CIRCTLoopScheduleToCalyx
|
CIRCTLoopScheduleToCalyx
|
||||||
CIRCTLTLToCore
|
CIRCTLTLToCore
|
||||||
CIRCTMooreToCore
|
CIRCTMooreToCore
|
||||||
|
|
|
@ -20,7 +20,6 @@ add_subdirectory(HWToLLVM)
|
||||||
add_subdirectory(HWToSMT)
|
add_subdirectory(HWToSMT)
|
||||||
add_subdirectory(HWToSV)
|
add_subdirectory(HWToSV)
|
||||||
add_subdirectory(HWToSystemC)
|
add_subdirectory(HWToSystemC)
|
||||||
add_subdirectory(LLHDToLLVM)
|
|
||||||
add_subdirectory(LoopScheduleToCalyx)
|
add_subdirectory(LoopScheduleToCalyx)
|
||||||
add_subdirectory(MooreToCore)
|
add_subdirectory(MooreToCore)
|
||||||
add_subdirectory(PipelineToHW)
|
add_subdirectory(PipelineToHW)
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
add_circt_conversion_library(CIRCTLLHDToLLVM
|
|
||||||
LLHDToLLVM.cpp
|
|
||||||
|
|
||||||
DEPENDS
|
|
||||||
CIRCTConversionPassIncGen
|
|
||||||
MLIRArithDialect
|
|
||||||
|
|
||||||
LINK_COMPONENTS
|
|
||||||
Core
|
|
||||||
|
|
||||||
LINK_LIBS PUBLIC
|
|
||||||
CIRCTLLHD
|
|
||||||
CIRCTComb
|
|
||||||
CIRCTCombToArith
|
|
||||||
CIRCTCombToLLVM
|
|
||||||
CIRCTHWToLLVM
|
|
||||||
CIRCTHW
|
|
||||||
CIRCTSupport
|
|
||||||
MLIRArithToLLVM
|
|
||||||
MLIRControlFlowToLLVM
|
|
||||||
MLIRFuncToLLVM
|
|
||||||
MLIRLLVMCommonConversion
|
|
||||||
MLIRVectorDialect
|
|
||||||
MLIRTransforms
|
|
||||||
MLIRReconcileUnrealizedCasts
|
|
||||||
)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,2 @@
|
||||||
add_subdirectory(IR)
|
add_subdirectory(IR)
|
||||||
if(CIRCT_LLHD_SIM_ENABLED)
|
|
||||||
add_subdirectory(Simulator)
|
|
||||||
endif()
|
|
||||||
add_subdirectory(Transforms)
|
add_subdirectory(Transforms)
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
set(LLVM_OPTIONAL_SOURCES
|
|
||||||
State.cpp
|
|
||||||
Engine.cpp
|
|
||||||
signals-runtime-wrappers.cpp
|
|
||||||
Trace.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_circt_library(CIRCTLLHDSimState
|
|
||||||
State.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_circt_library(CIRCTLLHDSimTrace
|
|
||||||
Trace.cpp
|
|
||||||
|
|
||||||
LINK_LIBS PUBLIC
|
|
||||||
CIRCTLLHDSimState
|
|
||||||
)
|
|
||||||
|
|
||||||
add_circt_library(circt-llhd-signals-runtime-wrappers SHARED
|
|
||||||
signals-runtime-wrappers.cpp
|
|
||||||
|
|
||||||
LINK_LIBS PUBLIC
|
|
||||||
CIRCTLLHDSimState
|
|
||||||
)
|
|
||||||
set_target_properties(circt-llhd-signals-runtime-wrappers
|
|
||||||
PROPERTIES CXX_VISIBILITY_PRESET "default")
|
|
||||||
|
|
||||||
add_circt_library(CIRCTLLHDSimEngine
|
|
||||||
Engine.cpp
|
|
||||||
|
|
||||||
LINK_LIBS PUBLIC
|
|
||||||
CIRCTLLHD
|
|
||||||
CIRCTLLHDToLLVM
|
|
||||||
CIRCTLLHDSimState
|
|
||||||
CIRCTLLHDSimTrace
|
|
||||||
circt-llhd-signals-runtime-wrappers
|
|
||||||
MLIRExecutionEngine
|
|
||||||
)
|
|
|
@ -1,315 +0,0 @@
|
||||||
//===- Engine.cpp - Simulator Engine class implementation -------*- 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 implements the Engine class.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/Engine.h"
|
|
||||||
#include "circt/Conversion/LLHDToLLVM.h"
|
|
||||||
|
|
||||||
#include "mlir/ExecutionEngine/ExecutionEngine.h"
|
|
||||||
#include "mlir/IR/Builders.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/TargetSelect.h"
|
|
||||||
|
|
||||||
using namespace circt::llhd::sim;
|
|
||||||
|
|
||||||
Engine::Engine(
|
|
||||||
llvm::raw_ostream &out, ModuleOp module,
|
|
||||||
llvm::function_ref<mlir::LogicalResult(mlir::ModuleOp)> mlirTransformer,
|
|
||||||
llvm::function_ref<llvm::Error(llvm::Module *)> llvmTransformer,
|
|
||||||
std::string root, TraceMode tm, ArrayRef<StringRef> sharedLibPaths)
|
|
||||||
: out(out), root(root), traceMode(tm) {
|
|
||||||
state = std::make_unique<State>();
|
|
||||||
state->root = root + '.' + root;
|
|
||||||
|
|
||||||
buildLayout(module);
|
|
||||||
|
|
||||||
auto rootEntity = module.lookupSymbol<EntityOp>(root);
|
|
||||||
|
|
||||||
// Insert explicit instantiation of the design root.
|
|
||||||
OpBuilder insertInst =
|
|
||||||
OpBuilder::atBlockEnd(&rootEntity.getBody().getBlocks().front());
|
|
||||||
insertInst.create<InstOp>(rootEntity.getBlocks().front().back().getLoc(),
|
|
||||||
std::nullopt, root, root, ArrayRef<Value>(),
|
|
||||||
ArrayRef<Value>());
|
|
||||||
|
|
||||||
if (failed(mlirTransformer(module))) {
|
|
||||||
llvm::errs() << "failed to apply the MLIR passes\n";
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->module = module;
|
|
||||||
|
|
||||||
llvm::InitializeNativeTarget();
|
|
||||||
llvm::InitializeNativeTargetAsmPrinter();
|
|
||||||
|
|
||||||
mlir::ExecutionEngineOptions options;
|
|
||||||
options.transformer = llvmTransformer;
|
|
||||||
options.sharedLibPaths = sharedLibPaths;
|
|
||||||
auto maybeEngine = mlir::ExecutionEngine::create(this->module, options);
|
|
||||||
assert(maybeEngine && "failed to create JIT");
|
|
||||||
engine = std::move(*maybeEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine::~Engine() = default;
|
|
||||||
|
|
||||||
void Engine::dumpStateLayout() { state->dumpLayout(); }
|
|
||||||
|
|
||||||
void Engine::dumpStateSignalTriggers() { state->dumpSignalTriggers(); }
|
|
||||||
|
|
||||||
int Engine::simulate(int n, uint64_t maxTime) {
|
|
||||||
assert(engine && "engine not found");
|
|
||||||
assert(state && "state not found");
|
|
||||||
|
|
||||||
auto tm = static_cast<TraceMode>(traceMode);
|
|
||||||
Trace trace(state, out, tm);
|
|
||||||
|
|
||||||
SmallVector<void *, 1> arg({&state});
|
|
||||||
// Initialize tbe simulation state.
|
|
||||||
auto invocationResult = engine->invokePacked("llhd_init", arg);
|
|
||||||
if (invocationResult) {
|
|
||||||
llvm::errs() << "Failed invocation of llhd_init: " << invocationResult;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceMode != TraceMode::None) {
|
|
||||||
// Add changes for all the signals' initial values.
|
|
||||||
for (size_t i = 0, e = state->signals.size(); i < e; ++i) {
|
|
||||||
trace.addChange(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a dummy event to get the simulation started.
|
|
||||||
state->queue.push_back(Slot(Time()));
|
|
||||||
++state->queue.events;
|
|
||||||
|
|
||||||
// Keep track of the instances that need to wakeup.
|
|
||||||
llvm::SmallVector<unsigned, 8> wakeupQueue;
|
|
||||||
|
|
||||||
// Add all instances to the wakeup queue for the first run and add the jitted
|
|
||||||
// function pointers to all of the instances to make them readily available.
|
|
||||||
for (size_t i = 0, e = state->instances.size(); i < e; ++i) {
|
|
||||||
wakeupQueue.push_back(i);
|
|
||||||
auto &inst = state->instances[i];
|
|
||||||
auto expectedFPtr = engine->lookupPacked(inst.unit);
|
|
||||||
if (!expectedFPtr) {
|
|
||||||
llvm::errs() << "Could not lookup " << inst.unit << "!\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
inst.unitFPtr = *expectedFPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cycle = 0;
|
|
||||||
while (state->queue.events > 0) {
|
|
||||||
const auto &pop = state->queue.top();
|
|
||||||
|
|
||||||
// Interrupt the simulation if a stop condition is met.
|
|
||||||
if ((n > 0 && cycle >= n) ||
|
|
||||||
(maxTime > 0 && pop.time.getTime() > maxTime)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the simulation time.
|
|
||||||
state->time = pop.time;
|
|
||||||
|
|
||||||
if (traceMode != TraceMode::None)
|
|
||||||
trace.flush();
|
|
||||||
|
|
||||||
// Process signal changes.
|
|
||||||
size_t i = 0, e = pop.changesSize;
|
|
||||||
while (i < e) {
|
|
||||||
const auto sigIndex = pop.changes[i].first;
|
|
||||||
auto &curr = state->signals[sigIndex];
|
|
||||||
APInt buff(curr.getSize() * 8, 0);
|
|
||||||
llvm::LoadIntFromMemory(buff, curr.getValue(), curr.getSize());
|
|
||||||
|
|
||||||
// Apply the changes to the buffer until we reach the next signal.
|
|
||||||
while (i < e && pop.changes[i].first == sigIndex) {
|
|
||||||
const auto &change = pop.buffers[pop.changes[i].second];
|
|
||||||
const auto offset = change.first;
|
|
||||||
const auto &drive = change.second;
|
|
||||||
if (drive.getBitWidth() < buff.getBitWidth())
|
|
||||||
buff.insertBits(drive, offset);
|
|
||||||
else
|
|
||||||
buff = drive;
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!curr.updateWhenChanged(buff.getRawData()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Add sensitive instances.
|
|
||||||
for (auto inst : curr.getTriggeredInstanceIndices()) {
|
|
||||||
// Skip if the process is not currently sensible to the signal.
|
|
||||||
if (!state->instances[inst].isEntity) {
|
|
||||||
const auto &sensList = state->instances[inst].sensitivityList;
|
|
||||||
auto it = std::find_if(sensList.begin(), sensList.end(),
|
|
||||||
[sigIndex](const SignalDetail &sig) {
|
|
||||||
return sig.globalIndex == sigIndex;
|
|
||||||
});
|
|
||||||
if (sensList.end() != it &&
|
|
||||||
state->instances[inst].procState->senses[it - sensList.begin()] ==
|
|
||||||
0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Invalidate scheduled wakeup
|
|
||||||
state->instances[inst].expectedWakeup = Time();
|
|
||||||
}
|
|
||||||
wakeupQueue.push_back(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the updated signal.
|
|
||||||
if (traceMode != TraceMode::None)
|
|
||||||
trace.addChange(sigIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add scheduled process resumes to the wakeup queue.
|
|
||||||
for (auto inst : pop.scheduled) {
|
|
||||||
if (state->time == state->instances[inst].expectedWakeup)
|
|
||||||
wakeupQueue.push_back(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
state->queue.pop();
|
|
||||||
|
|
||||||
std::sort(wakeupQueue.begin(), wakeupQueue.end());
|
|
||||||
wakeupQueue.erase(std::unique(wakeupQueue.begin(), wakeupQueue.end()),
|
|
||||||
wakeupQueue.end());
|
|
||||||
|
|
||||||
// Run the instances present in the wakeup queue.
|
|
||||||
for (auto i : wakeupQueue) {
|
|
||||||
auto &inst = state->instances[i];
|
|
||||||
auto signalTable = inst.sensitivityList.data();
|
|
||||||
|
|
||||||
// Gather the instance arguments for unit invocation.
|
|
||||||
SmallVector<void *, 3> args;
|
|
||||||
if (inst.isEntity)
|
|
||||||
args.assign({&state, &inst.entityState, &signalTable});
|
|
||||||
else {
|
|
||||||
args.assign({&state, &inst.procState, &signalTable});
|
|
||||||
}
|
|
||||||
// Run the unit.
|
|
||||||
(*inst.unitFPtr)(args.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear wakeup queue.
|
|
||||||
wakeupQueue.clear();
|
|
||||||
++cycle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceMode != TraceMode::None) {
|
|
||||||
// Flush any remainign changes
|
|
||||||
trace.flush(/*force=*/true);
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::errs() << "Finished at " << state->time.toString() << " (" << cycle
|
|
||||||
<< " cycles)\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::buildLayout(ModuleOp module) {
|
|
||||||
// Start from the root entity.
|
|
||||||
auto rootEntity = module.lookupSymbol<EntityOp>(root);
|
|
||||||
assert(rootEntity && "root entity not found!");
|
|
||||||
|
|
||||||
// Build root instance, the parent and instance names are the same for the
|
|
||||||
// root.
|
|
||||||
Instance rootInst(state->root);
|
|
||||||
rootInst.unit = root;
|
|
||||||
rootInst.path = root;
|
|
||||||
|
|
||||||
// Recursively walk the units starting at root.
|
|
||||||
walkEntity(rootEntity, rootInst);
|
|
||||||
|
|
||||||
// The root is always an instance.
|
|
||||||
rootInst.isEntity = true;
|
|
||||||
// Store the root instance.
|
|
||||||
state->instances.push_back(std::move(rootInst));
|
|
||||||
|
|
||||||
// Add triggers to signals.
|
|
||||||
for (size_t i = 0, e = state->instances.size(); i < e; ++i) {
|
|
||||||
auto &inst = state->instances[i];
|
|
||||||
for (auto trigger : inst.sensitivityList) {
|
|
||||||
state->signals[trigger.globalIndex].pushInstanceIndex(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::walkEntity(EntityOp entity, Instance &child) {
|
|
||||||
entity.walk([&](Operation *op) {
|
|
||||||
assert(op);
|
|
||||||
|
|
||||||
// Add a signal to the signal table.
|
|
||||||
if (auto sig = dyn_cast<SigOp>(op)) {
|
|
||||||
uint64_t index = state->addSignal(sig.getName().str(), child.name);
|
|
||||||
child.sensitivityList.push_back(
|
|
||||||
SignalDetail({nullptr, 0, child.sensitivityList.size(), index}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build (recursive) instance layout.
|
|
||||||
if (auto inst = dyn_cast<InstOp>(op)) {
|
|
||||||
// Skip self-recursion.
|
|
||||||
if (inst.getCallee() == child.name)
|
|
||||||
return;
|
|
||||||
if (auto e =
|
|
||||||
op->getParentOfType<ModuleOp>().lookupSymbol(inst.getCallee())) {
|
|
||||||
Instance newChild(child.unit + '.' + inst.getName().str());
|
|
||||||
newChild.unit = inst.getCallee().str();
|
|
||||||
newChild.nArgs = inst.getNumOperands();
|
|
||||||
newChild.path = child.path + "/" + inst.getName().str();
|
|
||||||
|
|
||||||
// Add instance arguments to sensitivity list. The first nArgs signals
|
|
||||||
// in the sensitivity list represent the unit's arguments, while the
|
|
||||||
// following ones represent the unit-defined signals.
|
|
||||||
llvm::SmallVector<Value, 8> args;
|
|
||||||
args.insert(args.end(), inst.getInputs().begin(),
|
|
||||||
inst.getInputs().end());
|
|
||||||
args.insert(args.end(), inst.getOutputs().begin(),
|
|
||||||
inst.getOutputs().end());
|
|
||||||
|
|
||||||
for (size_t i = 0, e = args.size(); i < e; ++i) {
|
|
||||||
// The signal comes from an instance's argument.
|
|
||||||
if (auto blockArg = dyn_cast<BlockArgument>(args[i])) {
|
|
||||||
auto detail = child.sensitivityList[blockArg.getArgNumber()];
|
|
||||||
detail.instIndex = i;
|
|
||||||
newChild.sensitivityList.push_back(detail);
|
|
||||||
} else if (auto sigOp = dyn_cast<SigOp>(args[i].getDefiningOp())) {
|
|
||||||
// The signal comes from one of the instance's owned signals.
|
|
||||||
auto it = std::find_if(
|
|
||||||
child.sensitivityList.begin(), child.sensitivityList.end(),
|
|
||||||
[&](SignalDetail &detail) {
|
|
||||||
return state->signals[detail.globalIndex].getName() ==
|
|
||||||
sigOp.getName() &&
|
|
||||||
state->signals[detail.globalIndex].getOwner() ==
|
|
||||||
child.name;
|
|
||||||
});
|
|
||||||
if (it != child.sensitivityList.end()) {
|
|
||||||
auto detail = *it;
|
|
||||||
detail.instIndex = i;
|
|
||||||
newChild.sensitivityList.push_back(detail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively walk a new entity, otherwise it is a process and cannot
|
|
||||||
// define new signals or instances.
|
|
||||||
if (auto ent = dyn_cast<EntityOp>(e)) {
|
|
||||||
newChild.isEntity = true;
|
|
||||||
walkEntity(ent, newChild);
|
|
||||||
} else {
|
|
||||||
newChild.isEntity = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the created instance.
|
|
||||||
state->instances.push_back(std::move(newChild));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,314 +0,0 @@
|
||||||
//===- State.cpp - LLHD simulator state -------------------------*- 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 implements the constructs used to keep track of the simulation
|
|
||||||
// state in the LLHD simulator.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/State.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/Format.h"
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
using namespace circt::llhd::sim;
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Time
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
std::string Time::toString() const {
|
|
||||||
return std::to_string(time) + "ps " + std::to_string(delta) + "d " +
|
|
||||||
std::to_string(eps) + "e";
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Signal
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
std::string Signal::toHexString() const {
|
|
||||||
std::string ret;
|
|
||||||
raw_string_ostream ss(ret);
|
|
||||||
ss << "0x";
|
|
||||||
for (int i = size - 1; i >= 0; --i) {
|
|
||||||
ss << format_hex_no_prefix(static_cast<int>(value[i]), 2);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Signal::toHexString(unsigned elemIndex) const {
|
|
||||||
assert(elements.size() > 0 && "the signal type has to be tuple or array!");
|
|
||||||
auto elemSize = elements[elemIndex].second;
|
|
||||||
auto *ptr = value + elements[elemIndex].first;
|
|
||||||
std::string ret;
|
|
||||||
raw_string_ostream ss(ret);
|
|
||||||
ss << "0x";
|
|
||||||
for (int i = elemSize - 1; i >= 0; --i) {
|
|
||||||
ss << format_hex_no_prefix(static_cast<int>(ptr[i]), 2);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Signal::~Signal() {
|
|
||||||
std::free(value);
|
|
||||||
value = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Slot
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
bool Slot::operator<(const Slot &rhs) const { return time < rhs.time; }
|
|
||||||
|
|
||||||
bool Slot::operator>(const Slot &rhs) const { return rhs.time < time; }
|
|
||||||
|
|
||||||
void Slot::insertChange(int index, int bitOffset, uint8_t *bytes,
|
|
||||||
unsigned width) {
|
|
||||||
// Get the amount of 64 bit words required to store the value in an APInt.
|
|
||||||
auto size = llvm::divideCeil(width, 8);
|
|
||||||
|
|
||||||
APInt buffer(width, 0);
|
|
||||||
llvm::LoadIntFromMemory(buffer, bytes, size);
|
|
||||||
auto offsetBufferPair = std::make_pair(bitOffset, buffer);
|
|
||||||
|
|
||||||
if (changesSize >= buffers.size()) {
|
|
||||||
// Create a new change buffer if we don't have any unused one available for
|
|
||||||
// reuse.
|
|
||||||
buffers.push_back(offsetBufferPair);
|
|
||||||
} else {
|
|
||||||
// Reuse the first available buffer.
|
|
||||||
buffers[changesSize] = offsetBufferPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the signal index to the change buffer so we can retrieve
|
|
||||||
// it after sorting.
|
|
||||||
changes.push_back(std::make_pair(index, changesSize));
|
|
||||||
++changesSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Slot::insertChange(unsigned inst) { scheduled.push_back(inst); }
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// UpdateQueue
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
void UpdateQueue::insertOrUpdate(Time time, int index, int bitOffset,
|
|
||||||
uint8_t *bytes, unsigned width) {
|
|
||||||
auto &slot = getOrCreateSlot(time);
|
|
||||||
slot.insertChange(index, bitOffset, bytes, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateQueue::insertOrUpdate(Time time, unsigned inst) {
|
|
||||||
auto &slot = getOrCreateSlot(time);
|
|
||||||
slot.insertChange(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
Slot &UpdateQueue::getOrCreateSlot(Time time) {
|
|
||||||
auto &top = begin()[topSlot];
|
|
||||||
|
|
||||||
// Directly add to top slot.
|
|
||||||
if (!top.unused && time == top.time) {
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to search through the queue for an existing slot only if we're
|
|
||||||
// spawning an event later than the top slot. Adding to an existing slot
|
|
||||||
// scheduled earlier than the top slot should never happens, as then it should
|
|
||||||
// be the top.
|
|
||||||
if (events > 0 && top.time < time) {
|
|
||||||
for (size_t i = 0, e = size(); i < e; ++i) {
|
|
||||||
if (time == begin()[i].time) {
|
|
||||||
return begin()[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn new event using an existing slot.
|
|
||||||
if (!unused.empty()) {
|
|
||||||
auto firstUnused = unused.pop_back_val();
|
|
||||||
auto &newSlot = begin()[firstUnused];
|
|
||||||
newSlot.unused = false;
|
|
||||||
newSlot.time = time;
|
|
||||||
|
|
||||||
// Update the top of the queue either if it is currently unused or the new
|
|
||||||
// timestamp is earlier than it.
|
|
||||||
if (top.unused || time < top.time)
|
|
||||||
topSlot = firstUnused;
|
|
||||||
|
|
||||||
++events;
|
|
||||||
return newSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We do not have pre-allocated slots available, generate a new one.
|
|
||||||
push_back(Slot(time));
|
|
||||||
|
|
||||||
// Update the top of the queue either if it is currently unused or the new
|
|
||||||
// timestamp is earlier than it.
|
|
||||||
if (top.unused || time < top.time)
|
|
||||||
topSlot = size() - 1;
|
|
||||||
|
|
||||||
++events;
|
|
||||||
return back();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Slot &UpdateQueue::top() {
|
|
||||||
assert(topSlot < size() && "top is pointing out of bounds!");
|
|
||||||
|
|
||||||
// Sort the changes of the top slot such that all changes to the same signal
|
|
||||||
// are in succession.
|
|
||||||
auto &top = begin()[topSlot];
|
|
||||||
llvm::sort(top.changes.begin(), top.changes.begin() + top.changesSize);
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateQueue::pop() {
|
|
||||||
// Reset internal structures and decrease the event counter.
|
|
||||||
auto &curr = begin()[topSlot];
|
|
||||||
curr.unused = true;
|
|
||||||
curr.changesSize = 0;
|
|
||||||
curr.scheduled.clear();
|
|
||||||
curr.changes.clear();
|
|
||||||
curr.time = Time();
|
|
||||||
--events;
|
|
||||||
|
|
||||||
// Add to unused slots list for easy retrieval.
|
|
||||||
unused.push_back(topSlot);
|
|
||||||
|
|
||||||
// Update the current top of the queue.
|
|
||||||
topSlot = std::distance(
|
|
||||||
begin(),
|
|
||||||
std::min_element(begin(), end(), [](const auto &a, const auto &b) {
|
|
||||||
// a is "smaller" than b if either a's timestamp is earlier than b's, or
|
|
||||||
// b is unused (i.e. b has no actual meaning).
|
|
||||||
return !a.unused && (a < b || b.unused);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Instance
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
Instance::~Instance() {
|
|
||||||
std::free(procState);
|
|
||||||
procState = nullptr;
|
|
||||||
std::free(entityState);
|
|
||||||
entityState = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// State
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
State::~State() {
|
|
||||||
for (auto &inst : instances) {
|
|
||||||
if (inst.procState) {
|
|
||||||
std::free(inst.procState->senses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Slot State::popQueue() {
|
|
||||||
assert(!queue.empty() && "the event queue is empty");
|
|
||||||
Slot pop = queue.top();
|
|
||||||
queue.pop();
|
|
||||||
return pop;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::pushQueue(Time t, unsigned inst) {
|
|
||||||
Time newTime = time + t;
|
|
||||||
queue.insertOrUpdate(newTime, inst);
|
|
||||||
instances[inst].expectedWakeup = newTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::SmallVectorTemplateCommon<Instance>::iterator
|
|
||||||
State::getInstanceIterator(std::string instName) {
|
|
||||||
auto it =
|
|
||||||
std::find_if(instances.begin(), instances.end(),
|
|
||||||
[&](const auto &inst) { return instName == inst.name; });
|
|
||||||
|
|
||||||
assert(it != instances.end() && "instance does not exist!");
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
int State::addSignal(std::string name, std::string owner) {
|
|
||||||
signals.push_back(Signal(name, owner));
|
|
||||||
return signals.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::addProcPtr(std::string name, ProcState *procStatePtr) {
|
|
||||||
auto it = getInstanceIterator(name);
|
|
||||||
|
|
||||||
// Store instance index in process state.
|
|
||||||
procStatePtr->inst = it - instances.begin();
|
|
||||||
(*it).procState = procStatePtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int State::addSignalData(int index, std::string owner, uint8_t *value,
|
|
||||||
uint64_t size) {
|
|
||||||
auto it = getInstanceIterator(owner);
|
|
||||||
|
|
||||||
uint64_t globalIdx = (*it).sensitivityList[index + (*it).nArgs].globalIndex;
|
|
||||||
auto &sig = signals[globalIdx];
|
|
||||||
|
|
||||||
// Add pointer and size to global signal table entry.
|
|
||||||
sig.store(value, size);
|
|
||||||
|
|
||||||
// Add the value pointer to the signal detail struct for each instance this
|
|
||||||
// signal appears in.
|
|
||||||
for (auto inst : signals[globalIdx].getTriggeredInstanceIndices()) {
|
|
||||||
for (auto &detail : instances[inst].sensitivityList) {
|
|
||||||
if (detail.globalIndex == globalIdx) {
|
|
||||||
detail.value = sig.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return globalIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::addSignalElement(unsigned index, unsigned offset, unsigned size) {
|
|
||||||
signals[index].pushElement(std::make_pair(offset, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::dumpSignal(llvm::raw_ostream &out, int index) {
|
|
||||||
auto &sig = signals[index];
|
|
||||||
for (auto inst : sig.getTriggeredInstanceIndices()) {
|
|
||||||
out << time.toString() << " " << instances[inst].path << "/"
|
|
||||||
<< sig.getName() << " " << sig.toHexString() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::dumpLayout() {
|
|
||||||
llvm::errs() << "::------------------- Layout -------------------::\n";
|
|
||||||
for (const auto &inst : instances) {
|
|
||||||
llvm::errs() << inst.name << ":\n";
|
|
||||||
llvm::errs() << "---path: " << inst.path << "\n";
|
|
||||||
llvm::errs() << "---isEntity: " << inst.isEntity << "\n";
|
|
||||||
llvm::errs() << "---sensitivity list: ";
|
|
||||||
for (auto in : inst.sensitivityList) {
|
|
||||||
llvm::errs() << in.globalIndex << " ";
|
|
||||||
}
|
|
||||||
llvm::errs() << "\n";
|
|
||||||
}
|
|
||||||
llvm::errs() << "::----------------------------------------------::\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::dumpSignalTriggers() {
|
|
||||||
llvm::errs() << "::------------- Signal information -------------::\n";
|
|
||||||
for (size_t i = 0, e = signals.size(); i < e; ++i) {
|
|
||||||
llvm::errs() << signals[i].getOwner() << "/" << signals[i].getName()
|
|
||||||
<< " triggers: ";
|
|
||||||
for (auto trig : signals[i].getTriggeredInstanceIndices()) {
|
|
||||||
llvm::errs() << trig << " ";
|
|
||||||
}
|
|
||||||
llvm::errs() << "\n";
|
|
||||||
}
|
|
||||||
llvm::errs() << "::----------------------------------------------::\n";
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
//===- Trace.cpp - Simulation trace 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 file implements the Trace class, used to handle the signal trace
|
|
||||||
// generation for the llhd-sim tool.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/Trace.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
|
|
||||||
using namespace circt::llhd::sim;
|
|
||||||
|
|
||||||
Trace::Trace(std::unique_ptr<State> const &state, llvm::raw_ostream &out,
|
|
||||||
TraceMode mode)
|
|
||||||
: out(out), state(state), mode(mode) {
|
|
||||||
auto root = state->root;
|
|
||||||
for (auto &sig : state->signals) {
|
|
||||||
bool done = (mode != TraceMode::Full && mode != TraceMode::Merged &&
|
|
||||||
!sig.isOwner(root)) ||
|
|
||||||
(mode == TraceMode::NamedOnly && sig.isValidSigName());
|
|
||||||
isTraced.push_back(!done);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Changes gathering methods
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
void Trace::pushChange(unsigned inst, unsigned sigIndex, int elem = -1) {
|
|
||||||
auto &sig = state->signals[sigIndex];
|
|
||||||
std::string valueDump;
|
|
||||||
std::string path;
|
|
||||||
llvm::raw_string_ostream ss(path);
|
|
||||||
|
|
||||||
ss << state->instances[inst].path << '/' << sig.getName();
|
|
||||||
|
|
||||||
if (elem >= 0) {
|
|
||||||
// Add element index to the hierarchical path.
|
|
||||||
ss << '[' << elem << ']';
|
|
||||||
// Get element value dump.
|
|
||||||
valueDump = sig.toHexString(elem);
|
|
||||||
} else {
|
|
||||||
// Get signal value dump.
|
|
||||||
valueDump = sig.toHexString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check wheter we have an actual change from last value.
|
|
||||||
auto lastValKey = std::make_pair(path, elem);
|
|
||||||
if (valueDump != lastValue[lastValKey]) {
|
|
||||||
changes.push_back(std::make_pair(path, valueDump));
|
|
||||||
lastValue[lastValKey] = valueDump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::pushAllChanges(unsigned inst, unsigned sigIndex) {
|
|
||||||
auto &sig = state->signals[sigIndex];
|
|
||||||
if (sig.hasElement()) {
|
|
||||||
// Push changes for all signal elements.
|
|
||||||
for (size_t i = 0, e = sig.getElementSize(); i < e; ++i) {
|
|
||||||
pushChange(inst, sigIndex, i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Push one change for the whole signal.
|
|
||||||
pushChange(inst, sigIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::addChange(unsigned sigIndex) {
|
|
||||||
currentTime = state->time;
|
|
||||||
if (isTraced[sigIndex]) {
|
|
||||||
if (mode == TraceMode::Full) {
|
|
||||||
auto &sig = state->signals[sigIndex];
|
|
||||||
// Add a change for each connected instance.
|
|
||||||
for (auto inst : sig.getTriggeredInstanceIndices()) {
|
|
||||||
pushAllChanges(inst, sigIndex);
|
|
||||||
}
|
|
||||||
} else if (mode == TraceMode::Reduced) {
|
|
||||||
// The root is always the last instance in the instances list.
|
|
||||||
pushAllChanges(state->instances.size() - 1, sigIndex);
|
|
||||||
} else if (mode == TraceMode::Merged || mode == TraceMode::MergedReduce ||
|
|
||||||
mode == TraceMode::NamedOnly) {
|
|
||||||
addChangeMerged(sigIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::addChangeMerged(unsigned sigIndex) {
|
|
||||||
auto &sig = state->signals[sigIndex];
|
|
||||||
if (sig.hasElement()) {
|
|
||||||
// Add a change for all sub-elements
|
|
||||||
for (size_t i = 0, e = sig.getElementSize(); i < e; ++i) {
|
|
||||||
auto valueDump = sig.toHexString(i);
|
|
||||||
mergedChanges[std::make_pair(sigIndex, i)] = valueDump;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add one change for the whole signal.
|
|
||||||
auto valueDump = sig.toHexString();
|
|
||||||
mergedChanges[std::make_pair(sigIndex, -1)] = valueDump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Flush methods
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
void Trace::sortChanges() {
|
|
||||||
std::sort(changes.begin(), changes.end(),
|
|
||||||
[](std::pair<std::string, std::string> &lhs,
|
|
||||||
std::pair<std::string, std::string> &rhs) -> bool {
|
|
||||||
return lhs.first < rhs.first;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::flush(bool force) {
|
|
||||||
if (mode == TraceMode::Full || mode == TraceMode::Reduced)
|
|
||||||
flushFull();
|
|
||||||
else if (mode == TraceMode::Merged || mode == TraceMode::MergedReduce ||
|
|
||||||
mode == TraceMode::NamedOnly)
|
|
||||||
if (state->time.getTime() > currentTime.getTime() || force)
|
|
||||||
flushMerged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::flushFull() {
|
|
||||||
if (changes.size() > 0) {
|
|
||||||
sortChanges();
|
|
||||||
|
|
||||||
auto timeDump = currentTime.toString();
|
|
||||||
for (auto change : changes) {
|
|
||||||
out << timeDump << " " << change.first << " " << change.second << "\n";
|
|
||||||
}
|
|
||||||
changes.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::flushMerged() {
|
|
||||||
// Move the merged changes to the changes vector for dumping.
|
|
||||||
for (auto elem : mergedChanges) {
|
|
||||||
auto sigIndex = elem.first.first;
|
|
||||||
auto sigElem = elem.first.second;
|
|
||||||
auto &sig = state->signals[sigIndex];
|
|
||||||
auto change = elem.second;
|
|
||||||
|
|
||||||
if (mode == TraceMode::Merged) {
|
|
||||||
// Add the changes for all connected instances.
|
|
||||||
for (auto inst : sig.getTriggeredInstanceIndices()) {
|
|
||||||
pushChange(inst, sigIndex, sigElem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The root is always the last instance in the instances list.
|
|
||||||
pushChange(state->instances.size() - 1, sigIndex, sigElem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.size() > 0) {
|
|
||||||
sortChanges();
|
|
||||||
|
|
||||||
// Flush the changes to output stream.
|
|
||||||
out << currentTime.getTime() << "ps\n";
|
|
||||||
for (auto change : changes) {
|
|
||||||
out << " " << change.first << " " << change.second << "\n";
|
|
||||||
}
|
|
||||||
mergedChanges.clear();
|
|
||||||
changes.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
//===- signals-runtime-wrappers.cpp - Runtime library 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 file implements the runtime library used in LLHD simulation.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "signals-runtime-wrappers.h"
|
|
||||||
|
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
using namespace circt::llhd::sim;
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Runtime interface
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
int allocSignal(State *state, int index, char *owner, uint8_t *value,
|
|
||||||
int64_t size) {
|
|
||||||
assert(state && "alloc_signal: state not found");
|
|
||||||
std::string sOwner(owner);
|
|
||||||
|
|
||||||
return state->addSignalData(index, sOwner, value, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSigArrayElements(State *state, unsigned index, unsigned size,
|
|
||||||
unsigned numElements) {
|
|
||||||
for (size_t i = 0; i < numElements; ++i)
|
|
||||||
state->addSignalElement(index, size * i, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSigStructElement(State *state, unsigned index, unsigned offset,
|
|
||||||
unsigned size) {
|
|
||||||
state->addSignalElement(index, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocProc(State *state, char *owner, ProcState *procState) {
|
|
||||||
assert(state && "alloc_proc: state not found");
|
|
||||||
std::string sOwner(owner);
|
|
||||||
state->addProcPtr(sOwner, procState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocEntity(State *state, char *owner, uint8_t *entityState) {
|
|
||||||
assert(state && "alloc_entity: state not found");
|
|
||||||
auto it = state->getInstanceIterator(owner);
|
|
||||||
(*it).entityState = entityState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void driveSignal(State *state, SignalDetail *detail, uint8_t *value,
|
|
||||||
uint64_t width, int time, int delta, int eps) {
|
|
||||||
assert(state && "drive_signal: state not found");
|
|
||||||
|
|
||||||
auto globalIndex = detail->globalIndex;
|
|
||||||
auto offset = detail->offset;
|
|
||||||
|
|
||||||
int bitOffset =
|
|
||||||
(detail->value - state->signals[globalIndex].getValue()) * 8 + offset;
|
|
||||||
|
|
||||||
// Spawn a new event.
|
|
||||||
state->queue.insertOrUpdate(state->time + Time(time, delta, eps), globalIndex,
|
|
||||||
bitOffset, value, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
void llhdSuspend(State *state, ProcState *procState, int time, int delta,
|
|
||||||
int eps) {
|
|
||||||
// Add a new scheduled wake up if a time is specified.
|
|
||||||
if (time || delta || eps) {
|
|
||||||
Time sTime(time, delta, eps);
|
|
||||||
state->pushQueue(sTime, procState->inst);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
//===- signals-runtime-wrappers.h - Simulation runtime library --*- 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 runtime library used in LLHD simulation.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H
|
|
||||||
#define CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H
|
|
||||||
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/State.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Runtime interfaces
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
/// Allocate a new signal. The index of the new signal in the state's list of
|
|
||||||
/// signals is returned.
|
|
||||||
int allocSignal(circt::llhd::sim::State *state, int index, char *owner,
|
|
||||||
uint8_t *value, int64_t size);
|
|
||||||
|
|
||||||
/// Add offset and size information for the elements of an array signal.
|
|
||||||
void addSigArrayElements(circt::llhd::sim::State *state, unsigned index,
|
|
||||||
unsigned size, unsigned numElements);
|
|
||||||
|
|
||||||
/// Add offset and size information for one element of a struct signal. Elements
|
|
||||||
/// are assumed to be added (by calling this function) in sequential order, from
|
|
||||||
/// first to last.
|
|
||||||
void addSigStructElement(circt::llhd::sim::State *state, unsigned index,
|
|
||||||
unsigned offset, unsigned size);
|
|
||||||
|
|
||||||
/// Add allocated constructs to a process instance.
|
|
||||||
void allocProc(circt::llhd::sim::State *state, char *owner,
|
|
||||||
circt::llhd::sim::ProcState *procState);
|
|
||||||
|
|
||||||
/// Add allocated entity state to the given instance.
|
|
||||||
void allocEntity(circt::llhd::sim::State *state, char *owner,
|
|
||||||
uint8_t *entityState);
|
|
||||||
|
|
||||||
/// Drive a value onto a signal.
|
|
||||||
void driveSignal(circt::llhd::sim::State *state,
|
|
||||||
circt::llhd::sim::SignalDetail *index, uint8_t *value,
|
|
||||||
uint64_t width, int time, int delta, int eps);
|
|
||||||
|
|
||||||
/// Suspend a process.
|
|
||||||
void llhdSuspend(circt::llhd::sim::State *state,
|
|
||||||
circt::llhd::sim::ProcState *procState, int time, int delta,
|
|
||||||
int eps);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H
|
|
|
@ -38,11 +38,6 @@ if (CIRCT_GTEST_AVAILABLE)
|
||||||
list(APPEND CIRCT_TEST_DEPENDS CIRCTUnitTests)
|
list(APPEND CIRCT_TEST_DEPENDS CIRCTUnitTests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CIRCT_LLHD_SIM_ENABLED)
|
|
||||||
list(APPEND CIRCT_TEST_DEPENDS llhd-sim)
|
|
||||||
list(APPEND CIRCT_TEST_DEPENDS circt-llhd-signals-runtime-wrappers)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CIRCT_SLANG_FRONTEND_ENABLED)
|
if(CIRCT_SLANG_FRONTEND_ENABLED)
|
||||||
list(APPEND CIRCT_TEST_DEPENDS circt-verilog)
|
list(APPEND CIRCT_TEST_DEPENDS circt-verilog)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts --split-input-file | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_empty(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
// CHECK: llvm.return
|
|
||||||
// CHECK: }
|
|
||||||
llhd.entity @convert_empty() -> () {}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_one_input(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
// CHECK: [[IN0:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.return
|
|
||||||
// CHECK: }
|
|
||||||
llhd.entity @convert_one_input(%in0 : !llhd.sig<i1>) -> () {}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_one_output(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
// CHECK: [[OUT0:%.*]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.return
|
|
||||||
// CHECK: }
|
|
||||||
llhd.entity @convert_one_output () -> (%out0 : !llhd.sig<i1>) {}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_input_and_output(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
// CHECK: [[IN0:%.*]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OUT0:%.*]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.return
|
|
||||||
// CHECK: }
|
|
||||||
llhd.entity @convert_input_and_output (%in0 : !llhd.sig<i1>) -> (%out0 : !llhd.sig<i1>) {}
|
|
|
@ -1,126 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convertSigExtract(
|
|
||||||
// CHECK-SAME: %arg0: i5, %arg1: !llvm.ptr)
|
|
||||||
func.func @convertSigExtract(%arg0: i5, %arg1: !llhd.sig<i32>) {
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
|
|
||||||
// Adjust offset
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i5 to i64
|
|
||||||
// CHECK: [[NEW_OFFSET:%.+]] = llvm.add [[OFFSET]], [[TMP]]
|
|
||||||
|
|
||||||
// Adjust value pointer to closest byte
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.ptrtoint [[VALUE_PTR]]
|
|
||||||
// CHECK: [[C8_I64:%.+]] = llvm.mlir.constant(8 :
|
|
||||||
// CHECK: [[BYTE_OFFSET:%.+]] = llvm.udiv [[NEW_OFFSET]], [[C8_I64]]
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.add [[TMP1]], [[BYTE_OFFSET]]
|
|
||||||
// CHECK: [[VALUE_PTR:%.+]] = llvm.inttoptr [[TMP2]]
|
|
||||||
|
|
||||||
// Adjust offset to closest byte
|
|
||||||
// CHECK: [[OFFSET:%.+]] = llvm.urem [[NEW_OFFSET]], [[C8_I64]]
|
|
||||||
|
|
||||||
// Create new signal struct on the stack.
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.insertvalue [[VALUE_PTR]], [[TMP1]][0]
|
|
||||||
// CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1]
|
|
||||||
// CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2]
|
|
||||||
// CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3]
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.store [[TMP5]], [[BUF]]
|
|
||||||
|
|
||||||
llhd.sig.extract %arg1 from %arg0 : (!llhd.sig<i32>) -> !llhd.sig<i10>
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convertSigArrayGet(
|
|
||||||
// CHECK-SAME: %arg0: i2, %arg1: !llvm.ptr)
|
|
||||||
func.func @convertSigArrayGet(%arg0 : i2, %arg1 : !llhd.sig<!hw.array<4xi4>>) {
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
|
|
||||||
// Adjust value pointer
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i2 to i3
|
|
||||||
// CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, [[TMP]]] : (!llvm.ptr, i3) -> !llvm.ptr, !llvm.array<4 x i4>
|
|
||||||
|
|
||||||
// Create new signal struct on the stack.
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0]
|
|
||||||
// CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1]
|
|
||||||
// CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2]
|
|
||||||
// CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3]
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.store [[TMP5]], [[BUF]]
|
|
||||||
|
|
||||||
llhd.sig.array_get %arg1[%arg0] : !llhd.sig<!hw.array<4xi4>>
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convertSigArraySlice(
|
|
||||||
// CHECK-SAME: %arg0: i2, %arg1: !llvm.ptr)
|
|
||||||
func.func @convertSigArraySlice(%arg0: i2, %arg1: !llhd.sig<!hw.array<4xi4>>) {
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
|
|
||||||
// Adjust value pointer
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i2 to i3
|
|
||||||
// CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, [[TMP]]] : (!llvm.ptr, i3) -> !llvm.ptr, !llvm.array<4 x i4>
|
|
||||||
|
|
||||||
// Create new signal struct on the stack.
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0]
|
|
||||||
// CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1]
|
|
||||||
// CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2]
|
|
||||||
// CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3]
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.store [[TMP5]], [[BUF]]
|
|
||||||
|
|
||||||
llhd.sig.array_slice %arg1 at %arg0 : (!llhd.sig<!hw.array<4xi4>>) -> !llhd.sig<!hw.array<2xi4>>
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convertSigStructExtract(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr)
|
|
||||||
func.func @convertSigStructExtract(%arg0: !llhd.sig<!hw.struct<foo: i1, bar: i2, baz: i3>>) {
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
|
|
||||||
// Adjust value pointer
|
|
||||||
// CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i3, i2, i1)>
|
|
||||||
|
|
||||||
// Create new signal struct on the stack.
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0]
|
|
||||||
// CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1]
|
|
||||||
// CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2]
|
|
||||||
// CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3]
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.store [[TMP5]], [[BUF]]
|
|
||||||
|
|
||||||
llhd.sig.struct_extract %arg0["bar"] : !llhd.sig<!hw.struct<foo: i1, bar: i2, baz: i3>>
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @lower_var(
|
|
||||||
// CHECK-SAME: %arg0: i1, %arg1: i32) {
|
|
||||||
func.func @lower_var(%arg0: i1, %arg1: i32) {
|
|
||||||
// CHECK: [[VAR0:%.+]] = llvm.alloca {{%.+}} x i1
|
|
||||||
// CHECK: llvm.store %arg0, [[VAR0]]
|
|
||||||
%0 = llhd.var %arg0 : i1
|
|
||||||
// CHECK: [[VAR1:%.+]] = llvm.alloca {{%.+}} x i32
|
|
||||||
// CHECK: llvm.store %arg1, [[VAR1]]
|
|
||||||
%1 = llhd.var %arg1 : i32
|
|
||||||
// CHECK: llvm.return
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @lower_load(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr) {
|
|
||||||
func.func @lower_load(%arg0: !llhd.ptr<i1>, %arg1: !llhd.ptr<i32>) {
|
|
||||||
// CHECK: llvm.load %arg0 : !llvm.ptr -> i1
|
|
||||||
%0 = llhd.load %arg0 : !llhd.ptr<i1>
|
|
||||||
// CHECK: llvm.load %arg1 : !llvm.ptr -> i32
|
|
||||||
%1 = llhd.load %arg1 : !llhd.ptr<i32>
|
|
||||||
// CHECK: llvm.return
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @lower_store(
|
|
||||||
// CHECK-SAME: %arg0: i1, %arg1: !llvm.ptr, %arg2: i32, %arg3: !llvm.ptr) {
|
|
||||||
func.func @lower_store(%arg0: i1, %arg1: !llhd.ptr<i1>, %arg2: i32, %arg3: !llhd.ptr<i32>) {
|
|
||||||
// CHECK: llvm.store %arg0, %arg1 : i1, !llvm.ptr
|
|
||||||
llhd.store %arg1, %arg0 : !llhd.ptr<i1>
|
|
||||||
// CHECK: llvm.store %arg2, %arg3 : i32, !llvm.ptr
|
|
||||||
llhd.store %arg3, %arg2 : !llhd.ptr<i32>
|
|
||||||
// CHECK: llvm.return
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK: @dummyA
|
|
||||||
// CHECK: @dummyB
|
|
||||||
func.func private @dummyA(%arg0: i42)
|
|
||||||
func.func private @dummyB(%arg0: !llhd.ptr<i42>)
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @PersistValuesAcrossPotentialResumePoints(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr)
|
|
||||||
llhd.proc @PersistValuesAcrossPotentialResumePoints () -> () {
|
|
||||||
// Values used across basic blocks get persisted directly
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.constant(1337 :
|
|
||||||
// CHECK: [[PERSIST_PTR1:%.+]] = llvm.getelementptr %arg1[0, 3, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)>
|
|
||||||
// CHECK: llvm.store [[TMP1]], [[PERSIST_PTR1]] : i42, !llvm.ptr
|
|
||||||
%0 = hw.constant 1337 : i42
|
|
||||||
|
|
||||||
// Variables used across basic blocks get persisted by loading their value
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.alloca {{%.+}} x i42
|
|
||||||
// CHECK: llvm.store [[TMP1]], [[TMP2]] : i42, !llvm.ptr
|
|
||||||
// CHECK: [[PERSIST_PTR2:%.+]] = llvm.getelementptr %arg1[0, 3, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)>
|
|
||||||
// CHECK: [[TMP3:%.+]] = llvm.load [[TMP2]] : !llvm.ptr -> i42
|
|
||||||
// CHECK: llvm.store [[TMP3]], [[PERSIST_PTR2]] : i42, !llvm.ptr
|
|
||||||
%1 = llhd.var %0 : i42
|
|
||||||
|
|
||||||
// CHECK: llvm.br [[BB:\^.+]]
|
|
||||||
cf.br ^resume
|
|
||||||
|
|
||||||
// CHECK: [[BB]]:
|
|
||||||
^resume:
|
|
||||||
// CHECK: [[PERSIST_PTR2:%.+]] = llvm.getelementptr %arg1[0, 3, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)>
|
|
||||||
// CHECK: [[PERSIST_PTR1:%.+]] = llvm.getelementptr %arg1[0, 3, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)>
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.load [[PERSIST_PTR1]] : !llvm.ptr -> i42
|
|
||||||
// CHECK: llvm.call @dummyA([[TMP1]])
|
|
||||||
// CHECK: llvm.call @dummyB([[PERSIST_PTR2]])
|
|
||||||
func.call @dummyA(%0) : (i42) -> ()
|
|
||||||
func.call @dummyB(%1) : (!llhd.ptr<i42>) -> ()
|
|
||||||
|
|
||||||
// CHECK: llvm.br [[BB]]
|
|
||||||
cf.br ^resume
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @driveSignal(!llvm.ptr, !llvm.ptr, !llvm.ptr, i64, i64, i64, i64)
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_sig(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
llhd.entity @convert_sig() -> () {
|
|
||||||
// Unused in entity definition. Only used at instantiation site.
|
|
||||||
%0 = hw.constant 0 : i1
|
|
||||||
%1 = hw.array_create %0, %0, %0, %0 : i1
|
|
||||||
|
|
||||||
// CHECK: llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
llhd.sig "sig0" %0 : i1
|
|
||||||
llhd.sig "sig1" %1 : !hw.array<4xi1>
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_prb(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
llhd.entity @convert_prb(%a: !llhd.sig<i1>, %b: !llhd.sig<!hw.array<3xi5>>) -> () {
|
|
||||||
// CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_A]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUEPTR_A:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_A]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[OFFSET_A:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[VALUE_A:%.+]] = llvm.load [[VALUEPTR_A]] : !llvm.ptr -> i16
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.trunc [[OFFSET_A]] : i64 to i16
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.lshr [[VALUE_A]], [[TMP1]] : i16
|
|
||||||
// CHECK: [[VALUE_A:%.+]] = llvm.trunc [[TMP2]] : i16 to i1
|
|
||||||
%0 = llhd.prb %a : !llhd.sig<i1>
|
|
||||||
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_B]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[VALUEPTR_B:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[VALUE_B:%.+]] = llvm.load [[VALUEPTR_B]] : !llvm.ptr -> !llvm.array<3 x i5>
|
|
||||||
%1 = llhd.prb %b : !llhd.sig<!hw.array<3xi5>>
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_drv(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
llhd.entity @convert_drv(%a: !llhd.sig<i1>, %b: !llhd.sig<!hw.array<3xi5>>) -> () {
|
|
||||||
// CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
|
|
||||||
// CHECK: [[C0_I1:%.+]] = llvm.mlir.constant(false) : i1
|
|
||||||
// CHECK: [[C0_I5:%.+]] = llvm.mlir.constant(0 : i5) : i5
|
|
||||||
%c0_i1 = hw.constant 0 : i1
|
|
||||||
%c0_i5 = hw.constant 0 : i5
|
|
||||||
|
|
||||||
// CHECK: [[ARRPTR:%.+]] = llvm.mlir.addressof {{@.+}} : !llvm.ptr
|
|
||||||
// CHECK: [[ARR:%.+]] = llvm.load [[ARRPTR]] : !llvm.ptr -> !llvm.array<3 x i5>
|
|
||||||
%0 = hw.array_create %c0_i5, %c0_i5, %c0_i5 : i5
|
|
||||||
|
|
||||||
// CHECK: [[DT:%.+]] = llvm.mlir.constant(dense<[1000, 0, 0]>
|
|
||||||
%1 = llhd.constant_time #llhd.time<1ns, 0d, 0e>
|
|
||||||
|
|
||||||
// CHECK: [[C1_I64:%.+]] = llvm.mlir.constant(1 : i64) : i64
|
|
||||||
// CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x i1
|
|
||||||
// CHECK: llvm.store [[C0_I1]], [[BUF]] : i1, !llvm.ptr
|
|
||||||
// CHECK: [[DTS:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DTD:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DTE:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: llvm.call @driveSignal(%arg0, [[SIGPTR_A]], [[BUF]], [[C1_I64]], [[DTS]], [[DTD]], [[DTE]])
|
|
||||||
llhd.drv %a, %c0_i1 after %1 : !llhd.sig<i1>
|
|
||||||
|
|
||||||
// CHECK: [[C8_I64:%.+]] = llvm.mlir.constant(8 : i64) : i64
|
|
||||||
// CHECK: [[TMP1:%.+]] = llvm.mlir.zero : !llvm.ptr
|
|
||||||
// CHECK: [[TMP2:%.+]] = llvm.getelementptr [[TMP1]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<3 x i5>
|
|
||||||
// CHECK: [[ARRBYTES:%.+]] = llvm.ptrtoint [[TMP2]] : !llvm.ptr to i64
|
|
||||||
// CHECK: [[ARRBITS:%.+]] = llvm.mul [[ARRBYTES]], [[C8_I64]]
|
|
||||||
|
|
||||||
// CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x !llvm.array<3 x i5>
|
|
||||||
// CHECK: llvm.store [[ARR]], [[BUF]] : !llvm.array<3 x i5>, !llvm.ptr
|
|
||||||
// CHECK: [[DTS:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DTD:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DTE:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: llvm.call @driveSignal(%arg0, [[SIGPTR_B]], [[BUF]], [[ARRBITS]], [[DTS]], [[DTD]], [[DTE]])
|
|
||||||
llhd.drv %b, %0 after %1 : !llhd.sig<!hw.array<3xi5>>
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_drv_enable(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
llhd.entity @convert_drv_enable(%a: !llhd.sig<i1>) -> () {
|
|
||||||
// Last piece of read logic.
|
|
||||||
// CHECK: [[VALUE_A:%.+]] = llvm.trunc {{%.+}} : i16 to i1
|
|
||||||
%0 = llhd.prb %a : !llhd.sig<i1>
|
|
||||||
%1 = llhd.constant_time #llhd.time<1ns, 0d, 0e>
|
|
||||||
|
|
||||||
// CHECK: [[C1_I1:%.+]] = llvm.mlir.constant(1 {{.*}}) : i1
|
|
||||||
// CHECK: [[ENABLE:%.+]] = llvm.icmp "eq" {{%.+}}, [[C1_I1]] : i1
|
|
||||||
// CHECK: llvm.cond_br [[ENABLE]], ^bb1, ^bb2
|
|
||||||
// CHECK: ^bb1:
|
|
||||||
// CHECK: llvm.call @driveSignal
|
|
||||||
// CHECK: llvm.br ^bb2
|
|
||||||
// CHECK: ^bb2:
|
|
||||||
llhd.drv %a, %0 after %1 if %0 : !llhd.sig<i1>
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix `llhd.reg` code generation and add test.
|
|
|
@ -1,49 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @driveSignal(!llvm.ptr, !llvm.ptr, !llvm.ptr, i64, i64, i64, i64)
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @Foo(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
|
|
||||||
llhd.entity @Foo () -> () {
|
|
||||||
// Unused in entity definition. Only used at instantiation site.
|
|
||||||
// CHECK: [[C0:%.+]] = llvm.mlir.constant(false) : i1
|
|
||||||
%0 = hw.constant 0 : i1
|
|
||||||
|
|
||||||
// CHECK: [[SIG_PTR:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
%toggle = llhd.sig "toggle" %0 : i1
|
|
||||||
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIG_PTR]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[SIG_VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIG_PTR]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[SIG_OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[SIG_VALUE:%.+]] = llvm.load [[SIG_VALUE_PTR]] : !llvm.ptr -> i16
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.trunc [[SIG_OFFSET]] : i64 to i16
|
|
||||||
// CHECK: [[SIG_VALUE_SHIFTED:%.+]] = llvm.lshr [[SIG_VALUE]], [[TMP]] : i16
|
|
||||||
// CHECK: [[SIG_VALUE:%.+]] = llvm.trunc [[SIG_VALUE_SHIFTED]] : i16 to i1
|
|
||||||
%1 = llhd.prb %toggle : !llhd.sig<i1>
|
|
||||||
|
|
||||||
// CHECK: [[DRV_VALUE:%.+]] = llvm.xor
|
|
||||||
%allset = hw.constant 1 : i1
|
|
||||||
%2 = comb.xor %1, %allset : i1
|
|
||||||
|
|
||||||
// CHECK: [[DT:%.+]] = llvm.mlir.constant(dense<[1000, 0, 0]>
|
|
||||||
%dt = llhd.constant_time #llhd.time<1ns, 0d, 0e>
|
|
||||||
|
|
||||||
// CHECK: [[C1_I64:%.+]] = llvm.mlir.constant(1 : i64) : i64
|
|
||||||
// CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32
|
|
||||||
// CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x i1
|
|
||||||
// CHECK: llvm.store [[DRV_VALUE]], [[BUF]] : i1, !llvm.ptr
|
|
||||||
// CHECK: [[DT_S:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DT_D:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: [[DT_E:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64>
|
|
||||||
// CHECK: llvm.call @driveSignal(%arg0, [[SIG_PTR]], [[BUF]], [[C1_I64]], [[DT_S]], [[DT_D]], [[DT_E]])
|
|
||||||
llhd.drv %toggle, %2 after %dt : !llhd.sig<i1>
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: @convertConstantTime
|
|
||||||
llvm.func @convertConstantTime() {
|
|
||||||
// CHECK-NEXT: {{%.+}} = llvm.mlir.constant(dense<[0, 1, 2]> : tensor<3xi64>) : !llvm.array<3 x i64>
|
|
||||||
%2 = llhd.constant_time #llhd.time<0ns, 1d, 2e>
|
|
||||||
// CHECK-NEXT: llvm.return
|
|
||||||
llvm.return
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @llhdSuspend(!llvm.ptr, !llvm.ptr, i64, i64, i64)
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_wait(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr)
|
|
||||||
llhd.proc @convert_wait(%a: !llhd.sig<i1>, %b: !llhd.sig<i1>) -> () {
|
|
||||||
// CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
// CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)>
|
|
||||||
|
|
||||||
// CHECK: [[RESUME_PTR:%.+]] = llvm.getelementptr %arg1[1] : (!llvm.ptr) -> !llvm.ptr, i32
|
|
||||||
// CHECK: [[RESUME:%.+]] = llvm.load [[RESUME_PTR]] : !llvm.ptr -> i32
|
|
||||||
// CHECK: llvm.br [[BB:\^.+]]
|
|
||||||
// CHECK: [[BB]]:
|
|
||||||
|
|
||||||
// Resume 1 (after wait)
|
|
||||||
// CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 :
|
|
||||||
// CHECK: [[EQ:%.+]] = llvm.icmp "eq" [[RESUME]], [[C1_I32]]
|
|
||||||
// CHECK: llvm.cond_br [[EQ]], [[BB_END:\^.+]], [[BB:\^.+]]
|
|
||||||
// CHECK: [[BB]]:
|
|
||||||
|
|
||||||
// Resume 0 (entry point)
|
|
||||||
// CHECK: [[C0_I32:%.+]] = llvm.mlir.constant(0 :
|
|
||||||
// CHECK: [[EQ:%.+]] = llvm.icmp "eq" [[RESUME]], [[C0_I32]]
|
|
||||||
// CHECK: llvm.cond_br [[EQ]], [[BB_ENTRY:\^.+]], {{\^.+}}
|
|
||||||
|
|
||||||
// CHECK: [[BB_ENTRY]]:
|
|
||||||
%0 = llhd.constant_time #llhd.time<1ns, 0d, 0e>
|
|
||||||
|
|
||||||
// Update resume index to 1 (after wait)
|
|
||||||
// CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 :
|
|
||||||
// CHECK: [[RESUME_PTR:%.+]] = llvm.getelementptr %arg1[1]
|
|
||||||
// CHECK: llvm.store [[C1_I32]], [[RESUME_PTR]]
|
|
||||||
|
|
||||||
// Clear sensitivity flags for all signals.
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[2]
|
|
||||||
// CHECK: [[SENSE_PTR:%.+]] = llvm.load [[TMP]]
|
|
||||||
// CHECK: [[FALSE:%.+]] = llvm.mlir.constant(false)
|
|
||||||
// CHECK: [[SENSE_A:%.+]] = llvm.getelementptr [[SENSE_PTR]][0]
|
|
||||||
// CHECK: llvm.store [[FALSE]], [[SENSE_A]] : i1, !llvm.ptr
|
|
||||||
// CHECK: [[SENSE_B:%.+]] = llvm.getelementptr [[SENSE_PTR]][1]
|
|
||||||
// CHECK: llvm.store [[FALSE]], [[SENSE_B]] : i1, !llvm.ptr
|
|
||||||
|
|
||||||
// Set sensitivity flag for signal "b" (index 1).
|
|
||||||
// CHECK: [[SIGIDX_PTR:%.+]] = llvm.getelementptr [[SIGPTR_B]][2]
|
|
||||||
// CHECK: [[SIGIDX:%.+]] = llvm.load [[SIGIDX_PTR]] : !llvm.ptr -> i64
|
|
||||||
// CHECK: [[TRUE:%.+]] = llvm.mlir.constant(true)
|
|
||||||
// CHECK: [[SENSE:%.+]] = llvm.getelementptr [[SENSE_PTR]][[[SIGIDX]]]
|
|
||||||
// CHECK: llvm.store [[TRUE]], [[SENSE]] : i1, !llvm.ptr
|
|
||||||
|
|
||||||
// CHECK: llvm.call @llhdSuspend(%arg0, %arg1, {{%.+}}, {{%.+}}, {{%.+}})
|
|
||||||
llhd.wait for %0, (%b : !llhd.sig<i1>), ^end
|
|
||||||
|
|
||||||
// CHECK: [[BB_END]]:
|
|
||||||
// CHECK: llvm.br [[BB_END]]
|
|
||||||
^end:
|
|
||||||
cf.br ^end
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @convert_halt(
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr)
|
|
||||||
llhd.proc @convert_halt() -> (%a: !llhd.sig<i1>, %b: !llhd.sig<i1>) {
|
|
||||||
// Clear sensitivity flags for all signals.
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[2]
|
|
||||||
// CHECK: [[SENSE_PTR:%.+]] = llvm.load [[TMP]]
|
|
||||||
// CHECK: [[FALSE:%.+]] = llvm.mlir.constant(false)
|
|
||||||
// CHECK: [[SENSE_A:%.+]] = llvm.getelementptr [[SENSE_PTR]][0]
|
|
||||||
// CHECK: llvm.store [[FALSE]], [[SENSE_A]] : i1, !llvm.ptr
|
|
||||||
// CHECK: [[SENSE_B:%.+]] = llvm.getelementptr [[SENSE_PTR]][1]
|
|
||||||
// CHECK: llvm.store [[FALSE]], [[SENSE_B]] : i1, !llvm.ptr
|
|
||||||
llhd.halt
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --verify-diagnostics --split-input-file
|
|
||||||
|
|
||||||
llhd.entity @root() -> () {
|
|
||||||
// expected-error @+1 {{failed to legalize operation 'llhd.inst'}}
|
|
||||||
llhd.inst "inst" @initUsesProbedValue () -> () : () -> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
llhd.entity @initUsesProbedValue () -> () {
|
|
||||||
%0 = hw.constant 0 : i1
|
|
||||||
%1 = llhd.sig "sig" %0 : i1
|
|
||||||
%2 = llhd.prb %1 : !llhd.sig<i1>
|
|
||||||
%3 = hw.array_create %2, %2 : i1
|
|
||||||
%4 = llhd.sig "sig1" %3 : !hw.array<2xi1>
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add testcase where the init value of llhd.sig comes from a block argument
|
|
|
@ -1,77 +0,0 @@
|
||||||
// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s
|
|
||||||
|
|
||||||
// CHECK-LABEL: llvm.func @llhd_init
|
|
||||||
// CHECK-SAME: %arg0: !llvm.ptr) {
|
|
||||||
llhd.entity @Root() -> () {
|
|
||||||
// CHECK: [[SIZE:%.+]] = llvm.ptrtoint
|
|
||||||
// CHECK: [[MEM:%.+]] = llvm.call @malloc([[SIZE]])
|
|
||||||
// CHECK: llvm.call @allocEntity(%arg0, [[OWNER:%.+]], [[MEM]])
|
|
||||||
|
|
||||||
// sig0
|
|
||||||
// CHECK: [[VALUE:%.+]] = llvm.mlir.constant(1337 : i42) : i42
|
|
||||||
// CHECK: llvm.store [[VALUE]], [[BUF:%.+]] : i42, !llvm.ptr
|
|
||||||
// CHECK: [[SIZE:%.+]] = llvm.mlir.constant(6 :
|
|
||||||
// CHECK: llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]])
|
|
||||||
|
|
||||||
// sig1
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<2 x i1>
|
|
||||||
// CHECK: [[SIZE:%.+]] = llvm.ptrtoint [[TMP]] : !llvm.ptr to i64
|
|
||||||
// CHECK: llvm.store {{%.+}}, [[BUF:%.+]] : !llvm.array<2 x i1>, !llvm.ptr
|
|
||||||
// CHECK: [[SIGID1:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]])
|
|
||||||
// sig1 layout details
|
|
||||||
// CHECK: [[ELEMENT_COUNT:%.+]] = llvm.mlir.constant(2 :
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<2 x i1>
|
|
||||||
// CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]]
|
|
||||||
// CHECK: llvm.call @addSigArrayElements(%arg0, [[SIGID1]], [[ELEMENT_SIZE]], [[ELEMENT_COUNT]])
|
|
||||||
|
|
||||||
// sig2
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)>
|
|
||||||
// CHECK: [[SIZE:%.+]] = llvm.ptrtoint [[TMP]] : !llvm.ptr to i64
|
|
||||||
// CHECK: llvm.store {{%.+}}, [[BUF:%.+]] : !llvm.struct<(i5, i1)>, !llvm.ptr
|
|
||||||
// CHECK: [[SIGID2:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]])
|
|
||||||
// sig2 layout details f2
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)>
|
|
||||||
// CHECK: [[ELEMENT_OFFSET:%.+]] = llvm.ptrtoint [[TMP]]
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, i5
|
|
||||||
// CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]]
|
|
||||||
// CHECK: llvm.call @addSigStructElement(%arg0, [[SIGID2]], [[ELEMENT_OFFSET]], [[ELEMENT_SIZE]])
|
|
||||||
// sig2 layout details f1
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)>
|
|
||||||
// CHECK: [[ELEMENT_OFFSET:%.+]] = llvm.ptrtoint [[TMP]]
|
|
||||||
// CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, i1
|
|
||||||
// CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]]
|
|
||||||
// CHECK: llvm.call @addSigStructElement(%arg0, [[SIGID2]], [[ELEMENT_OFFSET]], [[ELEMENT_SIZE]])
|
|
||||||
|
|
||||||
llhd.inst "inst0" @Signals () -> () : () -> ()
|
|
||||||
|
|
||||||
// CHECK: llvm.call @allocEntity(%arg0, [[OWNER:%.+]], {{%.+}})
|
|
||||||
|
|
||||||
// sig3
|
|
||||||
// CHECK-DAG: [[FALSE:%.+]] = llvm.mlir.constant(false)
|
|
||||||
// CHECK-DAG: [[TMP1:%.+]] = llvm.mlir.undef
|
|
||||||
// CHECK-DAG: [[TMP2:%.+]] = llvm.insertvalue [[FALSE]], [[TMP1]][0]
|
|
||||||
// CHECK-DAG: [[TMP3:%.+]] = llvm.insertvalue [[FALSE]], [[TMP2]][1]
|
|
||||||
// CHECK: llvm.store [[TMP3]], [[BUF:%.+]] : !llvm.array<2 x i1>, !llvm.ptr
|
|
||||||
// CHECK: [[SIGID3:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], {{%.+}})
|
|
||||||
|
|
||||||
llhd.inst "inst1" @PartiallyLowered () -> () : () -> ()
|
|
||||||
// llhd.inst "inst2" @MultipleResults () -> () : () -> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
llhd.entity @Signals () -> () {
|
|
||||||
%0 = hw.constant 1337 : i42
|
|
||||||
%1 = llhd.sig "sig0" %0 : i42
|
|
||||||
%2 = hw.aggregate_constant [0 : i1, 1 : i1] : !hw.array<2xi1>
|
|
||||||
%3 = llhd.sig "sig1" %2 : !hw.array<2xi1>
|
|
||||||
%4 = hw.aggregate_constant [0 : i1, 1 : i5] : !hw.struct<f1: i1, f2: i5>
|
|
||||||
%5 = llhd.sig "sig2" %4 : !hw.struct<f1: i1, f2: i5>
|
|
||||||
}
|
|
||||||
|
|
||||||
llhd.entity @PartiallyLowered () -> () {
|
|
||||||
%0 = llvm.mlir.constant(false) : i1
|
|
||||||
%1 = llvm.mlir.undef : !llvm.array<2 x i1>
|
|
||||||
%2 = llvm.insertvalue %0, %1[0] : !llvm.array<2 x i1>
|
|
||||||
%3 = llvm.insertvalue %0, %2[1] : !llvm.array<2 x i1>
|
|
||||||
%4 = builtin.unrealized_conversion_cast %3 : !llvm.array<2 x i1> to !hw.array<2xi1>
|
|
||||||
%5 = llhd.sig "sig3" %4 : !hw.array<2xi1>
|
|
||||||
}
|
|
|
@ -75,11 +75,6 @@ if config.zlib == "1":
|
||||||
if config.scheduling_or_tools != "":
|
if config.scheduling_or_tools != "":
|
||||||
config.available_features.add('or-tools')
|
config.available_features.add('or-tools')
|
||||||
|
|
||||||
# Add llhd-sim if it is built.
|
|
||||||
if config.llhd_sim_enabled:
|
|
||||||
config.available_features.add('llhd-sim')
|
|
||||||
tools.append('llhd-sim')
|
|
||||||
|
|
||||||
# Add circt-verilog if the Slang frontend is enabled.
|
# Add circt-verilog if the Slang frontend is enabled.
|
||||||
if config.slang_frontend_enabled:
|
if config.slang_frontend_enabled:
|
||||||
config.available_features.add('slang')
|
config.available_features.add('slang')
|
||||||
|
|
|
@ -38,7 +38,6 @@ config.circt_shlib_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@"
|
||||||
config.verilator_path = "@VERILATOR_PATH@"
|
config.verilator_path = "@VERILATOR_PATH@"
|
||||||
config.zlib = "@HAVE_ZLIB@"
|
config.zlib = "@HAVE_ZLIB@"
|
||||||
config.scheduling_or_tools = "@SCHEDULING_OR_TOOLS@"
|
config.scheduling_or_tools = "@SCHEDULING_OR_TOOLS@"
|
||||||
config.llhd_sim_enabled = @CIRCT_LLHD_SIM_ENABLED@
|
|
||||||
config.slang_frontend_enabled = @CIRCT_SLANG_FRONTEND_ENABLED@
|
config.slang_frontend_enabled = @CIRCT_SLANG_FRONTEND_ENABLED@
|
||||||
|
|
||||||
# Support substitution of the tools_dir with user parameters. This is
|
# Support substitution of the tools_dir with user parameters. This is
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
// REQUIRES: llhd-sim
|
|
||||||
// RUN: llhd-sim --help | FileCheck %s --implicit-check-not='{{[Oo]}}ptions:'
|
|
||||||
|
|
||||||
// CHECK: OVERVIEW: LLHD simulator
|
|
||||||
// CHECK: OPTIONS
|
|
||||||
// CHECK: Generic Options
|
|
||||||
// CHECK: llhd-sim Options
|
|
|
@ -12,7 +12,6 @@ add_subdirectory(firtool)
|
||||||
add_subdirectory(handshake-runner)
|
add_subdirectory(handshake-runner)
|
||||||
add_subdirectory(hlstool)
|
add_subdirectory(hlstool)
|
||||||
add_subdirectory(ibistool)
|
add_subdirectory(ibistool)
|
||||||
add_subdirectory(llhd-sim)
|
|
||||||
add_subdirectory(om-linker)
|
add_subdirectory(om-linker)
|
||||||
add_subdirectory(py-split-input-file)
|
add_subdirectory(py-split-input-file)
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,6 @@ target_link_libraries(circt-opt
|
||||||
CIRCTHandshakeTransforms
|
CIRCTHandshakeTransforms
|
||||||
CIRCTLECTransforms
|
CIRCTLECTransforms
|
||||||
CIRCTLLHD
|
CIRCTLLHD
|
||||||
CIRCTLLHDToLLVM
|
|
||||||
CIRCTHWToLLVM
|
CIRCTHWToLLVM
|
||||||
CIRCTCombToArith
|
CIRCTCombToArith
|
||||||
CIRCTCombToLLVM
|
CIRCTCombToLLVM
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
set(LIBS
|
|
||||||
CIRCTLLHD
|
|
||||||
CIRCTComb
|
|
||||||
CIRCTHW
|
|
||||||
CIRCTLLHDToLLVM
|
|
||||||
CIRCTLLHDSimEngine
|
|
||||||
)
|
|
||||||
|
|
||||||
# llhd-sim fails to link on Windows with MSVC.
|
|
||||||
IF(CIRCT_LLHD_SIM_ENABLED)
|
|
||||||
add_circt_tool(llhd-sim
|
|
||||||
llhd-sim.cpp)
|
|
||||||
|
|
||||||
llvm_update_compile_flags(llhd-sim)
|
|
||||||
target_link_libraries(llhd-sim PRIVATE ${LIBS})
|
|
||||||
endif()
|
|
|
@ -1,234 +0,0 @@
|
||||||
//===- llhd-sim.cpp - LLHD simulator tool -----------------------*- 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 implements a command line tool to run LLHD simulation.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "circt/Conversion/LLHDToLLVM.h"
|
|
||||||
#include "circt/Dialect/Comb/CombDialect.h"
|
|
||||||
#include "circt/Dialect/HW/HWDialect.h"
|
|
||||||
#include "circt/Dialect/LLHD/IR/LLHDDialect.h"
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/Engine.h"
|
|
||||||
#include "circt/Dialect/LLHD/Simulator/Trace.h"
|
|
||||||
#include "circt/Support/Version.h"
|
|
||||||
|
|
||||||
#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
|
|
||||||
#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
|
|
||||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
|
||||||
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
|
|
||||||
#include "mlir/ExecutionEngine/OptUtils.h"
|
|
||||||
#include "mlir/Parser/Parser.h"
|
|
||||||
#include "mlir/Pass/PassManager.h"
|
|
||||||
#include "mlir/Support/FileUtilities.h"
|
|
||||||
#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
|
|
||||||
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
|
|
||||||
#include "mlir/Target/LLVMIR/Export.h"
|
|
||||||
#include "mlir/Transforms/Passes.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/Error.h"
|
|
||||||
#include "llvm/Support/InitLLVM.h"
|
|
||||||
#include "llvm/Support/SourceMgr.h"
|
|
||||||
#include "llvm/Support/ToolOutputFile.h"
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
using namespace mlir;
|
|
||||||
using namespace mlir::func;
|
|
||||||
using namespace circt;
|
|
||||||
using namespace circt::llhd::sim;
|
|
||||||
|
|
||||||
static cl::OptionCategory mainCategory("llhd-sim Options");
|
|
||||||
|
|
||||||
static cl::opt<std::string> inputFilename(cl::Positional,
|
|
||||||
cl::desc("<input-file>"),
|
|
||||||
cl::init("-"), cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<std::string> outputFilename("o", cl::desc("Output filename"),
|
|
||||||
cl::value_desc("filename"),
|
|
||||||
cl::init("-"),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<int> nSteps("n", cl::desc("Set the maximum number of steps"),
|
|
||||||
cl::value_desc("max-steps"), cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<uint64_t> maxTime(
|
|
||||||
"T",
|
|
||||||
cl::desc("Stop the simulation after the given amount of simulation time in "
|
|
||||||
"picoseconds, including all sub-steps for that real-time step"),
|
|
||||||
cl::value_desc("max-time"), cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<bool>
|
|
||||||
dumpLLVMDialect("dump-llvm-dialect",
|
|
||||||
cl::desc("Dump the LLVM IR dialect module"),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<bool> dumpLLVMIR("dump-llvm-ir",
|
|
||||||
cl::desc("Dump the LLVM IR module"),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<bool> dumpMLIR("dump-mlir",
|
|
||||||
cl::desc("Dump the original MLIR module"),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<bool> dumpLayout("dump-layout",
|
|
||||||
cl::desc("Dump the gathered instance layout"),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<std::string> root(
|
|
||||||
"root",
|
|
||||||
cl::desc("Specify the name of the entity to use as root of the design"),
|
|
||||||
cl::value_desc("root_name"), cl::init("root"), cl::cat(mainCategory));
|
|
||||||
static cl::alias rootA("r", cl::desc("Alias for -root"), cl::aliasopt(root),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
enum OptLevel { O0, O1, O2, O3 };
|
|
||||||
|
|
||||||
static cl::opt<OptLevel>
|
|
||||||
optimizationLevel(cl::desc("Choose optimization level:"), cl::init(O2),
|
|
||||||
cl::values(clEnumVal(O0, "Run passes and codegen at O0"),
|
|
||||||
clEnumVal(O1, "Run passes and codegen at O1"),
|
|
||||||
clEnumVal(O2, "Run passes and codegen at O2"),
|
|
||||||
clEnumVal(O3, "Run passes and codegen at O3")),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::opt<TraceMode> traceMode(
|
|
||||||
"trace-format", cl::desc("Choose the dump format:"),
|
|
||||||
cl::init(TraceMode::Full),
|
|
||||||
cl::values(
|
|
||||||
clEnumValN(TraceMode::Full, "full",
|
|
||||||
"Dump signal changes for every time step and sub-step, "
|
|
||||||
"for all instances"),
|
|
||||||
clEnumValN(TraceMode::Reduced, "reduced",
|
|
||||||
"Dump signal changes for every time-step and "
|
|
||||||
"sub-step, only for the top-level instance"),
|
|
||||||
clEnumValN(TraceMode::Merged, "merged",
|
|
||||||
"Only dump changes for real-time steps, for all instances"),
|
|
||||||
clEnumValN(TraceMode::MergedReduce, "merged-reduce",
|
|
||||||
"Only dump changes for real-time steps, only for the "
|
|
||||||
"top-level instance"),
|
|
||||||
clEnumValN(
|
|
||||||
TraceMode::NamedOnly, "named-only",
|
|
||||||
"Only dump changes for real-time steps, only for top-level "
|
|
||||||
"instance and signals not having the default name '(sig)?[0-9]*'"),
|
|
||||||
clEnumValN(TraceMode::None, "none", "Don't dump a signal trace")),
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static cl::list<std::string>
|
|
||||||
sharedLibs("shared-libs",
|
|
||||||
cl::desc("Libraries to link dynamically. Specify absolute path "
|
|
||||||
"to llhd-signals-runtime-wrappers for GCC or Windows. "
|
|
||||||
"Optional otherwise."),
|
|
||||||
cl::ZeroOrMore, cl::MiscFlags::CommaSeparated,
|
|
||||||
cl::cat(mainCategory));
|
|
||||||
|
|
||||||
static int dumpLLVM(ModuleOp module, MLIRContext &context) {
|
|
||||||
if (dumpLLVMDialect) {
|
|
||||||
module.dump();
|
|
||||||
llvm::errs() << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate the module, that contains the LLVM dialect, to LLVM IR.
|
|
||||||
llvm::LLVMContext llvmContext;
|
|
||||||
auto llvmModule = mlir::translateModuleToLLVMIR(module, llvmContext);
|
|
||||||
if (!llvmModule) {
|
|
||||||
llvm::errs() << "Failed to emit LLVM IR\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto llvmTransformer =
|
|
||||||
makeOptimizingTransformer(optimizationLevel, 0, nullptr);
|
|
||||||
|
|
||||||
if (auto err = llvmTransformer(llvmModule.get())) {
|
|
||||||
llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::errs() << *llvmModule << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static LogicalResult applyMLIRPasses(ModuleOp module) {
|
|
||||||
PassManager pm(module.getContext());
|
|
||||||
|
|
||||||
pm.addPass(createConvertLLHDToLLVMPass());
|
|
||||||
pm.addPass(::mlir::createReconcileUnrealizedCastsPass());
|
|
||||||
|
|
||||||
return pm.run(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
InitLLVM y(argc, argv);
|
|
||||||
|
|
||||||
// Set the bug report message to indicate users should file issues on
|
|
||||||
// llvm/circt and not llvm/llvm-project.
|
|
||||||
setBugReportMsg(circtBugReportMsg);
|
|
||||||
|
|
||||||
// Hide default LLVM options, other than for this tool.
|
|
||||||
cl::HideUnrelatedOptions(mainCategory);
|
|
||||||
|
|
||||||
cl::ParseCommandLineOptions(argc, argv, "LLHD simulator\n");
|
|
||||||
|
|
||||||
// Set up the input and output files.
|
|
||||||
std::string errorMessage;
|
|
||||||
auto file = openInputFile(inputFilename, &errorMessage);
|
|
||||||
if (!file) {
|
|
||||||
llvm::errs() << errorMessage << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto output = openOutputFile(outputFilename, &errorMessage);
|
|
||||||
if (!output) {
|
|
||||||
llvm::errs() << errorMessage << "\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the input file.
|
|
||||||
SourceMgr mgr;
|
|
||||||
mgr.AddNewSourceBuffer(std::move(file), SMLoc());
|
|
||||||
|
|
||||||
MLIRContext context;
|
|
||||||
// Load the dialects
|
|
||||||
context
|
|
||||||
.loadDialect<llhd::LLHDDialect, LLVM::LLVMDialect, FuncDialect,
|
|
||||||
hw::HWDialect, comb::CombDialect, cf::ControlFlowDialect>();
|
|
||||||
mlir::registerLLVMDialectTranslation(context);
|
|
||||||
mlir::registerBuiltinDialectTranslation(context);
|
|
||||||
|
|
||||||
mlir::OwningOpRef<mlir::ModuleOp> module(
|
|
||||||
parseSourceFile<ModuleOp>(mgr, &context));
|
|
||||||
|
|
||||||
if (dumpMLIR) {
|
|
||||||
module->dump();
|
|
||||||
llvm::errs() << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SmallVector<StringRef, 1> sharedLibPaths(sharedLibs.begin(),
|
|
||||||
sharedLibs.end());
|
|
||||||
|
|
||||||
llhd::sim::Engine engine(
|
|
||||||
output->os(), *module, &applyMLIRPasses,
|
|
||||||
makeOptimizingTransformer(optimizationLevel, 0, nullptr), root, traceMode,
|
|
||||||
sharedLibPaths);
|
|
||||||
|
|
||||||
if (dumpLLVMDialect || dumpLLVMIR) {
|
|
||||||
return dumpLLVM(engine.getModule(), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dumpLayout) {
|
|
||||||
engine.dumpStateLayout();
|
|
||||||
engine.dumpStateSignalTriggers();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.simulate(nSteps, maxTime);
|
|
||||||
|
|
||||||
output->keep();
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue