[LLHD] Remove llhd-sim (#7351)

This commit is contained in:
Martin Erhart 2024-07-19 18:54:14 +02:00 committed by GitHub
parent dde9f4bc96
commit 2345382e67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 299 additions and 4657 deletions

View File

@ -448,24 +448,6 @@ else()
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)
#-------------------------------------------------------------------------------

View File

@ -32,7 +32,6 @@ tools/arcilator @fabianschuiki @maerhart
# LLHD
**/LLHD* @fabianschuiki @maerhart
tools/llhd-sim @fabianschuiki @maerhart
# Pipeline
**/Dialect/Pipeline @mortbopet

View File

@ -50,7 +50,7 @@ digraph G {
ESI [URL="https://circt.llvm.org/docs/Dialects/ESI/"]
FSM [URL="https://circt.llvm.org/docs/Dialects/FSM/"]
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
lower_to_sv_and_core [shape=point label="" fillcolor=black]
@ -79,7 +79,6 @@ digraph G {
subgraph backend_internal_tools{
node [fillcolor="#fdc086"]
Arcilator
llhd_sim [label="llhd-sim"]
ExportSystemC
ExportVerilog [URL="https://circt.llvm.org/docs/VerilogGeneration/"]
}
@ -88,7 +87,7 @@ digraph G {
// External tools
subgraph external_tools {
node [shape=octagon fillcolor="#ffff99"]
Moore
Slang
Calyx_native [label="Calyx native"]
}
@ -126,13 +125,14 @@ digraph G {
// Things that lower into a subset of the RTL-like dialects. Cluster these
// 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]
lower_to_sv_and_core -> SV
lower_to_sv_and_core -> Comb [lhead=cluster_RTL]
lower_to_core -> Comb [lhead=cluster_RTL]
Seq -> SV
{HW MooreMIR} -> LLHD
LLHD -> Arcilator
Interop -> Arc [ltail=cluster_RTL]
Comb -> SystemC [ltail=cluster_RTL]
ExportVerilog -> SVFile
@ -140,6 +140,7 @@ digraph G {
// Tool flows
Arc -> Arcilator
Arcilator -> SimBinary
Arcilator -> VCDTrace
Scheduling -> LoopSchedule [dir=both]
Scheduling -> Pipeline [dir=both]
Chisel -> FIRFile
@ -149,13 +150,10 @@ digraph G {
ExportSystemC -> SystemCFile
SV -> ExportVerilog
Seq -> ExportVerilog [ltail=cluster_RTL]
HW -> llhd_sim [ltail=cluster_RTL]
SVVHDL -> Moore [weight=999]
Moore -> MooreMIR
SVVHDL -> Slang [weight=999]
Slang -> Moore
Calyx -> Calyx_native
Calyx_native -> lower_to_sv_and_core [arrowhead=none]
LLHD -> llhd_sim
llhd_sim -> VCDTrace
ESI -> ServiceDesc
MSFT -> TCL
PyFile -> PyCDE
@ -170,7 +168,7 @@ digraph G {
// Leave one rank free above the RTL cluster to improve routing of incoming
// 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]
// 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

View File

@ -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 &regCounter);
/// Create an LLHD to LLVM conversion pass.
std::unique_ptr<OperationPass<ModuleOp>> createConvertLLHDToLLVMPass();
} // namespace circt
#endif // CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H

View File

@ -36,7 +36,6 @@
#include "circt/Conversion/HWToSystemC.h"
#include "circt/Conversion/HandshakeToDC.h"
#include "circt/Conversion/HandshakeToHW.h"
#include "circt/Conversion/LLHDToLLVM.h"
#include "circt/Conversion/LTLToCore.h"
#include "circt/Conversion/LoopScheduleToCalyx.h"
#include "circt/Conversion/MooreToCore.h"

View File

@ -519,22 +519,6 @@ def ConvertMooreToCore : Pass<"convert-moore-to-core", "mlir::ModuleOp"> {
"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
//===----------------------------------------------------------------------===//

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -25,7 +25,6 @@ add_circt_public_c_api_library(CIRCTCAPIConversion
CIRCTHWToSMT
CIRCTHWToSV
CIRCTHWToSystemC
CIRCTLLHDToLLVM
CIRCTLoopScheduleToCalyx
CIRCTLTLToCore
CIRCTMooreToCore

View File

@ -20,7 +20,6 @@ add_subdirectory(HWToLLVM)
add_subdirectory(HWToSMT)
add_subdirectory(HWToSV)
add_subdirectory(HWToSystemC)
add_subdirectory(LLHDToLLVM)
add_subdirectory(LoopScheduleToCalyx)
add_subdirectory(MooreToCore)
add_subdirectory(PipelineToHW)

View File

@ -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

View File

@ -1,5 +1,2 @@
add_subdirectory(IR)
if(CIRCT_LLHD_SIM_ENABLED)
add_subdirectory(Simulator)
endif()
add_subdirectory(Transforms)

View File

@ -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
)

View File

@ -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));
}
}
});
}

View File

@ -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";
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -38,11 +38,6 @@ if (CIRCT_GTEST_AVAILABLE)
list(APPEND CIRCT_TEST_DEPENDS CIRCTUnitTests)
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)
list(APPEND CIRCT_TEST_DEPENDS circt-verilog)
endif()

View File

@ -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>) {}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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>
}

View File

@ -75,11 +75,6 @@ if config.zlib == "1":
if config.scheduling_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.
if config.slang_frontend_enabled:
config.available_features.add('slang')

View File

@ -38,7 +38,6 @@ config.circt_shlib_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@"
config.verilator_path = "@VERILATOR_PATH@"
config.zlib = "@HAVE_ZLIB@"
config.scheduling_or_tools = "@SCHEDULING_OR_TOOLS@"
config.llhd_sim_enabled = @CIRCT_LLHD_SIM_ENABLED@
config.slang_frontend_enabled = @CIRCT_SLANG_FRONTEND_ENABLED@
# Support substitution of the tools_dir with user parameters. This is

View File

@ -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

View File

@ -12,7 +12,6 @@ add_subdirectory(firtool)
add_subdirectory(handshake-runner)
add_subdirectory(hlstool)
add_subdirectory(ibistool)
add_subdirectory(llhd-sim)
add_subdirectory(om-linker)
add_subdirectory(py-split-input-file)

View File

@ -46,7 +46,6 @@ target_link_libraries(circt-opt
CIRCTHandshakeTransforms
CIRCTLECTransforms
CIRCTLLHD
CIRCTLLHDToLLVM
CIRCTHWToLLVM
CIRCTCombToArith
CIRCTCombToLLVM

View File

@ -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()

View File

@ -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;
}