2017-08-16 06:05:21 -05:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
2021-06-07 17:39:36 -05:00
|
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
2017-08-16 06:05:21 -05:00
|
|
|
*
|
|
|
|
* 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/yosys.h"
|
|
|
|
#include "kernel/sigtools.h"
|
|
|
|
#include "kernel/celltypes.h"
|
2020-10-17 08:49:36 -05:00
|
|
|
#include "kernel/mem.h"
|
2022-01-26 08:50:38 -06:00
|
|
|
#include "kernel/fstdata.h"
|
2022-02-15 02:30:42 -06:00
|
|
|
#include "kernel/ff.h"
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2020-10-16 11:46:59 -05:00
|
|
|
#include <ctime>
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
|
2022-01-28 03:18:02 -06:00
|
|
|
enum class SimulationMode {
|
2022-02-02 02:37:32 -06:00
|
|
|
sim,
|
2022-01-28 03:18:02 -06:00
|
|
|
cmp,
|
|
|
|
gold,
|
|
|
|
gate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const std::map<std::string, int> g_units =
|
|
|
|
{
|
|
|
|
{ "", -9 }, // default is ns
|
|
|
|
{ "s", 0 },
|
|
|
|
{ "ms", -3 },
|
|
|
|
{ "us", -6 },
|
|
|
|
{ "ns", -9 },
|
|
|
|
{ "ps", -12 },
|
|
|
|
{ "fs", -15 },
|
|
|
|
{ "as", -18 },
|
|
|
|
{ "zs", -21 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static double stringToTime(std::string str)
|
|
|
|
{
|
|
|
|
if (str=="END") return -1;
|
|
|
|
|
|
|
|
char *endptr;
|
|
|
|
long value = strtol(str.c_str(), &endptr, 10);
|
|
|
|
|
|
|
|
if (g_units.find(endptr)==g_units.end())
|
|
|
|
log_error("Cannot parse '%s', bad unit '%s'\n", str.c_str(), endptr);
|
|
|
|
|
|
|
|
if (value < 0)
|
|
|
|
log_error("Time value '%s' must be positive\n", str.c_str());
|
|
|
|
|
|
|
|
return value * pow(10.0, g_units.at(endptr));
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
struct SimWorker;
|
|
|
|
struct OutputWriter
|
|
|
|
{
|
|
|
|
OutputWriter(SimWorker *w) { worker = w;};
|
|
|
|
virtual ~OutputWriter() {};
|
|
|
|
virtual void write_header() = 0;
|
|
|
|
virtual void write_step_header(int t) = 0;
|
2022-03-02 02:39:22 -06:00
|
|
|
virtual void enter_scope(IdString) {};
|
|
|
|
virtual void exit_scope() {};
|
|
|
|
virtual void register_signal(Wire *, int) {};
|
|
|
|
virtual void write_value(int, Const&) {};
|
2022-02-28 11:22:39 -06:00
|
|
|
SimWorker *worker;
|
|
|
|
};
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
struct SimShared
|
|
|
|
{
|
|
|
|
bool debug = false;
|
|
|
|
bool hide_internal = true;
|
2017-08-17 08:54:51 -05:00
|
|
|
bool writeback = false;
|
2017-08-18 05:54:17 -05:00
|
|
|
bool zinit = false;
|
|
|
|
int rstlen = 1;
|
2022-01-26 08:50:38 -06:00
|
|
|
FstData *fst = nullptr;
|
2022-01-28 03:18:02 -06:00
|
|
|
double start_time = 0;
|
|
|
|
double stop_time = -1;
|
2022-02-02 02:37:32 -06:00
|
|
|
SimulationMode sim_mode = SimulationMode::sim;
|
2022-01-31 02:38:25 -06:00
|
|
|
bool cycles_set = false;
|
2022-02-28 11:22:39 -06:00
|
|
|
std::vector<std::unique_ptr<OutputWriter>> outputfiles;
|
2017-08-17 05:27:08 -05:00
|
|
|
};
|
|
|
|
|
2017-08-18 05:54:17 -05:00
|
|
|
void zinit(State &v)
|
|
|
|
{
|
|
|
|
if (v != State::S1)
|
|
|
|
v = State::S0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zinit(Const &v)
|
|
|
|
{
|
|
|
|
for (auto &bit : v.bits)
|
|
|
|
zinit(bit);
|
|
|
|
}
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
struct SimInstance
|
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
SimShared *shared;
|
2022-01-26 08:50:38 -06:00
|
|
|
|
|
|
|
std::string scope;
|
2017-08-16 06:05:21 -05:00
|
|
|
Module *module;
|
|
|
|
Cell *instance;
|
|
|
|
|
|
|
|
SimInstance *parent;
|
|
|
|
dict<Cell*, SimInstance*> children;
|
|
|
|
|
|
|
|
SigMap sigmap;
|
|
|
|
dict<SigBit, State> state_nets;
|
|
|
|
dict<SigBit, pool<Cell*>> upd_cells;
|
|
|
|
dict<SigBit, pool<Wire*>> upd_outports;
|
|
|
|
|
|
|
|
pool<SigBit> dirty_bits;
|
2017-08-18 04:44:50 -05:00
|
|
|
pool<Cell*> dirty_cells;
|
2020-10-17 08:49:36 -05:00
|
|
|
pool<IdString> dirty_memories;
|
2017-08-17 05:27:08 -05:00
|
|
|
pool<SimInstance*, hash_ptr_ops> dirty_children;
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
struct ff_state_t
|
|
|
|
{
|
|
|
|
Const past_d;
|
2022-02-15 02:30:42 -06:00
|
|
|
Const past_ad;
|
2022-02-17 10:18:36 -06:00
|
|
|
State past_clk;
|
|
|
|
State past_ce;
|
|
|
|
State past_srst;
|
2022-02-15 02:30:42 -06:00
|
|
|
|
|
|
|
FfData data;
|
2017-08-17 05:27:08 -05:00
|
|
|
};
|
|
|
|
|
2017-08-18 03:24:14 -05:00
|
|
|
struct mem_state_t
|
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
Mem *mem;
|
|
|
|
std::vector<Const> past_wr_clk;
|
|
|
|
std::vector<Const> past_wr_en;
|
|
|
|
std::vector<Const> past_wr_addr;
|
|
|
|
std::vector<Const> past_wr_data;
|
2017-08-18 03:24:14 -05:00
|
|
|
Const data;
|
|
|
|
};
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
dict<Cell*, ff_state_t> ff_database;
|
2020-10-17 08:49:36 -05:00
|
|
|
dict<IdString, mem_state_t> mem_database;
|
2017-08-18 03:24:14 -05:00
|
|
|
pool<Cell*> formal_database;
|
2020-10-17 08:49:36 -05:00
|
|
|
dict<Cell*, IdString> mem_cells;
|
|
|
|
|
|
|
|
std::vector<Mem> memories;
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
dict<Wire*, pair<int, Const>> signal_database;
|
2022-01-28 06:24:38 -06:00
|
|
|
dict<Wire*, fstHandle> fst_handles;
|
2017-08-17 05:27:08 -05:00
|
|
|
|
2022-01-26 08:50:38 -06:00
|
|
|
SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
|
|
|
|
shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module)
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2019-06-05 16:16:24 -05:00
|
|
|
log_assert(module);
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
if (parent) {
|
|
|
|
log_assert(parent->children.count(instance) == 0);
|
|
|
|
parent->children[instance] = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto wire : module->wires())
|
|
|
|
{
|
|
|
|
SigSpec sig = sigmap(wire);
|
|
|
|
|
|
|
|
for (int i = 0; i < GetSize(sig); i++) {
|
|
|
|
if (state_nets.count(sig[i]) == 0)
|
|
|
|
state_nets[sig[i]] = State::Sx;
|
|
|
|
if (wire->port_output) {
|
|
|
|
upd_outports[sig[i]].insert(wire);
|
|
|
|
dirty_bits.insert(sig[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 03:15:22 -06:00
|
|
|
if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) {
|
2022-01-28 06:24:38 -06:00
|
|
|
fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
|
2022-01-28 07:20:16 -06:00
|
|
|
if (id==0 && wire->name.isPublic())
|
|
|
|
log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name)).c_str());
|
2022-01-28 06:24:38 -06:00
|
|
|
fst_handles[wire] = id;
|
|
|
|
}
|
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
if (wire->attributes.count(ID::init)) {
|
|
|
|
Const initval = wire->attributes.at(ID::init);
|
2017-08-16 06:05:21 -05:00
|
|
|
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
|
|
|
|
if (initval[i] == State::S0 || initval[i] == State::S1) {
|
|
|
|
state_nets[sig[i]] = initval[i];
|
|
|
|
dirty_bits.insert(sig[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
memories = Mem::get_all_memories(module);
|
|
|
|
for (auto &mem : memories) {
|
|
|
|
auto &mdb = mem_database[mem.memid];
|
|
|
|
mdb.mem = &mem;
|
|
|
|
for (auto &port : mem.wr_ports) {
|
|
|
|
mdb.past_wr_clk.push_back(Const(State::Sx));
|
|
|
|
mdb.past_wr_en.push_back(Const(State::Sx, GetSize(port.en)));
|
|
|
|
mdb.past_wr_addr.push_back(Const(State::Sx, GetSize(port.addr)));
|
|
|
|
mdb.past_wr_data.push_back(Const(State::Sx, GetSize(port.data)));
|
|
|
|
}
|
|
|
|
mdb.data = mem.get_init_data();
|
|
|
|
}
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
for (auto cell : module->cells())
|
|
|
|
{
|
|
|
|
Module *mod = module->design->module(cell->type);
|
|
|
|
|
|
|
|
if (mod != nullptr) {
|
2022-01-31 01:56:29 -06:00
|
|
|
dirty_children.insert(new SimInstance(shared, scope + "." + RTLIL::unescape_id(cell->name), mod, cell, this));
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &port : cell->connections()) {
|
|
|
|
if (cell->input(port.first))
|
2020-04-21 02:58:52 -05:00
|
|
|
for (auto bit : sigmap(port.second)) {
|
2017-08-16 06:05:21 -05:00
|
|
|
upd_cells[bit].insert(cell);
|
2020-04-21 02:58:52 -05:00
|
|
|
// Make sure cell inputs connected to constants are updated in the first cycle
|
|
|
|
if (bit.wire == nullptr)
|
|
|
|
dirty_bits.insert(bit);
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
|
2022-02-17 10:18:36 -06:00
|
|
|
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
|
2022-02-15 02:30:42 -06:00
|
|
|
FfData ff_data(nullptr, cell);
|
2017-08-17 05:27:08 -05:00
|
|
|
ff_state_t ff;
|
2022-02-15 02:30:42 -06:00
|
|
|
ff.past_d = Const(State::Sx, ff_data.width);
|
|
|
|
ff.past_ad = Const(State::Sx, ff_data.width);
|
|
|
|
ff.past_clk = State::Sx;
|
|
|
|
ff.past_ce = State::Sx;
|
|
|
|
ff.past_srst = State::Sx;
|
|
|
|
ff.data = ff_data;
|
2017-08-17 05:27:08 -05:00
|
|
|
ff_database[cell] = ff;
|
|
|
|
}
|
2017-08-18 03:24:14 -05:00
|
|
|
|
2021-05-22 12:14:13 -05:00
|
|
|
if (cell->is_mem_cell())
|
2020-06-29 03:33:39 -05:00
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
mem_cells[cell] = cell->parameters.at(ID::MEMID).decode_string();
|
2020-06-29 03:33:39 -05:00
|
|
|
}
|
2020-04-02 11:51:32 -05:00
|
|
|
if (cell->type.in(ID($assert), ID($cover), ID($assume))) {
|
2017-08-18 03:24:14 -05:00
|
|
|
formal_database.insert(cell);
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
2017-08-18 05:54:17 -05:00
|
|
|
|
|
|
|
if (shared->zinit)
|
|
|
|
{
|
|
|
|
for (auto &it : ff_database)
|
|
|
|
{
|
|
|
|
ff_state_t &ff = it.second;
|
|
|
|
zinit(ff.past_d);
|
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
SigSpec qsig = it.second.data.sig_q;
|
2017-08-18 05:54:17 -05:00
|
|
|
Const qdata = get_state(qsig);
|
|
|
|
zinit(qdata);
|
|
|
|
set_state(qsig, qdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &it : mem_database) {
|
|
|
|
mem_state_t &mem = it.second;
|
2020-10-17 08:49:36 -05:00
|
|
|
for (auto &val : mem.past_wr_en)
|
|
|
|
zinit(val);
|
2017-08-18 05:54:17 -05:00
|
|
|
zinit(mem.data);
|
|
|
|
}
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
~SimInstance()
|
|
|
|
{
|
|
|
|
for (auto child : children)
|
|
|
|
delete child.second;
|
|
|
|
}
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
IdString name() const
|
|
|
|
{
|
|
|
|
if (instance != nullptr)
|
|
|
|
return instance->name;
|
|
|
|
return module->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string hiername() const
|
|
|
|
{
|
|
|
|
if (instance != nullptr)
|
|
|
|
return parent->hiername() + "." + log_id(instance->name);
|
|
|
|
|
|
|
|
return log_id(module->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
Const get_state(SigSpec sig)
|
|
|
|
{
|
|
|
|
Const value;
|
|
|
|
|
|
|
|
for (auto bit : sigmap(sig))
|
2017-08-17 05:27:08 -05:00
|
|
|
if (bit.wire == nullptr)
|
|
|
|
value.bits.push_back(bit.data);
|
|
|
|
else if (state_nets.count(bit))
|
2017-08-16 06:05:21 -05:00
|
|
|
value.bits.push_back(state_nets.at(bit));
|
|
|
|
else
|
|
|
|
value.bits.push_back(State::Sz);
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
if (shared->debug)
|
|
|
|
log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
|
2017-08-16 06:05:21 -05:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
bool set_state(SigSpec sig, Const value)
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
bool did_something = false;
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
sig = sigmap(sig);
|
2019-12-17 10:32:48 -06:00
|
|
|
log_assert(GetSize(sig) <= GetSize(value));
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
for (int i = 0; i < GetSize(sig); i++)
|
|
|
|
if (state_nets.at(sig[i]) != value[i]) {
|
|
|
|
state_nets.at(sig[i]) = value[i];
|
|
|
|
dirty_bits.insert(sig[i]);
|
2017-08-17 05:27:08 -05:00
|
|
|
did_something = true;
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
if (shared->debug)
|
|
|
|
log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
|
|
|
|
return did_something;
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void update_cell(Cell *cell)
|
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
if (ff_database.count(cell))
|
|
|
|
return;
|
|
|
|
|
2017-08-18 03:24:14 -05:00
|
|
|
if (formal_database.count(cell))
|
|
|
|
return;
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
if (mem_cells.count(cell))
|
2017-08-18 04:44:50 -05:00
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
dirty_memories.insert(mem_cells[cell]);
|
2017-08-18 04:44:50 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
if (children.count(cell))
|
|
|
|
{
|
|
|
|
auto child = children.at(cell);
|
|
|
|
for (auto &conn: cell->connections())
|
2021-03-01 13:01:39 -06:00
|
|
|
if (cell->input(conn.first) && GetSize(conn.second)) {
|
2017-08-16 06:05:21 -05:00
|
|
|
Const value = get_state(conn.second);
|
|
|
|
child->set_state(child->module->wire(conn.first), value);
|
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
dirty_children.insert(child);
|
2017-08-16 06:05:21 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (yosys_celltypes.cell_evaluable(cell->type))
|
|
|
|
{
|
|
|
|
RTLIL::SigSpec sig_a, sig_b, sig_c, sig_d, sig_s, sig_y;
|
|
|
|
bool has_a, has_b, has_c, has_d, has_s, has_y;
|
|
|
|
|
2020-03-12 14:57:01 -05:00
|
|
|
has_a = cell->hasPort(ID::A);
|
|
|
|
has_b = cell->hasPort(ID::B);
|
2020-04-02 11:51:32 -05:00
|
|
|
has_c = cell->hasPort(ID::C);
|
|
|
|
has_d = cell->hasPort(ID::D);
|
2020-03-12 14:57:01 -05:00
|
|
|
has_s = cell->hasPort(ID::S);
|
|
|
|
has_y = cell->hasPort(ID::Y);
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2020-03-12 14:57:01 -05:00
|
|
|
if (has_a) sig_a = cell->getPort(ID::A);
|
|
|
|
if (has_b) sig_b = cell->getPort(ID::B);
|
2020-04-02 11:51:32 -05:00
|
|
|
if (has_c) sig_c = cell->getPort(ID::C);
|
|
|
|
if (has_d) sig_d = cell->getPort(ID::D);
|
2020-03-12 14:57:01 -05:00
|
|
|
if (has_s) sig_s = cell->getPort(ID::S);
|
|
|
|
if (has_y) sig_y = cell->getPort(ID::Y);
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
if (shared->debug)
|
|
|
|
log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type));
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
// Simple (A -> Y) and (A,B -> Y) cells
|
|
|
|
if (has_a && !has_c && !has_d && !has_s && has_y) {
|
|
|
|
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (A,B,C -> Y) cells
|
|
|
|
if (has_a && has_b && has_c && !has_d && !has_s && has_y) {
|
|
|
|
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-24 09:02:29 -06:00
|
|
|
// (A,S -> Y) cells
|
|
|
|
if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
|
|
|
|
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
// (A,B,S -> Y) cells
|
|
|
|
if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
|
|
|
|
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-18 03:24:14 -05:00
|
|
|
log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
void update_memory(IdString id) {
|
|
|
|
auto &mdb = mem_database[id];
|
|
|
|
auto &mem = *mdb.mem;
|
|
|
|
|
|
|
|
for (int port_idx = 0; port_idx < GetSize(mem.rd_ports); port_idx++)
|
|
|
|
{
|
|
|
|
auto &port = mem.rd_ports[port_idx];
|
|
|
|
Const addr = get_state(port.addr);
|
2021-05-24 18:12:19 -05:00
|
|
|
Const data = Const(State::Sx, mem.width << port.wide_log2);
|
2020-10-17 08:49:36 -05:00
|
|
|
|
|
|
|
if (port.clk_enable)
|
|
|
|
log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(mem.memid));
|
|
|
|
|
|
|
|
if (addr.is_fully_def()) {
|
|
|
|
int index = addr.as_int() - mem.start_offset;
|
|
|
|
if (index >= 0 && index < mem.size)
|
2021-05-24 18:12:19 -05:00
|
|
|
data = mdb.data.extract(index*mem.width, mem.width << port.wide_log2);
|
2020-10-17 08:49:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
set_state(port.data, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
void update_ph1()
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
pool<Cell*> queue_cells;
|
|
|
|
pool<Wire*> queue_outports;
|
|
|
|
|
2017-08-18 04:44:50 -05:00
|
|
|
queue_cells.swap(dirty_cells);
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
while (1)
|
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
for (auto bit : dirty_bits)
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
|
|
|
if (upd_cells.count(bit))
|
|
|
|
for (auto cell : upd_cells.at(bit))
|
2017-08-17 05:27:08 -05:00
|
|
|
queue_cells.insert(cell);
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
if (upd_outports.count(bit) && parent != nullptr)
|
|
|
|
for (auto wire : upd_outports.at(bit))
|
2017-08-17 05:27:08 -05:00
|
|
|
queue_outports.insert(wire);
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
dirty_bits.clear();
|
|
|
|
|
|
|
|
if (!queue_cells.empty())
|
|
|
|
{
|
|
|
|
for (auto cell : queue_cells)
|
|
|
|
update_cell(cell);
|
|
|
|
|
|
|
|
queue_cells.clear();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
for (auto &memid : dirty_memories)
|
|
|
|
update_memory(memid);
|
|
|
|
dirty_memories.clear();
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
for (auto wire : queue_outports)
|
|
|
|
if (instance->hasPort(wire->name)) {
|
|
|
|
Const value = get_state(wire);
|
|
|
|
parent->set_state(instance->getPort(wire->name), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
queue_outports.clear();
|
|
|
|
|
|
|
|
for (auto child : dirty_children)
|
|
|
|
child->update_ph1();
|
|
|
|
|
|
|
|
dirty_children.clear();
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
if (dirty_bits.empty())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
bool update_ph2()
|
|
|
|
{
|
|
|
|
bool did_something = false;
|
|
|
|
|
|
|
|
for (auto &it : ff_database)
|
|
|
|
{
|
|
|
|
ff_state_t &ff = it.second;
|
2022-02-17 10:18:36 -06:00
|
|
|
FfData &ff_data = ff.data;
|
2022-02-15 02:30:42 -06:00
|
|
|
|
2022-02-18 02:17:36 -06:00
|
|
|
Const current_q = get_state(ff.data.sig_q);
|
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
if (ff_data.has_clk) {
|
|
|
|
// flip-flops
|
|
|
|
State current_clk = get_state(ff_data.sig_clk)[0];
|
2022-02-18 02:17:36 -06:00
|
|
|
if (ff_data.pol_clk ? (ff.past_clk == State::S0 && current_clk != State::S0) :
|
|
|
|
(ff.past_clk == State::S1 && current_clk != State::S1)) {
|
|
|
|
bool ce = ff.past_ce == (ff_data.pol_ce ? State::S1 : State::S0);
|
|
|
|
// set if no ce, or ce is enabled
|
|
|
|
if (!ff_data.has_ce || (ff_data.has_ce && ce)) {
|
|
|
|
current_q = ff.past_d;
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
2022-02-18 02:17:36 -06:00
|
|
|
// override if sync reset
|
2022-02-21 09:36:12 -06:00
|
|
|
if ((ff_data.has_srst) && (ff.past_srst == (ff_data.pol_srst ? State::S1 : State::S0)) &&
|
|
|
|
((!ff_data.ce_over_srst) || (ff_data.ce_over_srst && ce))) {
|
2022-02-18 02:17:36 -06:00
|
|
|
current_q = ff_data.val_srst;
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
|
|
|
}
|
2022-02-18 02:17:36 -06:00
|
|
|
}
|
|
|
|
// async load
|
|
|
|
if (ff_data.has_aload) {
|
|
|
|
State current_aload = get_state(ff_data.sig_aload)[0];
|
|
|
|
if (current_aload == (ff_data.pol_aload ? State::S1 : State::S0)) {
|
|
|
|
current_q = ff_data.has_clk ? ff.past_ad : get_state(ff.data.sig_ad);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// async reset
|
|
|
|
if (ff_data.has_arst) {
|
|
|
|
State current_arst = get_state(ff_data.sig_arst)[0];
|
|
|
|
if (current_arst == (ff_data.pol_arst ? State::S1 : State::S0)) {
|
|
|
|
current_q = ff_data.val_arst;
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
2022-02-18 02:17:36 -06:00
|
|
|
}
|
|
|
|
// handle set/reset
|
|
|
|
if (ff.data.has_sr) {
|
|
|
|
Const current_clr = get_state(ff.data.sig_clr);
|
|
|
|
Const current_set = get_state(ff.data.sig_set);
|
|
|
|
|
|
|
|
for(int i=0;i<ff.past_d.size();i++) {
|
|
|
|
if (current_clr[i] == (ff_data.pol_clr ? State::S1 : State::S0)) {
|
|
|
|
current_q[i] = State::S0;
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
2022-02-18 02:17:36 -06:00
|
|
|
else if (current_set[i] == (ff_data.pol_set ? State::S1 : State::S0)) {
|
|
|
|
current_q[i] = State::S1;
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
2022-02-18 02:17:36 -06:00
|
|
|
if (ff_data.has_gclk) {
|
|
|
|
// $ff
|
|
|
|
current_q = ff.past_d;
|
|
|
|
}
|
|
|
|
if (set_state(ff_data.sig_q, current_q))
|
|
|
|
did_something = true;
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
|
|
|
|
2017-08-18 04:44:50 -05:00
|
|
|
for (auto &it : mem_database)
|
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
mem_state_t &mdb = it.second;
|
|
|
|
auto &mem = *mdb.mem;
|
2017-08-18 04:44:50 -05:00
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
for (int port_idx = 0; port_idx < GetSize(mem.wr_ports); port_idx++)
|
2017-08-18 04:44:50 -05:00
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
auto &port = mem.wr_ports[port_idx];
|
2017-08-18 04:44:50 -05:00
|
|
|
Const addr, data, enable;
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
if (!port.clk_enable)
|
2017-08-18 04:44:50 -05:00
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
addr = get_state(port.addr);
|
|
|
|
data = get_state(port.data);
|
|
|
|
enable = get_state(port.en);
|
2017-08-18 04:44:50 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
if (port.clk_polarity ?
|
|
|
|
(mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) :
|
|
|
|
(mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0))
|
2017-08-18 04:44:50 -05:00
|
|
|
continue;
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
addr = mdb.past_wr_addr[port_idx];
|
|
|
|
data = mdb.past_wr_data[port_idx];
|
|
|
|
enable = mdb.past_wr_en[port_idx];
|
2017-08-18 04:44:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (addr.is_fully_def())
|
|
|
|
{
|
2020-10-17 08:49:36 -05:00
|
|
|
int index = addr.as_int() - mem.start_offset;
|
|
|
|
if (index >= 0 && index < mem.size)
|
2021-05-24 18:12:19 -05:00
|
|
|
for (int i = 0; i < (mem.width << port.wide_log2); i++)
|
2020-10-17 08:49:36 -05:00
|
|
|
if (enable[i] == State::S1 && mdb.data.bits.at(index*mem.width+i) != data[i]) {
|
|
|
|
mdb.data.bits.at(index*mem.width+i) = data[i];
|
|
|
|
dirty_memories.insert(mem.memid);
|
2017-08-18 04:44:50 -05:00
|
|
|
did_something = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
for (auto it : children)
|
|
|
|
if (it.second->update_ph2()) {
|
|
|
|
dirty_children.insert(it.second);
|
|
|
|
did_something = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return did_something;
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_ph3()
|
|
|
|
{
|
|
|
|
for (auto &it : ff_database)
|
|
|
|
{
|
|
|
|
ff_state_t &ff = it.second;
|
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
if (ff.data.has_aload)
|
|
|
|
ff.past_ad = get_state(ff.data.sig_ad);
|
|
|
|
|
|
|
|
if (ff.data.has_clk || ff.data.has_gclk)
|
|
|
|
ff.past_d = get_state(ff.data.sig_d);
|
|
|
|
|
|
|
|
if (ff.data.has_clk)
|
|
|
|
ff.past_clk = get_state(ff.data.sig_clk)[0];
|
|
|
|
|
|
|
|
if (ff.data.has_ce)
|
|
|
|
ff.past_ce = get_state(ff.data.sig_ce)[0];
|
|
|
|
|
|
|
|
if (ff.data.has_srst)
|
|
|
|
ff.past_srst = get_state(ff.data.sig_srst)[0];
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
|
|
|
|
2017-08-18 04:44:50 -05:00
|
|
|
for (auto &it : mem_database)
|
|
|
|
{
|
|
|
|
mem_state_t &mem = it.second;
|
|
|
|
|
2020-10-17 08:49:36 -05:00
|
|
|
for (int i = 0; i < GetSize(mem.mem->wr_ports); i++) {
|
|
|
|
auto &port = mem.mem->wr_ports[i];
|
|
|
|
mem.past_wr_clk[i] = get_state(port.clk);
|
|
|
|
mem.past_wr_en[i] = get_state(port.en);
|
|
|
|
mem.past_wr_addr[i] = get_state(port.addr);
|
|
|
|
mem.past_wr_data[i] = get_state(port.data);
|
|
|
|
}
|
2017-08-18 04:44:50 -05:00
|
|
|
}
|
|
|
|
|
2017-08-18 03:24:14 -05:00
|
|
|
for (auto cell : formal_database)
|
|
|
|
{
|
|
|
|
string label = log_id(cell);
|
2020-03-12 14:57:01 -05:00
|
|
|
if (cell->attributes.count(ID::src))
|
|
|
|
label = cell->attributes.at(ID::src).decode_string();
|
2017-08-18 03:24:14 -05:00
|
|
|
|
2020-03-12 14:57:01 -05:00
|
|
|
State a = get_state(cell->getPort(ID::A))[0];
|
2020-04-02 11:51:32 -05:00
|
|
|
State en = get_state(cell->getPort(ID::EN))[0];
|
2017-08-18 03:24:14 -05:00
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
if (cell->type == ID($cover) && en == State::S1 && a != State::S1)
|
2017-08-18 03:24:14 -05:00
|
|
|
log("Cover %s.%s (%s) reached.\n", hiername().c_str(), log_id(cell), label.c_str());
|
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
if (cell->type == ID($assume) && en == State::S1 && a != State::S1)
|
2017-08-18 03:24:14 -05:00
|
|
|
log("Assumption %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
if (cell->type == ID($assert) && en == State::S1 && a != State::S1)
|
2017-08-18 03:24:14 -05:00
|
|
|
log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
|
|
|
}
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
for (auto it : children)
|
|
|
|
it.second->update_ph3();
|
|
|
|
}
|
|
|
|
|
2017-08-17 08:54:51 -05:00
|
|
|
void writeback(pool<Module*> &wbmods)
|
|
|
|
{
|
|
|
|
if (wbmods.count(module))
|
2017-08-20 05:31:50 -05:00
|
|
|
log_error("Instance %s of module %s is not unique: Writeback not possible. (Fix by running 'uniquify'.)\n", hiername().c_str(), log_id(module));
|
2017-08-17 08:54:51 -05:00
|
|
|
|
|
|
|
wbmods.insert(module);
|
|
|
|
|
|
|
|
for (auto wire : module->wires())
|
2020-04-02 11:51:32 -05:00
|
|
|
wire->attributes.erase(ID::init);
|
2017-08-17 08:54:51 -05:00
|
|
|
|
|
|
|
for (auto &it : ff_database)
|
|
|
|
{
|
2022-02-15 02:30:42 -06:00
|
|
|
SigSpec sig_q = it.second.data.sig_q;
|
2017-08-17 08:54:51 -05:00
|
|
|
Const initval = get_state(sig_q);
|
|
|
|
|
|
|
|
for (int i = 0; i < GetSize(sig_q); i++)
|
|
|
|
{
|
|
|
|
Wire *w = sig_q[i].wire;
|
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
if (w->attributes.count(ID::init) == 0)
|
|
|
|
w->attributes[ID::init] = Const(State::Sx, GetSize(w));
|
2017-08-17 08:54:51 -05:00
|
|
|
|
2020-04-02 11:51:32 -05:00
|
|
|
w->attributes[ID::init][sig_q[i].offset] = initval[i];
|
2017-08-17 08:54:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-18 04:44:50 -05:00
|
|
|
for (auto &it : mem_database)
|
|
|
|
{
|
|
|
|
mem_state_t &mem = it.second;
|
2020-10-17 08:49:36 -05:00
|
|
|
mem.mem->clear_inits();
|
|
|
|
MemInit minit;
|
|
|
|
minit.addr = mem.mem->start_offset;
|
|
|
|
minit.data = mem.data;
|
2021-05-20 19:26:52 -05:00
|
|
|
minit.en = Const(State::S1, mem.mem->width);
|
2020-10-17 08:49:36 -05:00
|
|
|
mem.mem->inits.push_back(minit);
|
|
|
|
mem.mem->emit();
|
2017-08-18 04:44:50 -05:00
|
|
|
}
|
|
|
|
|
2017-08-17 08:54:51 -05:00
|
|
|
for (auto it : children)
|
|
|
|
it.second->writeback(wbmods);
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
void write_output_header(OutputWriter *writer, int &id)
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2022-02-28 11:22:39 -06:00
|
|
|
writer->enter_scope(name());
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
for (auto wire : module->wires())
|
|
|
|
{
|
2017-08-17 05:27:08 -05:00
|
|
|
if (shared->hide_internal && wire->name[0] == '$')
|
2017-08-16 06:05:21 -05:00
|
|
|
continue;
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
signal_database[wire] = make_pair(id, Const());
|
|
|
|
writer->register_signal(wire, id);
|
|
|
|
id++;
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto child : children)
|
2022-02-28 11:22:39 -06:00
|
|
|
child.second->write_output_header(writer, id);
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
writer->exit_scope();
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
void write_output_step_values()
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2022-02-28 11:22:39 -06:00
|
|
|
for (auto &it : signal_database)
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
|
|
|
Wire *wire = it.first;
|
|
|
|
Const value = get_state(wire);
|
2017-08-17 05:27:08 -05:00
|
|
|
int id = it.second.first;
|
|
|
|
|
|
|
|
if (it.second.second == value)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
it.second.second = value;
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
for(auto& writer : shared->outputfiles)
|
|
|
|
writer->write_value(id, value);
|
2022-01-26 02:26:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto child : children)
|
2022-02-28 11:22:39 -06:00
|
|
|
child.second->write_output_step_values();
|
2022-01-26 02:26:19 -06:00
|
|
|
}
|
2022-01-28 06:24:38 -06:00
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
void setInitState()
|
2022-01-28 08:59:13 -06:00
|
|
|
{
|
|
|
|
for (auto &it : ff_database)
|
|
|
|
{
|
2022-02-15 02:30:42 -06:00
|
|
|
SigSpec qsig = it.second.data.sig_q;
|
2022-01-28 08:59:13 -06:00
|
|
|
if (qsig.is_wire()) {
|
|
|
|
IdString name = qsig.as_wire()->name;
|
|
|
|
fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(name));
|
|
|
|
if (id==0 && name.isPublic())
|
|
|
|
log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(name)).c_str());
|
|
|
|
if (id!=0) {
|
2022-02-15 02:30:42 -06:00
|
|
|
Const fst_val = Const::from_string(shared->fst->valueOf(id));
|
2022-01-28 08:59:13 -06:00
|
|
|
set_state(qsig, fst_val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto child : children)
|
2022-02-15 02:30:42 -06:00
|
|
|
child.second->setInitState();
|
2022-01-28 08:59:13 -06:00
|
|
|
}
|
|
|
|
|
2022-02-25 08:50:46 -06:00
|
|
|
void setState(dict<int, std::pair<SigBit,bool>> bits, std::string values)
|
2022-02-18 08:04:02 -06:00
|
|
|
{
|
2022-02-25 08:50:46 -06:00
|
|
|
for(auto bit : bits) {
|
|
|
|
if (bit.first >= GetSize(values))
|
|
|
|
log_error("Too few input data bits in file.\n");
|
|
|
|
switch(values.at(bit.first)) {
|
|
|
|
case '0': set_state(bit.second.first, bit.second.second ? State::S1 : State::S0); break;
|
|
|
|
case '1': set_state(bit.second.first, bit.second.second ? State::S0 : State::S1); break;
|
|
|
|
default: set_state(bit.second.first, State::Sx); break;
|
2022-02-18 08:04:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
bool checkSignals()
|
2022-01-28 06:24:38 -06:00
|
|
|
{
|
|
|
|
bool retVal = false;
|
|
|
|
for(auto &item : fst_handles) {
|
|
|
|
if (item.second==0) continue; // Ignore signals not found
|
2022-02-15 02:30:42 -06:00
|
|
|
Const fst_val = Const::from_string(shared->fst->valueOf(item.second));
|
2022-01-28 06:24:38 -06:00
|
|
|
Const sim_val = get_state(item.first);
|
2022-01-31 06:41:02 -06:00
|
|
|
if (sim_val.size()!=fst_val.size())
|
|
|
|
log_error("Signal '%s' size is different in gold and gate.\n", log_id(item.first));
|
2022-02-02 02:37:32 -06:00
|
|
|
if (shared->sim_mode == SimulationMode::sim) {
|
|
|
|
// No checks performed when using stimulus
|
|
|
|
} else if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
|
2022-01-31 06:41:02 -06:00
|
|
|
for(int i=0;i<fst_val.size();i++) {
|
|
|
|
if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
|
2022-02-04 03:01:06 -06:00
|
|
|
log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
2022-01-31 06:41:02 -06:00
|
|
|
retVal = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-01-28 06:24:38 -06:00
|
|
|
} else if (shared->sim_mode == SimulationMode::gold && !sim_val.is_fully_def()) { // sim data contains X
|
2022-01-31 06:41:02 -06:00
|
|
|
for(int i=0;i<sim_val.size();i++) {
|
|
|
|
if (sim_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
|
2022-02-04 03:01:06 -06:00
|
|
|
log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
2022-01-31 06:41:02 -06:00
|
|
|
retVal = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-01-28 06:24:38 -06:00
|
|
|
} else {
|
|
|
|
if (fst_val!=sim_val) {
|
2022-02-04 03:01:06 -06:00
|
|
|
log_warning("Signal '%s' in file %s in simulation '%s'\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
2022-01-28 06:24:38 -06:00
|
|
|
retVal = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto child : children)
|
2022-02-15 02:30:42 -06:00
|
|
|
retVal |= child.second->checkSignals();
|
2022-01-28 06:24:38 -06:00
|
|
|
return retVal;
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
};
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
struct SimWorker : SimShared
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
|
|
|
SimInstance *top = nullptr;
|
2017-08-17 05:27:08 -05:00
|
|
|
pool<IdString> clock, clockn, reset, resetn;
|
2020-10-16 11:19:58 -05:00
|
|
|
std::string timescale;
|
2022-01-26 08:50:38 -06:00
|
|
|
std::string sim_filename;
|
2022-02-18 08:04:02 -06:00
|
|
|
std::string map_filename;
|
2022-01-26 08:50:38 -06:00
|
|
|
std::string scope;
|
2017-08-16 06:05:21 -05:00
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
~SimWorker()
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
2022-02-28 11:22:39 -06:00
|
|
|
outputfiles.clear();
|
2017-08-17 05:27:08 -05:00
|
|
|
delete top;
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
|
2022-01-26 02:26:19 -06:00
|
|
|
void write_output_header()
|
|
|
|
{
|
2022-02-28 11:22:39 -06:00
|
|
|
for(auto& writer : outputfiles)
|
|
|
|
writer->write_header();
|
2022-01-26 02:26:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void write_output_step(int t)
|
|
|
|
{
|
2022-02-28 11:22:39 -06:00
|
|
|
for(auto& writer : outputfiles)
|
|
|
|
writer->write_step_header(t);
|
2022-01-26 02:26:19 -06:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
top->write_output_step_values();
|
2022-01-26 02:26:19 -06:00
|
|
|
}
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
void update()
|
|
|
|
{
|
2017-08-18 03:24:14 -05:00
|
|
|
while (1)
|
2017-08-17 05:27:08 -05:00
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
log("\n-- ph1 --\n");
|
|
|
|
|
|
|
|
top->update_ph1();
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
log("\n-- ph2 --\n");
|
2017-08-18 03:24:14 -05:00
|
|
|
|
|
|
|
if (!top->update_ph2())
|
|
|
|
break;
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
log("\n-- ph3 --\n");
|
|
|
|
|
|
|
|
top->update_ph3();
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_inports(pool<IdString> ports, State value)
|
|
|
|
{
|
|
|
|
for (auto portname : ports)
|
|
|
|
{
|
|
|
|
Wire *w = top->module->wire(portname);
|
|
|
|
|
|
|
|
if (w == nullptr)
|
|
|
|
log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
|
|
|
|
|
|
|
|
top->set_state(w, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(Module *topmod, int numcycles)
|
|
|
|
{
|
|
|
|
log_assert(top == nullptr);
|
2022-01-26 08:50:38 -06:00
|
|
|
top = new SimInstance(this, scope, topmod);
|
2017-08-17 05:27:08 -05:00
|
|
|
|
|
|
|
if (debug)
|
|
|
|
log("\n===== 0 =====\n");
|
2017-08-18 03:24:14 -05:00
|
|
|
else
|
|
|
|
log("Simulating cycle 0.\n");
|
2017-08-17 05:27:08 -05:00
|
|
|
|
|
|
|
set_inports(reset, State::S1);
|
|
|
|
set_inports(resetn, State::S0);
|
|
|
|
|
2017-08-18 05:54:17 -05:00
|
|
|
set_inports(clock, State::Sx);
|
|
|
|
set_inports(clockn, State::Sx);
|
|
|
|
|
2017-08-17 05:27:08 -05:00
|
|
|
update();
|
|
|
|
|
2022-01-26 02:26:19 -06:00
|
|
|
write_output_header();
|
|
|
|
write_output_step(0);
|
2017-08-17 05:27:08 -05:00
|
|
|
|
|
|
|
for (int cycle = 0; cycle < numcycles; cycle++)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
log("\n===== %d =====\n", 10*cycle + 5);
|
2022-02-02 03:08:23 -06:00
|
|
|
else
|
|
|
|
log("Simulating cycle %d.\n", (cycle*2)+1);
|
2017-08-17 05:27:08 -05:00
|
|
|
set_inports(clock, State::S0);
|
|
|
|
set_inports(clockn, State::S1);
|
|
|
|
|
|
|
|
update();
|
2022-01-26 02:26:19 -06:00
|
|
|
write_output_step(10*cycle + 5);
|
2017-08-17 05:27:08 -05:00
|
|
|
|
|
|
|
if (debug)
|
|
|
|
log("\n===== %d =====\n", 10*cycle + 10);
|
2017-08-18 03:24:14 -05:00
|
|
|
else
|
2022-02-02 03:08:23 -06:00
|
|
|
log("Simulating cycle %d.\n", (cycle*2)+2);
|
2017-08-17 05:27:08 -05:00
|
|
|
|
|
|
|
set_inports(clock, State::S1);
|
|
|
|
set_inports(clockn, State::S0);
|
|
|
|
|
2017-08-18 05:54:17 -05:00
|
|
|
if (cycle+1 == rstlen) {
|
2017-08-17 05:27:08 -05:00
|
|
|
set_inports(reset, State::S0);
|
|
|
|
set_inports(resetn, State::S1);
|
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
2022-01-26 02:26:19 -06:00
|
|
|
write_output_step(10*cycle + 10);
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
|
|
|
|
2022-01-26 02:26:19 -06:00
|
|
|
write_output_step(10*numcycles + 2);
|
|
|
|
|
2017-08-17 08:54:51 -05:00
|
|
|
if (writeback) {
|
|
|
|
pool<Module*> wbmods;
|
|
|
|
top->writeback(wbmods);
|
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
}
|
2022-01-26 08:50:38 -06:00
|
|
|
|
|
|
|
void run_cosim(Module *topmod, int numcycles)
|
|
|
|
{
|
|
|
|
log_assert(top == nullptr);
|
|
|
|
fst = new FstData(sim_filename);
|
|
|
|
|
2022-02-04 04:11:36 -06:00
|
|
|
if (scope.empty())
|
|
|
|
log_error("Scope must be defined for co-simulation.\n");
|
|
|
|
|
2022-01-28 06:24:38 -06:00
|
|
|
top = new SimInstance(this, scope, topmod);
|
|
|
|
|
2022-01-26 08:50:38 -06:00
|
|
|
std::vector<fstHandle> fst_clock;
|
|
|
|
|
|
|
|
for (auto portname : clock)
|
|
|
|
{
|
|
|
|
Wire *w = topmod->wire(portname);
|
|
|
|
if (!w)
|
|
|
|
log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
|
|
|
|
if (!w->port_input)
|
|
|
|
log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
|
|
|
|
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
|
|
|
|
if (id==0)
|
|
|
|
log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
|
|
|
|
fst_clock.push_back(id);
|
|
|
|
}
|
|
|
|
for (auto portname : clockn)
|
|
|
|
{
|
|
|
|
Wire *w = topmod->wire(portname);
|
|
|
|
if (!w)
|
|
|
|
log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
|
|
|
|
if (!w->port_input)
|
|
|
|
log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
|
|
|
|
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
|
|
|
|
if (id==0)
|
|
|
|
log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
|
|
|
|
fst_clock.push_back(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
SigMap sigmap(topmod);
|
|
|
|
std::map<Wire*,fstHandle> inputs;
|
|
|
|
|
|
|
|
for (auto wire : topmod->wires()) {
|
|
|
|
if (wire->port_input) {
|
|
|
|
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
|
2022-01-31 10:41:50 -06:00
|
|
|
if (id==0)
|
|
|
|
log_error("Unable to find required '%s' signal in file\n",(scope + "." + RTLIL::unescape_id(wire->name)).c_str());
|
2022-01-26 08:50:38 -06:00
|
|
|
inputs[wire] = id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 03:18:02 -06:00
|
|
|
uint64_t startCount = 0;
|
|
|
|
uint64_t stopCount = 0;
|
|
|
|
if (start_time==0) {
|
|
|
|
if (start_time < fst->getStartTime())
|
|
|
|
log_warning("Start time is before simulation file start time\n");
|
|
|
|
startCount = fst->getStartTime();
|
|
|
|
} else if (start_time==-1)
|
|
|
|
startCount = fst->getEndTime();
|
|
|
|
else {
|
|
|
|
startCount = start_time / fst->getTimescale();
|
|
|
|
if (startCount > fst->getEndTime()) {
|
|
|
|
startCount = fst->getEndTime();
|
|
|
|
log_warning("Start time is after simulation file end time\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (stop_time==0) {
|
|
|
|
if (stop_time < fst->getStartTime())
|
|
|
|
log_warning("Stop time is before simulation file start time\n");
|
|
|
|
stopCount = fst->getStartTime();
|
|
|
|
} else if (stop_time==-1)
|
|
|
|
stopCount = fst->getEndTime();
|
|
|
|
else {
|
|
|
|
stopCount = stop_time / fst->getTimescale();
|
|
|
|
if (stopCount > fst->getEndTime()) {
|
|
|
|
stopCount = fst->getEndTime();
|
|
|
|
log_warning("Stop time is after simulation file end time\n");
|
|
|
|
}
|
|
|
|
}
|
2022-01-28 12:41:43 -06:00
|
|
|
if (stopCount<startCount) {
|
|
|
|
log_error("Stop time is before start time\n");
|
|
|
|
}
|
2022-02-02 03:08:23 -06:00
|
|
|
|
|
|
|
bool initial = true;
|
|
|
|
int cycle = 0;
|
2022-02-15 02:30:42 -06:00
|
|
|
log("Co-simulation from %lu%s to %lu%s", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString());
|
|
|
|
if (cycles_set)
|
|
|
|
log(" for %d clock cycle(s)",numcycles);
|
|
|
|
log("\n");
|
|
|
|
bool all_samples = fst_clock.empty();
|
|
|
|
|
|
|
|
try {
|
|
|
|
fst->reconstructAllAtTimes(fst_clock, startCount, stopCount, [&](uint64_t time) {
|
|
|
|
log("Co-simulating %s %d [%lu%s].\n", (all_samples ? "sample" : "cycle"), cycle, (unsigned long)time, fst->getTimescaleString());
|
2022-02-25 09:15:32 -06:00
|
|
|
bool did_something = false;
|
2022-02-15 02:30:42 -06:00
|
|
|
for(auto &item : inputs) {
|
|
|
|
std::string v = fst->valueOf(item.second);
|
2022-02-25 08:50:46 -06:00
|
|
|
did_something |= top->set_state(item.first, Const::from_string(v));
|
2022-02-15 02:30:42 -06:00
|
|
|
}
|
2022-02-02 03:08:23 -06:00
|
|
|
|
2022-02-15 02:30:42 -06:00
|
|
|
if (initial) {
|
|
|
|
top->setInitState();
|
|
|
|
write_output_header();
|
|
|
|
initial = false;
|
|
|
|
}
|
2022-02-25 08:50:46 -06:00
|
|
|
if (did_something)
|
|
|
|
update();
|
|
|
|
write_output_step(time);
|
2022-02-15 02:30:42 -06:00
|
|
|
|
|
|
|
bool status = top->checkSignals();
|
|
|
|
if (status)
|
|
|
|
log_error("Signal difference\n");
|
|
|
|
cycle++;
|
|
|
|
|
|
|
|
// Limit to number of cycles if provided
|
|
|
|
if (cycles_set && cycle > numcycles *2)
|
|
|
|
throw fst_end_of_data_exception();
|
|
|
|
if (time==stopCount)
|
|
|
|
throw fst_end_of_data_exception();
|
|
|
|
});
|
|
|
|
} catch(fst_end_of_data_exception) {
|
|
|
|
// end of data detected
|
2022-02-02 03:08:23 -06:00
|
|
|
}
|
2022-02-15 02:30:42 -06:00
|
|
|
|
2022-02-02 03:08:23 -06:00
|
|
|
if (writeback) {
|
2022-01-31 02:19:34 -06:00
|
|
|
pool<Module*> wbmods;
|
|
|
|
top->writeback(wbmods);
|
2022-01-28 05:50:41 -06:00
|
|
|
}
|
2022-01-26 08:51:43 -06:00
|
|
|
}
|
2022-02-18 08:04:02 -06:00
|
|
|
|
|
|
|
void run_cosim_witness(Module *topmod)
|
|
|
|
{
|
|
|
|
log_assert(top == nullptr);
|
|
|
|
std::ifstream mf(map_filename);
|
|
|
|
std::string type, symbol;
|
|
|
|
int variable, index;
|
2022-02-25 08:50:46 -06:00
|
|
|
dict<int, std::pair<SigBit,bool>> inputs, inits, latches;
|
2022-02-18 08:04:02 -06:00
|
|
|
while (mf >> type >> variable >> index >> symbol) {
|
|
|
|
RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
|
|
|
|
Wire *w = topmod->wire(escaped_s);
|
|
|
|
if (!w)
|
|
|
|
log_error("Wire %s not present in module %s\n",log_signal(w),log_id(topmod));
|
|
|
|
if (index < w->start_offset || index > w->start_offset + w->width)
|
|
|
|
log_error("Index %d for wire %s is out of range\n", index, log_signal(w));
|
|
|
|
if (type == "input") {
|
2022-02-25 08:50:46 -06:00
|
|
|
inputs[variable] = {SigBit(w,index), false};
|
|
|
|
} else if (type == "init") {
|
|
|
|
inits[variable] = {SigBit(w,index), false};
|
2022-02-18 08:04:02 -06:00
|
|
|
} else if (type == "latch") {
|
2022-02-25 08:50:46 -06:00
|
|
|
latches[variable] = {SigBit(w,index), false};
|
2022-02-18 08:04:02 -06:00
|
|
|
} else if (type == "invlatch") {
|
2022-02-25 08:50:46 -06:00
|
|
|
latches[variable] = {SigBit(w,index), true};
|
2022-02-18 08:04:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ifstream f;
|
|
|
|
f.open(sim_filename.c_str());
|
|
|
|
if (f.fail() || GetSize(sim_filename) == 0)
|
|
|
|
log_error("Can not open file `%s`\n", sim_filename.c_str());
|
|
|
|
|
2022-02-27 09:37:40 -06:00
|
|
|
int state = 0;
|
|
|
|
std::string status;
|
2022-02-18 08:04:02 -06:00
|
|
|
int cycle = 0;
|
|
|
|
top = new SimInstance(this, scope, topmod);
|
|
|
|
while (!f.eof())
|
|
|
|
{
|
|
|
|
std::string line;
|
|
|
|
std::getline(f, line);
|
|
|
|
if (line.size()==0 || line[0]=='#') continue;
|
2022-02-27 09:37:40 -06:00
|
|
|
if (line[0]=='.') break;
|
|
|
|
if (state==0 && line.size()!=1) {
|
|
|
|
// old format detected, latch data
|
|
|
|
state = 2;
|
|
|
|
}
|
|
|
|
if (state==1 && line[0]!='b' && line[0]!='c') {
|
2022-02-18 08:04:02 -06:00
|
|
|
write_output_header();
|
2022-02-27 09:37:40 -06:00
|
|
|
// was old format but with 1 bit latch
|
|
|
|
top->setState(latches, status);
|
|
|
|
state = 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(state)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
status = line;
|
|
|
|
state = 1;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
state = 2;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
write_output_header();
|
|
|
|
top->setState(latches, line);
|
2022-02-28 03:41:44 -06:00
|
|
|
state = 3;
|
2022-02-27 09:37:40 -06:00
|
|
|
break;
|
|
|
|
default:
|
2022-02-28 04:40:06 -06:00
|
|
|
log("Simulating cycle %d.\n", cycle);
|
|
|
|
top->setState(inputs, line);
|
2022-02-27 09:37:40 -06:00
|
|
|
if (cycle) {
|
|
|
|
set_inports(clock, State::S1);
|
|
|
|
set_inports(clockn, State::S0);
|
|
|
|
} else {
|
|
|
|
top->setState(inits, line);
|
|
|
|
set_inports(clock, State::S0);
|
|
|
|
set_inports(clockn, State::S1);
|
|
|
|
}
|
2022-02-18 09:27:41 -06:00
|
|
|
update();
|
2022-02-27 09:37:40 -06:00
|
|
|
write_output_step(10*cycle);
|
|
|
|
if (cycle) {
|
|
|
|
set_inports(clock, State::S0);
|
|
|
|
set_inports(clockn, State::S1);
|
|
|
|
update();
|
|
|
|
write_output_step(10*cycle + 5);
|
|
|
|
}
|
|
|
|
cycle++;
|
|
|
|
break;
|
2022-02-18 08:04:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
write_output_step(10*cycle);
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
};
|
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
struct VCDWriter : public OutputWriter
|
|
|
|
{
|
|
|
|
VCDWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
|
|
|
|
vcdfile.open(filename.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_header() override
|
|
|
|
{
|
|
|
|
if (!vcdfile.is_open()) return;
|
|
|
|
vcdfile << stringf("$version %s $end\n", yosys_version_str);
|
|
|
|
|
|
|
|
std::time_t t = std::time(nullptr);
|
|
|
|
char mbstr[255];
|
|
|
|
if (std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&t))) {
|
|
|
|
vcdfile << stringf("$date ") << mbstr << stringf(" $end\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!worker->timescale.empty())
|
|
|
|
vcdfile << stringf("$timescale %s $end\n", worker->timescale.c_str());
|
|
|
|
|
|
|
|
int id = 1;
|
|
|
|
worker->top->write_output_header(this, id);
|
|
|
|
|
|
|
|
vcdfile << stringf("$enddefinitions $end\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_step_header(int t) override
|
|
|
|
{
|
|
|
|
if (!vcdfile.is_open()) return;
|
|
|
|
vcdfile << stringf("#%d\n", t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void enter_scope(IdString name) override
|
|
|
|
{
|
|
|
|
vcdfile << stringf("$scope module %s $end\n", log_id(name));
|
|
|
|
}
|
2022-03-02 02:39:22 -06:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
void exit_scope() override
|
|
|
|
{
|
|
|
|
vcdfile << stringf("$upscope $end\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void register_signal(Wire *wire, int id) override
|
|
|
|
{
|
|
|
|
vcdfile << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire));
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_value(int id, Const& value) override
|
|
|
|
{
|
|
|
|
if (!vcdfile.is_open()) return;
|
|
|
|
vcdfile << "b";
|
|
|
|
for (int i = GetSize(value)-1; i >= 0; i--) {
|
|
|
|
switch (value[i]) {
|
|
|
|
case State::S0: vcdfile << "0"; break;
|
|
|
|
case State::S1: vcdfile << "1"; break;
|
|
|
|
case State::Sx: vcdfile << "x"; break;
|
|
|
|
default: vcdfile << "z";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vcdfile << stringf(" n%d\n", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ofstream vcdfile;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FSTWriter : public OutputWriter
|
|
|
|
{
|
|
|
|
FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
|
|
|
|
fstfile = (struct fstContext *)fstWriterCreate(filename.c_str(),1);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~FSTWriter()
|
|
|
|
{
|
|
|
|
fstWriterClose(fstfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_header() override
|
|
|
|
{
|
|
|
|
if (!fstfile) return;
|
|
|
|
std::time_t t = std::time(nullptr);
|
|
|
|
fstWriterSetDate(fstfile, asctime(std::localtime(&t)));
|
|
|
|
fstWriterSetVersion(fstfile, yosys_version_str);
|
|
|
|
if (!worker->timescale.empty())
|
|
|
|
fstWriterSetTimescaleFromString(fstfile, worker->timescale.c_str());
|
|
|
|
|
|
|
|
fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ);
|
|
|
|
fstWriterSetRepackOnClose(fstfile, 1);
|
|
|
|
|
|
|
|
int id = 1;
|
|
|
|
worker->top->write_output_header(this, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_step_header(int t) override
|
|
|
|
{
|
|
|
|
if (!fstfile) return;
|
|
|
|
fstWriterEmitTimeChange(fstfile, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void enter_scope(IdString name) override
|
|
|
|
{
|
|
|
|
fstWriterSetScope(fstfile, FST_ST_VCD_MODULE, stringf("%s",log_id(name)).c_str(), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void exit_scope() override
|
|
|
|
{
|
|
|
|
fstWriterSetUpscope(fstfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
void register_signal(Wire *wire, int id) override
|
|
|
|
{
|
|
|
|
fstHandle fst_id = fstWriterCreateVar(fstfile, FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire),
|
|
|
|
stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0);
|
|
|
|
|
|
|
|
mapping.emplace(id, fst_id);
|
|
|
|
}
|
2022-03-02 02:39:22 -06:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
void write_value(int id, Const& value) override
|
|
|
|
{
|
|
|
|
if (!fstfile) return;
|
|
|
|
std::stringstream ss;
|
|
|
|
for (int i = GetSize(value)-1; i >= 0; i--) {
|
|
|
|
switch (value[i]) {
|
|
|
|
case State::S0: ss << "0"; break;
|
|
|
|
case State::S1: ss << "1"; break;
|
|
|
|
case State::Sx: ss << "x"; break;
|
|
|
|
default: ss << "z";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fstWriterEmitValueChange(fstfile, mapping[id], ss.str().c_str());
|
|
|
|
}
|
2022-03-02 02:39:22 -06:00
|
|
|
|
2022-02-28 11:22:39 -06:00
|
|
|
struct fstContext *fstfile = nullptr;
|
|
|
|
std::map<int,fstHandle> mapping;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AIWWriter : public OutputWriter
|
|
|
|
{
|
|
|
|
AIWWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
|
|
|
|
aiwfile.open(filename.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~AIWWriter()
|
|
|
|
{
|
|
|
|
aiwfile << '.' << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_header() override
|
|
|
|
{
|
|
|
|
if (!aiwfile.is_open()) return;
|
|
|
|
std::ifstream mf(worker->map_filename);
|
|
|
|
std::string type, symbol;
|
|
|
|
int variable, index;
|
|
|
|
while (mf >> type >> variable >> index >> symbol) {
|
|
|
|
RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
|
|
|
|
Wire *w = worker->top->module->wire(escaped_s);
|
|
|
|
if (!w)
|
|
|
|
log_error("Wire %s not present in module %s\n",log_signal(w),log_id(worker->top->module));
|
|
|
|
if (index < w->start_offset || index > w->start_offset + w->width)
|
|
|
|
log_error("Index %d for wire %s is out of range\n", index, log_signal(w));
|
|
|
|
if (type == "input") {
|
|
|
|
aiw_inputs[variable] = SigBit(w,index);
|
|
|
|
} else if (type == "init") {
|
|
|
|
aiw_inits[variable] = SigBit(w,index);
|
|
|
|
} else if (type == "latch") {
|
|
|
|
aiw_latches[variable] = {SigBit(w,index), false};
|
|
|
|
} else if (type == "invlatch") {
|
|
|
|
aiw_latches[variable] = {SigBit(w,index), true};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0;; i++)
|
|
|
|
{
|
|
|
|
if (aiw_latches.count(i)) {
|
|
|
|
auto v = worker->top->get_state(aiw_latches.at(i).first);
|
|
|
|
if (v == State::S1)
|
|
|
|
aiwfile << (aiw_latches.at(i).second ? '0' : '1');
|
|
|
|
else
|
|
|
|
aiwfile << (aiw_latches.at(i).second ? '1' : '0');
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aiwfile << '\n';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_step_header(int) override
|
|
|
|
{
|
|
|
|
if (!aiwfile.is_open()) return;
|
|
|
|
for (int i = 0;; i++)
|
|
|
|
{
|
|
|
|
if (aiw_inputs.count(i)) {
|
|
|
|
auto v = worker->top->get_state(aiw_inputs.at(i));
|
|
|
|
if (v == State::S1)
|
|
|
|
aiwfile << '1';
|
|
|
|
else
|
|
|
|
aiwfile << '0';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (aiw_inits.count(i)) {
|
|
|
|
auto v = worker->top->get_state(aiw_inits.at(i));
|
|
|
|
if (v == State::S1)
|
|
|
|
aiwfile << '1';
|
|
|
|
else
|
|
|
|
aiwfile << '0';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aiwfile << '\n';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ofstream aiwfile;
|
|
|
|
dict<int, std::pair<SigBit, bool>> aiw_latches;
|
|
|
|
dict<int, SigBit> aiw_inputs, aiw_inits;
|
|
|
|
};
|
|
|
|
|
2017-08-16 06:05:21 -05:00
|
|
|
struct SimPass : public Pass {
|
|
|
|
SimPass() : Pass("sim", "simulate the circuit") { }
|
2020-06-18 18:34:52 -05:00
|
|
|
void help() override
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
|
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
|
|
log("\n");
|
|
|
|
log(" sim [options] [top-level]\n");
|
|
|
|
log("\n");
|
|
|
|
log("This command simulates the circuit using the given top-level module.\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -vcd <filename>\n");
|
|
|
|
log(" write the simulation results to the given VCD file\n");
|
|
|
|
log("\n");
|
2022-01-26 02:26:19 -06:00
|
|
|
log(" -fst <filename>\n");
|
|
|
|
log(" write the simulation results to the given FST file\n");
|
|
|
|
log("\n");
|
2022-02-28 03:50:08 -06:00
|
|
|
log(" -aiw <filename>\n");
|
|
|
|
log(" write the simulation results to an AIGER witness file\n");
|
|
|
|
log(" (requires a *.aim file via -map)\n");
|
|
|
|
log("\n");
|
2017-08-17 05:27:08 -05:00
|
|
|
log(" -clock <portname>\n");
|
|
|
|
log(" name of top-level clock input\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -clockn <portname>\n");
|
|
|
|
log(" name of top-level clock input (inverse polarity)\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -reset <portname>\n");
|
|
|
|
log(" name of top-level reset input (active high)\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -resetn <portname>\n");
|
|
|
|
log(" name of top-level inverted reset input (active low)\n");
|
|
|
|
log("\n");
|
2017-08-18 05:54:17 -05:00
|
|
|
log(" -rstlen <integer>\n");
|
|
|
|
log(" number of cycles reset should stay active (default: 1)\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -zinit\n");
|
|
|
|
log(" zero-initialize all uninitialized regs and memories\n");
|
|
|
|
log("\n");
|
2020-10-16 11:19:58 -05:00
|
|
|
log(" -timescale <string>\n");
|
|
|
|
log(" include the specified timescale declaration in the vcd\n");
|
|
|
|
log("\n");
|
2017-08-16 06:05:21 -05:00
|
|
|
log(" -n <integer>\n");
|
2022-02-02 02:37:32 -06:00
|
|
|
log(" number of clock cycles to simulate (default: 20)\n");
|
2017-08-17 05:27:08 -05:00
|
|
|
log("\n");
|
|
|
|
log(" -a\n");
|
2022-02-02 02:37:32 -06:00
|
|
|
log(" use all nets in VCD/FST operations, not just those with public names\n");
|
2017-08-17 05:27:08 -05:00
|
|
|
log("\n");
|
2017-08-17 08:54:51 -05:00
|
|
|
log(" -w\n");
|
|
|
|
log(" writeback mode: use final simulation state as new init state\n");
|
|
|
|
log("\n");
|
2022-01-26 08:50:38 -06:00
|
|
|
log(" -r\n");
|
|
|
|
log(" read simulation results file (file formats supported: FST)\n");
|
|
|
|
log("\n");
|
2022-02-18 08:04:02 -06:00
|
|
|
log(" -map <filename>\n");
|
|
|
|
log(" read file with port and latch symbols, needed for AIGER witness input\n");
|
|
|
|
log("\n");
|
2022-01-26 08:50:38 -06:00
|
|
|
log(" -scope\n");
|
|
|
|
log(" scope of simulation top model\n");
|
|
|
|
log("\n");
|
2022-02-02 02:37:32 -06:00
|
|
|
log(" -at <time>\n");
|
|
|
|
log(" sets start and stop time\n");
|
|
|
|
log("\n");
|
2022-01-28 03:18:02 -06:00
|
|
|
log(" -start <time>\n");
|
|
|
|
log(" start co-simulation in arbitary time (default 0)\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -stop <time>\n");
|
|
|
|
log(" stop co-simulation in arbitary time (default END)\n");
|
|
|
|
log("\n");
|
2022-02-02 02:37:32 -06:00
|
|
|
log(" -sim\n");
|
|
|
|
log(" simulation with stimulus from FST (default)\n");
|
|
|
|
log("\n");
|
2022-01-28 03:18:02 -06:00
|
|
|
log(" -sim-cmp\n");
|
2022-02-02 02:37:32 -06:00
|
|
|
log(" co-simulation expect exact match\n");
|
2022-01-28 03:18:02 -06:00
|
|
|
log("\n");
|
|
|
|
log(" -sim-gold\n");
|
|
|
|
log(" co-simulation, x in simulation can match any value in FST\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -sim-gate\n");
|
|
|
|
log(" co-simulation, x in FST can match any value in simulation\n");
|
|
|
|
log("\n");
|
2017-08-17 05:27:08 -05:00
|
|
|
log(" -d\n");
|
|
|
|
log(" enable debug output\n");
|
2017-08-16 06:05:21 -05:00
|
|
|
log("\n");
|
|
|
|
}
|
2020-06-18 18:34:52 -05:00
|
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
2017-08-16 06:05:21 -05:00
|
|
|
{
|
|
|
|
SimWorker worker;
|
2017-08-17 05:27:08 -05:00
|
|
|
int numcycles = 20;
|
2022-02-02 02:37:32 -06:00
|
|
|
bool start_set = false, stop_set = false, at_set = false;
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
log_header(design, "Executing SIM pass (simulate the circuit).\n");
|
|
|
|
|
|
|
|
size_t argidx;
|
|
|
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
|
|
|
if (args[argidx] == "-vcd" && argidx+1 < args.size()) {
|
2020-11-24 14:17:16 -06:00
|
|
|
std::string vcd_filename = args[++argidx];
|
|
|
|
rewrite_filename(vcd_filename);
|
2022-02-28 11:22:39 -06:00
|
|
|
worker.outputfiles.emplace_back(std::unique_ptr<VCDWriter>(new VCDWriter(&worker, vcd_filename.c_str())));
|
2017-08-16 06:05:21 -05:00
|
|
|
continue;
|
|
|
|
}
|
2022-01-26 02:26:19 -06:00
|
|
|
if (args[argidx] == "-fst" && argidx+1 < args.size()) {
|
|
|
|
std::string fst_filename = args[++argidx];
|
|
|
|
rewrite_filename(fst_filename);
|
2022-02-28 11:22:39 -06:00
|
|
|
worker.outputfiles.emplace_back(std::unique_ptr<FSTWriter>(new FSTWriter(&worker, fst_filename.c_str())));
|
2022-01-26 02:26:19 -06:00
|
|
|
continue;
|
|
|
|
}
|
2022-02-28 03:50:08 -06:00
|
|
|
if (args[argidx] == "-aiw" && argidx+1 < args.size()) {
|
|
|
|
std::string aiw_filename = args[++argidx];
|
|
|
|
rewrite_filename(aiw_filename);
|
2022-02-28 11:22:39 -06:00
|
|
|
worker.outputfiles.emplace_back(std::unique_ptr<AIWWriter>(new AIWWriter(&worker, aiw_filename.c_str())));
|
2022-02-28 03:50:08 -06:00
|
|
|
continue;
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
if (args[argidx] == "-n" && argidx+1 < args.size()) {
|
2017-08-17 05:27:08 -05:00
|
|
|
numcycles = atoi(args[++argidx].c_str());
|
2022-01-31 02:38:25 -06:00
|
|
|
worker.cycles_set = true;
|
2017-08-17 05:27:08 -05:00
|
|
|
continue;
|
|
|
|
}
|
2017-08-18 05:54:17 -05:00
|
|
|
if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
|
|
|
|
worker.rstlen = atoi(args[++argidx].c_str());
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
if (args[argidx] == "-clock" && argidx+1 < args.size()) {
|
|
|
|
worker.clock.insert(RTLIL::escape_id(args[++argidx]));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-clockn" && argidx+1 < args.size()) {
|
|
|
|
worker.clockn.insert(RTLIL::escape_id(args[++argidx]));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-reset" && argidx+1 < args.size()) {
|
|
|
|
worker.reset.insert(RTLIL::escape_id(args[++argidx]));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-resetn" && argidx+1 < args.size()) {
|
|
|
|
worker.resetn.insert(RTLIL::escape_id(args[++argidx]));
|
|
|
|
continue;
|
|
|
|
}
|
2020-10-16 11:19:58 -05:00
|
|
|
if (args[argidx] == "-timescale" && argidx+1 < args.size()) {
|
|
|
|
worker.timescale = args[++argidx];
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-17 05:27:08 -05:00
|
|
|
if (args[argidx] == "-a") {
|
|
|
|
worker.hide_internal = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-d") {
|
|
|
|
worker.debug = true;
|
2017-08-16 06:05:21 -05:00
|
|
|
continue;
|
|
|
|
}
|
2017-08-17 08:54:51 -05:00
|
|
|
if (args[argidx] == "-w") {
|
|
|
|
worker.writeback = true;
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-18 05:54:17 -05:00
|
|
|
if (args[argidx] == "-zinit") {
|
|
|
|
worker.zinit = true;
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-26 08:50:38 -06:00
|
|
|
if (args[argidx] == "-r" && argidx+1 < args.size()) {
|
|
|
|
std::string sim_filename = args[++argidx];
|
|
|
|
rewrite_filename(sim_filename);
|
|
|
|
worker.sim_filename = sim_filename;
|
|
|
|
continue;
|
|
|
|
}
|
2022-02-18 08:04:02 -06:00
|
|
|
if (args[argidx] == "-map" && argidx+1 < args.size()) {
|
|
|
|
std::string map_filename = args[++argidx];
|
|
|
|
rewrite_filename(map_filename);
|
|
|
|
worker.map_filename = map_filename;
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-26 08:50:38 -06:00
|
|
|
if (args[argidx] == "-scope" && argidx+1 < args.size()) {
|
|
|
|
worker.scope = args[++argidx];
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-28 03:18:02 -06:00
|
|
|
if (args[argidx] == "-start" && argidx+1 < args.size()) {
|
|
|
|
worker.start_time = stringToTime(args[++argidx]);
|
2022-02-02 02:37:32 -06:00
|
|
|
start_set = true;
|
2022-01-28 03:18:02 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-stop" && argidx+1 < args.size()) {
|
|
|
|
worker.stop_time = stringToTime(args[++argidx]);
|
2022-02-02 02:37:32 -06:00
|
|
|
stop_set = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-at" && argidx+1 < args.size()) {
|
|
|
|
worker.start_time = stringToTime(args[++argidx]);
|
|
|
|
worker.stop_time = worker.start_time;
|
|
|
|
at_set = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-sim") {
|
|
|
|
worker.sim_mode = SimulationMode::sim;
|
2022-01-28 03:18:02 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-sim-cmp") {
|
|
|
|
worker.sim_mode = SimulationMode::cmp;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-sim-gold") {
|
|
|
|
worker.sim_mode = SimulationMode::gold;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (args[argidx] == "-sim-gate") {
|
|
|
|
worker.sim_mode = SimulationMode::gate;
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-16 06:05:21 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
extra_args(args, argidx, design);
|
2022-02-02 02:37:32 -06:00
|
|
|
if (at_set && (start_set || stop_set || worker.cycles_set))
|
|
|
|
log_error("'at' option can only be defined separate of 'start','stop' and 'n'\n");
|
|
|
|
if (stop_set && worker.cycles_set)
|
|
|
|
log_error("'stop' and 'n' can only be used exclusively'\n");
|
2017-08-16 06:05:21 -05:00
|
|
|
|
|
|
|
Module *top_mod = nullptr;
|
|
|
|
|
|
|
|
if (design->full_selection()) {
|
|
|
|
top_mod = design->top_module();
|
2019-06-05 16:16:24 -05:00
|
|
|
|
|
|
|
if (!top_mod)
|
|
|
|
log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n");
|
2017-08-16 06:05:21 -05:00
|
|
|
} else {
|
|
|
|
auto mods = design->selected_whole_modules();
|
|
|
|
if (GetSize(mods) != 1)
|
|
|
|
log_cmd_error("Only one top module must be selected.\n");
|
|
|
|
top_mod = mods.front();
|
|
|
|
}
|
|
|
|
|
2022-01-26 08:50:38 -06:00
|
|
|
if (worker.sim_filename.empty())
|
|
|
|
worker.run(top_mod, numcycles);
|
|
|
|
else
|
2022-02-18 08:04:02 -06:00
|
|
|
if (worker.map_filename.empty())
|
|
|
|
worker.run_cosim(top_mod, numcycles);
|
|
|
|
else
|
|
|
|
worker.run_cosim_witness(top_mod);
|
2017-08-16 06:05:21 -05:00
|
|
|
}
|
|
|
|
} SimPass;
|
|
|
|
|
|
|
|
PRIVATE_NAMESPACE_END
|