mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 13:29:12 +00:00 
			
		
		
		
	sim: Improvements and fixes for yw cosim
* Fixed $cover handling * Improved sparse memory handling when writing traces * JSON summary output
This commit is contained in:
		
							parent
							
								
									636b9f2705
								
							
						
					
					
						commit
						7ddec5093f
					
				
					 6 changed files with 152 additions and 50 deletions
				
			
		| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
#include "kernel/yosys.h"
 | 
			
		||||
#include "kernel/sigtools.h"
 | 
			
		||||
#include "kernel/json.h"
 | 
			
		||||
#include "kernel/yw.h"
 | 
			
		||||
#include "libs/json11/json11.hpp"
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
| 
						 | 
				
			
			@ -710,18 +711,6 @@ struct AigerWriter
 | 
			
		|||
			f << it.second;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_ywmap(PrettyJson &json)
 | 
			
		||||
	{
 | 
			
		||||
		json.begin_object();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
#include "kernel/log.h"
 | 
			
		||||
#include "kernel/mem.h"
 | 
			
		||||
#include "kernel/json.h"
 | 
			
		||||
#include "kernel/yw.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
| 
						 | 
				
			
			@ -141,18 +142,6 @@ struct BtorWorker
 | 
			
		|||
		return " " + infostr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void ywmap_state(const SigSpec &sig) {
 | 
			
		||||
		if (ywmap_json.active())
 | 
			
		||||
			ywmap_states.emplace_back(sig);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -316,6 +316,7 @@ def wit2yw(input, mapfile, output):
 | 
			
		|||
        if current_t > t:
 | 
			
		||||
            t = current_t
 | 
			
		||||
            values = WitnessValues()
 | 
			
		||||
            array_inits = set()
 | 
			
		||||
            frames.append(values)
 | 
			
		||||
 | 
			
		||||
        line = next(input, None)
 | 
			
		||||
| 
						 | 
				
			
			@ -327,39 +328,63 @@ def wit2yw(input, mapfile, output):
 | 
			
		|||
            line = next(input, None)
 | 
			
		||||
 | 
			
		||||
            btor_sig = btor_map.data[mode][int(tokens[0])]
 | 
			
		||||
            btor_sigs = [btor_sig]
 | 
			
		||||
 | 
			
		||||
            if btor_sig is None:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if isinstance(btor_sig, dict):
 | 
			
		||||
                addr = tokens[1]
 | 
			
		||||
                if not addr.startswith('[') or not addr.endswith(']'):
 | 
			
		||||
                if not addr.startswith('['):
 | 
			
		||||
                    addr = '[*]'
 | 
			
		||||
                    value = tokens[1]
 | 
			
		||||
                else:
 | 
			
		||||
                    value = tokens[2]
 | 
			
		||||
                if not addr.endswith(']'):
 | 
			
		||||
                    raise click.ClickException(f"{input_name}: expected address in BTOR witness file")
 | 
			
		||||
                addr = int(addr[1:-1], 2)
 | 
			
		||||
                path = btor_sig["path"]
 | 
			
		||||
                width = btor_sig["width"]
 | 
			
		||||
                size = btor_sig["size"]
 | 
			
		||||
                if addr == '[*]':
 | 
			
		||||
                    btor_sigs = [
 | 
			
		||||
                        [{
 | 
			
		||||
                            "path": (*path, f"\\[{addr}]"),
 | 
			
		||||
                            "width": width,
 | 
			
		||||
                            "offset": 0,
 | 
			
		||||
                        }]
 | 
			
		||||
                        for addr in range(size)
 | 
			
		||||
                        if (path, addr) not in array_inits
 | 
			
		||||
                    ]
 | 
			
		||||
                    array_inits.update((path, addr) for addr in range(size))
 | 
			
		||||
                else:
 | 
			
		||||
                    addr = int(addr[1:-1], 2)
 | 
			
		||||
 | 
			
		||||
                if addr < 0 or addr >= btor_sig["size"]:
 | 
			
		||||
                    raise click.ClickException(f"{input_name}: out of bounds address in BTOR witness file")
 | 
			
		||||
                    if addr < 0 or addr >= size:
 | 
			
		||||
                        raise click.ClickException(f"{input_name}: out of bounds address in BTOR witness file")
 | 
			
		||||
 | 
			
		||||
                btor_sig = [{
 | 
			
		||||
                    "path": (*btor_sig["path"], f"\\[{addr}]"),
 | 
			
		||||
                    "width": btor_sig["width"],
 | 
			
		||||
                    "offset": 0,
 | 
			
		||||
                }]
 | 
			
		||||
 | 
			
		||||
                signal_value = iter(reversed(tokens[2]))
 | 
			
		||||
                    array_inits.add((path, addr))
 | 
			
		||||
                    btor_sig = [{
 | 
			
		||||
                        "path": (*path, f"\\[{addr}]"),
 | 
			
		||||
                        "width": width,
 | 
			
		||||
                        "offset": 0,
 | 
			
		||||
                    }]
 | 
			
		||||
                    btor_sigs = [btor_sig]
 | 
			
		||||
            else:
 | 
			
		||||
                signal_value = iter(reversed(tokens[1]))
 | 
			
		||||
                value = tokens[1]
 | 
			
		||||
 | 
			
		||||
            for chunk in btor_sig:
 | 
			
		||||
                offset = chunk["offset"]
 | 
			
		||||
                path = chunk["path"]
 | 
			
		||||
                for i in range(offset, offset + chunk["width"]):
 | 
			
		||||
                    key = (path, i)
 | 
			
		||||
                    bits[key] = mode == "inputs"
 | 
			
		||||
                    values[key] = next(signal_value)
 | 
			
		||||
            for btor_sig in btor_sigs:
 | 
			
		||||
                value_bits = iter(reversed(value))
 | 
			
		||||
 | 
			
		||||
            if next(signal_value, None) is not None:
 | 
			
		||||
                raise click.ClickException(f"{input_name}: excess bits in BTOR witness file")
 | 
			
		||||
                for chunk in btor_sig:
 | 
			
		||||
                    offset = chunk["offset"]
 | 
			
		||||
                    path = chunk["path"]
 | 
			
		||||
                    for i in range(offset, offset + chunk["width"]):
 | 
			
		||||
                        key = (path, i)
 | 
			
		||||
                        bits[key] = mode == "inputs"
 | 
			
		||||
                        values[key] = next(value_bits)
 | 
			
		||||
 | 
			
		||||
                if next(value_bits, None) is not None:
 | 
			
		||||
                    raise click.ClickException(f"{input_name}: excess bits in BTOR witness file")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if line is None:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ ReadWitness::ReadWitness(const std::string &filename) :
 | 
			
		|||
		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 = json["init_only"].bool_value();
 | 
			
		||||
		signal.init_only = signal_json["init_only"].bool_value();
 | 
			
		||||
		signals.push_back(signal);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								kernel/yw.h
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								kernel/yw.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -52,6 +52,18 @@ struct WitnessHierarchyItem {
 | 
			
		|||
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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@
 | 
			
		|||
#include "kernel/fstdata.h"
 | 
			
		||||
#include "kernel/ff.h"
 | 
			
		||||
#include "kernel/yw.h"
 | 
			
		||||
#include "kernel/json.h"
 | 
			
		||||
 | 
			
		||||
#include <ctime>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +76,17 @@ struct OutputWriter
 | 
			
		|||
	SimWorker *worker;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SimInstance;
 | 
			
		||||
struct TriggeredAssertion {
 | 
			
		||||
	int step;
 | 
			
		||||
	SimInstance *instance;
 | 
			
		||||
	Cell *cell;
 | 
			
		||||
 | 
			
		||||
	TriggeredAssertion(int step, SimInstance *instance, Cell *cell) :
 | 
			
		||||
		step(step), instance(instance), cell(cell)
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SimShared
 | 
			
		||||
{
 | 
			
		||||
	bool debug = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +107,8 @@ struct SimShared
 | 
			
		|||
	bool date = false;
 | 
			
		||||
	bool multiclock = false;
 | 
			
		||||
	int next_output_id = 0;
 | 
			
		||||
	int step = 0;
 | 
			
		||||
	std::vector<TriggeredAssertion> triggered_assertions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void zinit(State &v)
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +175,7 @@ struct SimInstance
 | 
			
		|||
 | 
			
		||||
	dict<Wire*, pair<int, Const>> signal_database;
 | 
			
		||||
	dict<IdString, std::map<int, pair<int, Const>>> trace_mem_database;
 | 
			
		||||
	dict<std::pair<IdString, int>, Const> trace_mem_init_database;
 | 
			
		||||
	dict<Wire*, fstHandle> fst_handles;
 | 
			
		||||
	dict<Wire*, fstHandle> fst_inputs;
 | 
			
		||||
	dict<IdString, dict<int,fstHandle>> fst_memories;
 | 
			
		||||
| 
						 | 
				
			
			@ -306,6 +321,21 @@ struct SimInstance
 | 
			
		|||
		return log_id(module->name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vector<std::string> witness_full_path() const
 | 
			
		||||
	{
 | 
			
		||||
		if (instance != nullptr)
 | 
			
		||||
			return parent->witness_full_path(instance);
 | 
			
		||||
		return vector<std::string>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vector<std::string> witness_full_path(Cell *cell) const
 | 
			
		||||
	{
 | 
			
		||||
		auto result = witness_full_path();
 | 
			
		||||
		auto cell_path = witness_path(cell);
 | 
			
		||||
		result.insert(result.end(), cell_path.begin(), cell_path.end());
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Const get_state(SigSpec sig)
 | 
			
		||||
	{
 | 
			
		||||
		Const value;
 | 
			
		||||
| 
						 | 
				
			
			@ -700,7 +730,11 @@ struct SimInstance
 | 
			
		|||
				State a = get_state(cell->getPort(ID::A))[0];
 | 
			
		||||
				State en = get_state(cell->getPort(ID::EN))[0];
 | 
			
		||||
 | 
			
		||||
				if (cell->type == ID($cover) && en == State::S1 && a != State::S1)
 | 
			
		||||
				if (en == State::S1 && (cell->type == ID($cover) ? a == State::S1 : a != State::S1)) {
 | 
			
		||||
					shared->triggered_assertions.emplace_back(shared->step, this, cell);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (cell->type == ID($cover) && en == State::S1 && a == State::S1)
 | 
			
		||||
					log("Cover %s.%s (%s) reached.\n", hiername().c_str(), log_id(cell), label.c_str());
 | 
			
		||||
 | 
			
		||||
				if (cell->type == ID($assume) && en == State::S1 && a != State::S1)
 | 
			
		||||
| 
						 | 
				
			
			@ -875,7 +909,11 @@ struct SimInstance
 | 
			
		|||
		int output_id = shared->next_output_id++;
 | 
			
		||||
		Const data;
 | 
			
		||||
		if (!shared->output_data.empty()) {
 | 
			
		||||
			data = mem.get_init_data().extract(index * mem.width, mem.width);
 | 
			
		||||
			auto init_it = trace_mem_init_database.find(std::make_pair(memid, addr));
 | 
			
		||||
			if (init_it != trace_mem_init_database.end())
 | 
			
		||||
				data = init_it->second;
 | 
			
		||||
			else
 | 
			
		||||
				data = mem.get_init_data().extract(index * mem.width, mem.width);
 | 
			
		||||
			shared->output_data.front().second.emplace(output_id, data);
 | 
			
		||||
		}
 | 
			
		||||
		trace_mem_database[memid].emplace(index, make_pair(output_id, data));
 | 
			
		||||
| 
						 | 
				
			
			@ -1060,6 +1098,7 @@ struct SimWorker : SimShared
 | 
			
		|||
	std::string timescale;
 | 
			
		||||
	std::string sim_filename;
 | 
			
		||||
	std::string map_filename;
 | 
			
		||||
	std::string summary_filename;
 | 
			
		||||
	std::string scope;
 | 
			
		||||
 | 
			
		||||
	~SimWorker()
 | 
			
		||||
| 
						 | 
				
			
			@ -1103,6 +1142,9 @@ struct SimWorker : SimShared
 | 
			
		|||
 | 
			
		||||
	void update(bool gclk)
 | 
			
		||||
	{
 | 
			
		||||
		if (gclk)
 | 
			
		||||
			step += 1;
 | 
			
		||||
 | 
			
		||||
		while (1)
 | 
			
		||||
		{
 | 
			
		||||
			if (debug)
 | 
			
		||||
| 
						 | 
				
			
			@ -1130,7 +1172,7 @@ struct SimWorker : SimShared
 | 
			
		|||
		top->update_ph1();
 | 
			
		||||
		if (debug)
 | 
			
		||||
			log("\n-- ph3 (initialize) --\n");
 | 
			
		||||
		top->update_ph3(false);
 | 
			
		||||
		top->update_ph3(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_inports(pool<IdString> ports, State value)
 | 
			
		||||
| 
						 | 
				
			
			@ -1709,7 +1751,10 @@ struct SimWorker : SimShared
 | 
			
		|||
						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);
 | 
			
		||||
				if (t >= 1)
 | 
			
		||||
					found_path.instance->register_memory_addr(found_path.memid, found_path.addr);
 | 
			
		||||
				else
 | 
			
		||||
					found_path.instance->trace_mem_init_database.emplace(make_pair(found_path.memid, found_path.addr), value);
 | 
			
		||||
				found_path.instance->set_memory_state(
 | 
			
		||||
						found_path.memid, found_path.addr,
 | 
			
		||||
						value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1793,6 +1838,37 @@ struct SimWorker : SimShared
 | 
			
		|||
		write_output_files();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_summary()
 | 
			
		||||
	{
 | 
			
		||||
		if (summary_filename.empty())
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		PrettyJson json;
 | 
			
		||||
		if (!json.write_to_file(summary_filename))
 | 
			
		||||
			log_error("Can't open file `%s' for writing: %s\n", summary_filename.c_str(), strerror(errno));
 | 
			
		||||
 | 
			
		||||
		json.begin_object();
 | 
			
		||||
		json.entry("version", "Yosys sim summary");
 | 
			
		||||
		json.entry("generator", yosys_version_str);
 | 
			
		||||
		json.entry("steps", step);
 | 
			
		||||
		json.entry("top", log_id(top->module->name));
 | 
			
		||||
		json.name("assertions");
 | 
			
		||||
		json.begin_array();
 | 
			
		||||
		for (auto &assertion : triggered_assertions) {
 | 
			
		||||
			json.begin_object();
 | 
			
		||||
			json.entry("step", assertion.step);
 | 
			
		||||
			json.entry("type", log_id(assertion.cell->type));
 | 
			
		||||
			json.entry("path", assertion.instance->witness_full_path(assertion.cell));
 | 
			
		||||
			auto src = assertion.cell->get_string_attribute(ID::src);
 | 
			
		||||
			if (!src.empty()) {
 | 
			
		||||
				json.entry("src", src);
 | 
			
		||||
			}
 | 
			
		||||
			json.end_object();
 | 
			
		||||
		}
 | 
			
		||||
		json.end_array();
 | 
			
		||||
		json.end_object();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string define_signal(Wire *wire)
 | 
			
		||||
	{
 | 
			
		||||
		std::stringstream f;
 | 
			
		||||
| 
						 | 
				
			
			@ -2330,6 +2406,9 @@ struct SimPass : public Pass {
 | 
			
		|||
		log("    -append <integer>\n");
 | 
			
		||||
		log("        number of extra clock cycles to simulate for a Yosys witness input\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -summary <filename>\n");
 | 
			
		||||
		log("        write a JSON summary to the given file\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -map <filename>\n");
 | 
			
		||||
		log("        read file with port and latch symbols, needed for AIGER witness input\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -2469,6 +2548,12 @@ struct SimPass : public Pass {
 | 
			
		|||
				worker.map_filename = map_filename;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-summary" && argidx+1 < args.size()) {
 | 
			
		||||
				std::string summary_filename = args[++argidx];
 | 
			
		||||
				rewrite_filename(summary_filename);
 | 
			
		||||
				worker.summary_filename = summary_filename;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-scope" && argidx+1 < args.size()) {
 | 
			
		||||
				worker.scope = args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -2558,6 +2643,8 @@ struct SimPass : public Pass {
 | 
			
		|||
				log_cmd_error("Unhandled extension for simulation input file `%s`.\n", worker.sim_filename.c_str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		worker.write_summary();
 | 
			
		||||
	}
 | 
			
		||||
} SimPass;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue