3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 17:15:33 +00:00

Merge pull request #3624 from jix/sim_yw

Changes to support SBY trace generation with the sim command
This commit is contained in:
Miodrag Milanović 2023-01-23 16:55:17 +01:00 committed by GitHub
commit 8180cc4325
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1722 additions and 113 deletions

View file

@ -259,5 +259,6 @@ X(WR_PORTS)
X(WR_PRIORITY_MASK)
X(WR_WIDE_CONTINUATION)
X(X)
X(xprop_decoder)
X(Y)
X(Y_WIDTH)

172
kernel/json.cc Normal file
View file

@ -0,0 +1,172 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/json.h"
USING_YOSYS_NAMESPACE
void PrettyJson::emit_to_log()
{
struct LogTarget : public Target {
void emit(const char *data) override { log("%s", data); }
};
targets.push_back(std::unique_ptr<Target>(new LogTarget));
}
void PrettyJson::append_to_string(std::string &target)
{
struct AppendStringTarget : public Target {
std::string &target;
AppendStringTarget(std::string &target) : target(target) {}
void emit(const char *data) override { target += data; }
};
targets.push_back(std::unique_ptr<Target>(new AppendStringTarget(target)));
}
bool PrettyJson::write_to_file(const std::string &path)
{
struct WriteFileTarget : public Target {
std::ofstream target;
void emit(const char *data) override { target << data; }
void flush() override { target.flush(); }
};
auto target = std::unique_ptr<WriteFileTarget>(new WriteFileTarget);
target->target.open(path);
if (target->target.fail())
return false;
targets.push_back(std::unique_ptr<Target>(target.release()));
return true;
}
void PrettyJson::line(bool space_if_inline)
{
if (compact_depth != INT_MAX) {
if (space_if_inline)
raw(" ");
return;
}
int indent = state.size() - (state.empty() ? 0 : state.back() == VALUE);
newline_indent.resize(1 + 2 * indent, ' ');
raw(newline_indent.c_str());
}
void PrettyJson::raw(const char *raw_json)
{
for (auto &target : targets)
target->emit(raw_json);
}
void PrettyJson::flush()
{
for (auto &target : targets)
target->flush();
}
void PrettyJson::begin_object()
{
begin_value();
raw("{");
state.push_back(OBJECT_FIRST);
}
void PrettyJson::begin_array()
{
begin_value();
raw("[");
state.push_back(ARRAY_FIRST);
}
void PrettyJson::end_object()
{
Scope top_scope = state.back();
state.pop_back();
if (top_scope == OBJECT)
line(false);
else
log_assert(top_scope == OBJECT_FIRST);
raw("}");
end_value();
}
void PrettyJson::end_array()
{
Scope top_scope = state.back();
state.pop_back();
if (top_scope == ARRAY)
line(false);
else
log_assert(top_scope == ARRAY_FIRST);
raw("]");
end_value();
}
void PrettyJson::name(const char *name)
{
if (state.back() == OBJECT_FIRST) {
state.back() = OBJECT;
line(false);
} else {
raw(",");
line();
}
raw(Json(name).dump().c_str());
raw(": ");
state.push_back(VALUE);
}
void PrettyJson::begin_value()
{
if (state.back() == ARRAY_FIRST) {
line(false);
state.back() = ARRAY;
} else if (state.back() == ARRAY) {
raw(",");
line();
} else {
log_assert(state.back() == VALUE);
state.pop_back();
}
}
void PrettyJson::end_value()
{
if (state.empty()) {
raw("\n");
flush();
}
if (GetSize(state) < compact_depth)
compact_depth = INT_MAX;
}
void PrettyJson::value_json(const Json &value)
{
begin_value();
raw(value.dump().c_str());
end_value();
}
void PrettyJson::entry_json(const char *name, const Json &value)
{
this->name(name);
this->value(value);
}

104
kernel/json.h Normal file
View file

@ -0,0 +1,104 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef JSON_H
#define JSON_H
#include "kernel/yosys.h"
#include "libs/json11/json11.hpp"
#include <functional>
YOSYS_NAMESPACE_BEGIN
using json11::Json;
class PrettyJson
{
enum Scope {
VALUE,
OBJECT_FIRST,
OBJECT,
ARRAY_FIRST,
ARRAY,
};
struct Target {
virtual void emit(const char *data) = 0;
virtual void flush() {};
virtual ~Target() {};
};
std::string newline_indent = "\n";
std::vector<std::unique_ptr<Target>> targets;
std::vector<Scope> state = {VALUE};
int compact_depth = INT_MAX;
public:
void emit_to_log();
void append_to_string(std::string &target);
bool write_to_file(const std::string &path);
bool active() { return !targets.empty(); }
void compact() { compact_depth = GetSize(state); }
void line(bool space_if_inline = true);
void raw(const char *raw_json);
void flush();
void begin_object();
void begin_array();
void end_object();
void end_array();
void name(const char *name);
void begin_value();
void end_value();
void value_json(const Json &value);
void value(unsigned int value) { value_json(Json((int)value)); }
template<typename T>
void value(T &&value) { value_json(Json(std::forward<T>(value))); };
void entry_json(const char *name, const Json &value);
void entry(const char *name, unsigned int value) { entry_json(name, Json((int)value)); }
template<typename T>
void entry(const char *name, T &&value) { entry_json(name, Json(std::forward<T>(value))); };
template<typename T>
void object(const T &&values)
{
begin_object();
for (auto &item : values)
entry(item.first, item.second);
end_object();
}
template<typename T>
void array(const T &&values)
{
begin_object();
for (auto &item : values)
value(item);
end_object();
}
};
YOSYS_NAMESPACE_END
#endif

209
kernel/yw.cc Normal file
View file

@ -0,0 +1,209 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yw.h"
#include "libs/json11/json11.hpp"
USING_YOSYS_NAMESPACE
// Use the same formatting as witness.py uses
static const char *pretty_name(IdString id)
{
const char *c_str = id.c_str();
const char *p = c_str;
if (*p != '\\')
return c_str;
p++;
if (*p == '[') {
p++;
while (*p >= '0' && *p <= '9')
p++;
if (p[0] != ']' || p[1] != 0)
return c_str;
return c_str + 1;
}
if (!(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && *p != '_')
return c_str;
p++;
while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_')
p++;
if (*p != 0)
return c_str;
return c_str + 1;
}
std::string IdPath::str() const
{
std::string result;
for (auto &item : *this) {
const char *pretty = pretty_name(item);
if (pretty[0] == '[') {
result += pretty;
continue;
}
if (!result.empty())
result += '.';
result += pretty;
if (pretty[0] == '\\' || pretty[0] == '$')
result += ' ';
}
return result;
}
bool IdPath::get_address(int &addr) const
{
if (empty())
return false;
auto &last = back();
if (!last.begins_with("\\["))
return false;
if (last == "\\[0]") {
addr = 0;
return true;
}
char first = last.c_str()[2];
if (first < '1' || first > '9')
return false;
char *endptr;
addr = std::strtol(last.c_str() + 2, &endptr, 10);
return endptr[0] == ']' && endptr[1] == 0;
}
static std::vector<IdString> get_path(const json11::Json &json)
{
std::vector<IdString> result;
for (auto &path_item : json.array_items()) {
auto const &path_item_str = path_item.string_value();
if (path_item_str.empty())
return {};;
result.push_back(path_item_str);
}
return result;
}
ReadWitness::ReadWitness(const std::string &filename) :
filename(filename)
{
std::ifstream f(filename.c_str());
if (f.fail() || GetSize(filename) == 0)
log_error("Cannot open file `%s`\n", filename.c_str());
std::stringstream buf;
buf << f.rdbuf();
std::string err;
json11::Json json = json11::Json::parse(buf.str(), err);
if (!err.empty())
log_error("Failed to parse `%s`: %s\n", filename.c_str(), err.c_str());
std::string format = json["format"].string_value();
if (format.empty())
log_error("Failed to parse `%s`: Unknown format\n", filename.c_str());
if (format != "Yosys Witness Trace")
log_error("Failed to parse `%s`: Unsupported format `%s`\n", filename.c_str(), format.c_str());
for (auto &clock_json : json["clocks"].array_items()) {
Clock clock;
clock.path = get_path(clock_json["path"]);
if (clock.path.empty())
log_error("Failed to parse `%s`: Missing path for clock `%s`\n", filename.c_str(), clock_json.dump().c_str());
auto edge_str = clock_json["edge"];
if (edge_str.string_value() == "posedge")
clock.is_posedge = true;
else if (edge_str.string_value() == "negedge")
clock.is_negedge = true;
else
log_error("Failed to parse `%s`: Unknown edge type for clock `%s`\n", filename.c_str(), clock_json.dump().c_str());
if (!clock_json["offset"].is_number())
log_error("Failed to parse `%s`: Unknown offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str());
clock.offset = clock_json["offset"].int_value();
if (clock.offset < 0)
log_error("Failed to parse `%s`: Invalid offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str());
clocks.push_back(clock);
}
int bits_offset = 0;
for (auto &signal_json : json["signals"].array_items()) {
Signal signal;
signal.bits_offset = bits_offset;
signal.path = get_path(signal_json["path"]);
if (signal.path.empty())
log_error("Failed to parse `%s`: Missing path for signal `%s`\n", filename.c_str(), signal_json.dump().c_str());
if (!signal_json["width"].is_number())
log_error("Failed to parse `%s`: Unknown width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str());
signal.width = signal_json["width"].int_value();
if (signal.width < 0)
log_error("Failed to parse `%s`: Invalid width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str());
bits_offset += signal.width;
if (!signal_json["offset"].is_number())
log_error("Failed to parse `%s`: Unknown offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str());
signal.offset = signal_json["offset"].int_value();
if (signal.offset < 0)
log_error("Failed to parse `%s`: Invalid offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str());
signal.init_only = signal_json["init_only"].bool_value();
signals.push_back(signal);
}
for (auto &step_json : json["steps"].array_items()) {
Step step;
if (!step_json["bits"].is_string())
log_error("Failed to parse `%s`: Expected string as bits value for step %d\n", filename.c_str(), GetSize(steps));
step.bits = step_json["bits"].string_value();
for (char c : step.bits) {
if (c != '0' && c != '1' && c != 'x' && c != '?')
log_error("Failed to parse `%s`: Invalid bit '%c' value for step %d\n", filename.c_str(), c, GetSize(steps));
}
steps.push_back(step);
}
}
RTLIL::Const ReadWitness::get_bits(int t, int bits_offset, int width) const
{
log_assert(t >= 0 && t < GetSize(steps));
const std::string &bits = steps[t].bits;
RTLIL::Const result(State::Sa, width);
result.bits.reserve(width);
int read_begin = GetSize(bits) - 1 - bits_offset;
int read_end = max(-1, read_begin - width);
min(width, GetSize(bits) - bits_offset);
for (int i = read_begin, j = 0; i > read_end; i--, j++) {
RTLIL::State bit = State::Sa;
switch (bits[i]) {
case '0': bit = State::S0; break;
case '1': bit = State::S1; break;
case 'x': bit = State::Sx; break;
case '?': bit = State::Sa; break;
default:
log_abort();
}
result.bits[j] = bit;
}
return result;
}

182
kernel/yw.h Normal file
View file

@ -0,0 +1,182 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef YW_H
#define YW_H
#include "kernel/yosys.h"
#include "kernel/mem.h"
YOSYS_NAMESPACE_BEGIN
struct IdPath : public std::vector<RTLIL::IdString>
{
template<typename... T>
IdPath(T&&... args) : std::vector<RTLIL::IdString>(std::forward<T>(args)...) { }
IdPath prefix() const { return {begin(), end() - !empty()}; }
std::string str() const;
bool has_address() const { int tmp; return get_address(tmp); };
bool get_address(int &addr) const;
int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); }
};
struct WitnessHierarchyItem {
RTLIL::Module *module;
RTLIL::Wire *wire = nullptr;
RTLIL::Cell *cell = nullptr;
Mem *mem = nullptr;
WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Wire *wire) : module(module), wire(wire) {}
WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) {}
WitnessHierarchyItem(RTLIL::Module *module, Mem *mem) : module(module), mem(mem) {}
};
template<typename D, typename T>
void witness_hierarchy(RTLIL::Module *module, D data, T callback);
template<class T> static std::vector<std::string> witness_path(T *obj) {
std::vector<std::string> path;
if (obj->name.isPublic()) {
auto hdlname = obj->get_string_attribute(ID::hdlname);
for (auto token : split_tokens(hdlname))
path.push_back("\\" + token);
}
if (path.empty())
path.push_back(obj->name.str());
return path;
}
struct ReadWitness
{
struct Clock {
IdPath path;
int offset;
bool is_posedge = false;
bool is_negedge = false;
};
struct Signal {
IdPath path;
int offset;
int width;
bool init_only;
int bits_offset;
};
struct Step {
std::string bits;
};
std::string filename;
std::vector<Clock> clocks;
std::vector<Signal> signals;
std::vector<Step> steps;
ReadWitness(const std::string &filename);
RTLIL::Const get_bits(int t, int bits_offset, int width) const;
};
template<typename D, typename T>
void witness_hierarchy_recursion(IdPath &path, int hdlname_mode, RTLIL::Module *module, D data, T &callback)
{
auto const &const_path = path;
size_t path_size = path.size();
for (auto wire : module->wires())
{
auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : wire->get_hdlname_attribute();
for (auto item : hdlname)
path.push_back("\\" + item);
if (hdlname.size() == 1 && path.back() == wire->name)
hdlname.clear();
if (!hdlname.empty())
callback(const_path, WitnessHierarchyItem(module, wire), data);
path.resize(path_size);
if (hdlname.empty() || hdlname_mode <= 0) {
path.push_back(wire->name);
callback(const_path, WitnessHierarchyItem(module, wire), data);
path.pop_back();
}
}
for (auto cell : module->cells())
{
Module *child = module->design->module(cell->type);
if (child == nullptr)
continue;
auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : cell->get_hdlname_attribute();
for (auto item : hdlname)
path.push_back("\\" + item);
if (hdlname.size() == 1 && path.back() == cell->name)
hdlname.clear();
if (!hdlname.empty()) {
D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data);
witness_hierarchy_recursion<D, T>(path, 1, child, child_data, callback);
}
path.resize(path_size);
if (hdlname.empty() || hdlname_mode <= 0) {
path.push_back(cell->name);
D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data);
witness_hierarchy_recursion<D, T>(path, hdlname.empty() ? hdlname_mode : -1, child, child_data, callback);
path.pop_back();
}
}
for (auto mem : Mem::get_all_memories(module)) {
std::vector<std::string> hdlname;
if (hdlname_mode >= 0 && mem.cell != nullptr)
hdlname = mem.cell->get_hdlname_attribute();
for (auto item : hdlname)
path.push_back("\\" + item);
if (hdlname.size() == 1 && path.back() == mem.cell->name)
hdlname.clear();
if (!hdlname.empty()) {
callback(const_path, WitnessHierarchyItem(module, &mem), data);
}
path.resize(path_size);
if (hdlname.empty() || hdlname_mode <= 0) {
path.push_back(mem.memid);
callback(const_path, WitnessHierarchyItem(module, &mem), data);
path.pop_back();
if (mem.cell != nullptr && mem.cell->name != mem.memid) {
path.push_back(mem.cell->name);
callback(const_path, WitnessHierarchyItem(module, &mem), data);
path.pop_back();
}
}
}
}
template<typename D, typename T>
void witness_hierarchy(RTLIL::Module *module, D data, T callback)
{
IdPath path;
witness_hierarchy_recursion<D, T>(path, 0, module, data, callback);
}
YOSYS_NAMESPACE_END
#endif