mirror of https://github.com/YosysHQ/yosys.git
sim: Add Yosys witness (.yw) cosimulation
This commit is contained in:
parent
1494cfff00
commit
2dd5652215
|
@ -23,6 +23,7 @@
|
||||||
#include "kernel/mem.h"
|
#include "kernel/mem.h"
|
||||||
#include "kernel/fstdata.h"
|
#include "kernel/fstdata.h"
|
||||||
#include "kernel/ff.h"
|
#include "kernel/ff.h"
|
||||||
|
#include "kernel/yw.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
@ -1603,6 +1604,194 @@ struct SimWorker : SimShared
|
||||||
write_output_files();
|
write_output_files();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FoundYWPath
|
||||||
|
{
|
||||||
|
SimInstance *instance;
|
||||||
|
Wire *wire;
|
||||||
|
IdString memid;
|
||||||
|
int addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct YwHierarchy {
|
||||||
|
dict<IdPath, FoundYWPath> signal_paths;
|
||||||
|
dict<IdPath, FoundYWPath> clock_paths;
|
||||||
|
};
|
||||||
|
|
||||||
|
YwHierarchy prepare_yw_hierarchy(const ReadWitness &yw)
|
||||||
|
{
|
||||||
|
pool<IdPath> paths;
|
||||||
|
dict<IdPath, pool<IdString>> mem_paths;
|
||||||
|
|
||||||
|
for (auto &signal : yw.signals)
|
||||||
|
paths.insert(signal.path);
|
||||||
|
|
||||||
|
for (auto &clock : yw.clocks) {
|
||||||
|
if (paths.count(clock.path))
|
||||||
|
log_warning("Witness path `%s` is present as witness signal and as clock, treating as clock and ignoring signal data.\n", clock.path.str().c_str());
|
||||||
|
paths.insert(clock.path);
|
||||||
|
}
|
||||||
|
for (auto &path : paths)
|
||||||
|
if (path.has_address())
|
||||||
|
mem_paths[path.prefix()].insert(path.back());
|
||||||
|
|
||||||
|
YwHierarchy hierarchy;
|
||||||
|
witness_hierarchy(top->module, top, [&](IdPath const &path, WitnessHierarchyItem item, SimInstance *instance) {
|
||||||
|
if (item.cell != nullptr)
|
||||||
|
return instance->children.at(item.cell);
|
||||||
|
if (item.wire != nullptr) {
|
||||||
|
if (paths.count(path)) {
|
||||||
|
if (debug)
|
||||||
|
log("witness hierarchy: found wire %s\n", path.str().c_str());
|
||||||
|
bool inserted = hierarchy.signal_paths.emplace(path, {instance, item.wire, {}, INT_MIN}).second;
|
||||||
|
if (!inserted)
|
||||||
|
log_warning("Yosys witness path `%s` is ambiguous in this design\n", path.str().c_str());
|
||||||
|
}
|
||||||
|
} else if (item.mem) {
|
||||||
|
auto it = mem_paths.find(path);
|
||||||
|
if (it != mem_paths.end()) {
|
||||||
|
if (debug)
|
||||||
|
log("witness hierarchy: found mem %s\n", path.str().c_str());
|
||||||
|
IdPath word_path = path;
|
||||||
|
word_path.emplace_back();
|
||||||
|
for (auto addr_part : it->second) {
|
||||||
|
word_path.back() = addr_part;
|
||||||
|
int addr;
|
||||||
|
word_path.get_address(addr);
|
||||||
|
if (addr < item.mem->start_offset || (addr - item.mem->start_offset) >= item.mem->size)
|
||||||
|
continue;
|
||||||
|
bool inserted = hierarchy.signal_paths.emplace(word_path, {instance, nullptr, item.mem->memid, addr}).second;
|
||||||
|
if (!inserted)
|
||||||
|
log_warning("Yosys witness path `%s` is ambiguous in this design\n", path.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto &path : paths)
|
||||||
|
if (!hierarchy.signal_paths.count(path))
|
||||||
|
log_warning("Yosys witness path `%s` was not found in this design, ignoring\n", path.str().c_str());
|
||||||
|
|
||||||
|
for (auto &clock : yw.clocks) {
|
||||||
|
auto found_path_it = hierarchy.signal_paths.find(clock.path);
|
||||||
|
if (found_path_it == hierarchy.signal_paths.end())
|
||||||
|
continue;
|
||||||
|
hierarchy.clock_paths.insert(*found_path_it);
|
||||||
|
hierarchy.signal_paths.erase(found_path_it);
|
||||||
|
paths.insert(clock.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO add checks and warnings for witness signals (toplevel inputs, $any*) not present in the witness file
|
||||||
|
|
||||||
|
return hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_yw_state(const ReadWitness &yw, const YwHierarchy &hierarchy, int t)
|
||||||
|
{
|
||||||
|
log_assert(t >= 0 && t < GetSize(yw.steps));
|
||||||
|
|
||||||
|
for (auto &signal : yw.signals) {
|
||||||
|
if (signal.init_only && t >= 1)
|
||||||
|
continue;
|
||||||
|
auto found_path_it = hierarchy.signal_paths.find(signal.path);
|
||||||
|
if (found_path_it == hierarchy.signal_paths.end())
|
||||||
|
continue;
|
||||||
|
auto &found_path = found_path_it->second;
|
||||||
|
|
||||||
|
Const value = yw.get_bits(t, signal.bits_offset, signal.width);
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
log("yw: set %s to %s\n", signal.path.str().c_str(), log_const(value));
|
||||||
|
|
||||||
|
if (found_path.wire != nullptr) {
|
||||||
|
found_path.instance->set_state(
|
||||||
|
SigChunk(found_path.wire, signal.offset, signal.width),
|
||||||
|
value);
|
||||||
|
} else if (!found_path.memid.empty()) {
|
||||||
|
found_path.instance->register_memory_addr(found_path.memid, found_path.addr);
|
||||||
|
found_path.instance->set_memory_state(
|
||||||
|
found_path.memid, found_path.addr,
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_yw_clocks(const ReadWitness &yw, const YwHierarchy &hierarchy, bool active_edge)
|
||||||
|
{
|
||||||
|
for (auto &clock : yw.clocks) {
|
||||||
|
if (clock.is_negedge == clock.is_posedge)
|
||||||
|
continue;
|
||||||
|
auto found_path_it = hierarchy.clock_paths.find(clock.path);
|
||||||
|
if (found_path_it == hierarchy.clock_paths.end())
|
||||||
|
continue;
|
||||||
|
auto &found_path = found_path_it->second;
|
||||||
|
|
||||||
|
if (found_path.wire != nullptr) {
|
||||||
|
found_path.instance->set_state(
|
||||||
|
SigChunk(found_path.wire, clock.offset, 1),
|
||||||
|
active_edge == clock.is_posedge ? State::S1 : State::S0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_cosim_yw_witness(Module *topmod)
|
||||||
|
{
|
||||||
|
if (!clock.empty())
|
||||||
|
log_cmd_error("The -clock option is not required nor supported when reading a Yosys witness file.\n");
|
||||||
|
if (!reset.empty())
|
||||||
|
log_cmd_error("The -reset option is not required nor supported when reading a Yosys witness file.\n");
|
||||||
|
if (multiclock)
|
||||||
|
log_warning("The -multiclock option is not required and ignored when reading a Yosys witness file.\n");
|
||||||
|
|
||||||
|
ReadWitness yw(sim_filename);
|
||||||
|
|
||||||
|
top = new SimInstance(this, scope, topmod);
|
||||||
|
register_signals();
|
||||||
|
|
||||||
|
YwHierarchy hierarchy = prepare_yw_hierarchy(yw);
|
||||||
|
|
||||||
|
if (yw.steps.empty()) {
|
||||||
|
log_warning("Yosys witness file `%s` contains no time steps\n", yw.filename.c_str());
|
||||||
|
} else {
|
||||||
|
top->set_initstate_outputs(State::S1);
|
||||||
|
set_yw_state(yw, hierarchy, 0);
|
||||||
|
set_yw_clocks(yw, hierarchy, true);
|
||||||
|
initialize_stable_past();
|
||||||
|
register_output_step(0);
|
||||||
|
|
||||||
|
if (!yw.clocks.empty()) {
|
||||||
|
if (debug)
|
||||||
|
log("Simulating non-active clock edge.\n");
|
||||||
|
set_yw_clocks(yw, hierarchy, false);
|
||||||
|
update(false);
|
||||||
|
register_output_step(5);
|
||||||
|
}
|
||||||
|
top->set_initstate_outputs(State::S0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int cycle = 1; cycle < GetSize(yw.steps); cycle++)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
log("Simulating cycle %d.\n", cycle);
|
||||||
|
set_yw_state(yw, hierarchy, cycle);
|
||||||
|
set_yw_clocks(yw, hierarchy, true);
|
||||||
|
update(true);
|
||||||
|
register_output_step(10 * cycle);
|
||||||
|
|
||||||
|
if (!yw.clocks.empty()) {
|
||||||
|
if (debug)
|
||||||
|
log("Simulating non-active clock edge.\n");
|
||||||
|
set_yw_clocks(yw, hierarchy, false);
|
||||||
|
update(false);
|
||||||
|
register_output_step(5 + 10 * cycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_output_step(10*GetSize(yw.steps));
|
||||||
|
write_output_files();
|
||||||
|
}
|
||||||
|
|
||||||
std::string define_signal(Wire *wire)
|
std::string define_signal(Wire *wire)
|
||||||
{
|
{
|
||||||
std::stringstream f;
|
std::stringstream f;
|
||||||
|
@ -2132,9 +2321,9 @@ struct SimPass : public Pass {
|
||||||
log(" -w\n");
|
log(" -w\n");
|
||||||
log(" writeback mode: use final simulation state as new init state\n");
|
log(" writeback mode: use final simulation state as new init state\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -r\n");
|
log(" -r <filename>\n");
|
||||||
log(" read simulation results file\n");
|
log(" read simulation or formal results file\n");
|
||||||
log(" File formats supported: FST, VCD, AIW and WIT\n");
|
log(" File formats supported: FST, VCD, AIW, WIT and .yw\n");
|
||||||
log(" VCD support requires vcd2fst external tool to be present\n");
|
log(" VCD support requires vcd2fst external tool to be present\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -map <filename>\n");
|
log(" -map <filename>\n");
|
||||||
|
@ -2354,6 +2543,8 @@ struct SimPass : public Pass {
|
||||||
worker.run_cosim_aiger_witness(top_mod);
|
worker.run_cosim_aiger_witness(top_mod);
|
||||||
} else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".wit") == 0) {
|
} else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".wit") == 0) {
|
||||||
worker.run_cosim_btor2_witness(top_mod);
|
worker.run_cosim_btor2_witness(top_mod);
|
||||||
|
} else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".yw") == 0) {
|
||||||
|
worker.run_cosim_yw_witness(top_mod);
|
||||||
} else {
|
} else {
|
||||||
log_cmd_error("Unhandled extension for simulation input file `%s`.\n", worker.sim_filename.c_str());
|
log_cmd_error("Unhandled extension for simulation input file `%s`.\n", worker.sim_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue