mirror of https://github.com/llvm/circt.git
315 lines
9.6 KiB
C++
315 lines
9.6 KiB
C++
//===- 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";
|
|
}
|