mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			259 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  yosys -- Yosys Open SYnthesis Suite
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2022  Marcelina Kościelnicka <mwk@0x04.net>
 | 
						|
 *
 | 
						|
 *  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/register.h"
 | 
						|
#include "kernel/sigtools.h"
 | 
						|
#include "kernel/log.h"
 | 
						|
#include "kernel/mem.h"
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
USING_YOSYS_NAMESPACE
 | 
						|
PRIVATE_NAMESPACE_BEGIN
 | 
						|
 | 
						|
struct RomWorker
 | 
						|
{
 | 
						|
	RTLIL::Module *module;
 | 
						|
	SigMap sigmap;
 | 
						|
 | 
						|
	int count = 0;
 | 
						|
 | 
						|
	RomWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
 | 
						|
 | 
						|
	void do_switch(RTLIL::SwitchRule *sw)
 | 
						|
	{
 | 
						|
		for (auto cs : sw->cases) {
 | 
						|
			do_case(cs);
 | 
						|
		}
 | 
						|
 | 
						|
		if (sw->cases.empty()) {
 | 
						|
			log_debug("rejecting switch: no cases\n");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// A switch can be converted into ROM when:
 | 
						|
		//
 | 
						|
		// 1. No case contains a nested switch
 | 
						|
		// 2. All cases have the same set of assigned signals
 | 
						|
		// 3. All right-hand values in cases are constants
 | 
						|
		// 4. All compare values used in cases are fully-defined constants
 | 
						|
		// 5. The cases must cover all possible values (possibly by using default case)
 | 
						|
 | 
						|
		SigSpec lhs;
 | 
						|
		dict<SigBit, int> lhs_lookup;
 | 
						|
		for (auto &it: sw->cases[0]->actions) {
 | 
						|
			for (auto bit: it.lhs) {
 | 
						|
				if (!lhs_lookup.count(bit)) {
 | 
						|
					lhs_lookup[bit] = GetSize(lhs);
 | 
						|
					lhs.append(bit);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (lhs.empty()) {
 | 
						|
			log_debug("rejecting switch: lhs empty\n");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		int swsigbits = 0;
 | 
						|
		for (int i = 0; i < GetSize(sw->signal); i++)
 | 
						|
			if (sw->signal[i] != State::S0)
 | 
						|
				swsigbits = i + 1;
 | 
						|
 | 
						|
		dict<int, Const> vals;
 | 
						|
		Const default_val;
 | 
						|
		bool got_default = false;
 | 
						|
		int maxaddr = 0;
 | 
						|
		for (auto cs : sw->cases) {
 | 
						|
			if (!cs->switches.empty()) {
 | 
						|
				log_debug("rejecting switch: has nested switches\n");
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			Const val = Const(State::Sm, GetSize(lhs));
 | 
						|
			for (auto &it: cs->actions) {
 | 
						|
				if (!it.rhs.is_fully_const()) {
 | 
						|
					log_debug("rejecting switch: rhs not const\n");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				for (int i = 0; i < GetSize(it.lhs); i++) {
 | 
						|
					auto it2 = lhs_lookup.find(it.lhs[i]);
 | 
						|
					if (it2 == lhs_lookup.end()) {
 | 
						|
						log_debug("rejecting switch: lhs not uniform\n");
 | 
						|
						return;
 | 
						|
					}
 | 
						|
					val.set(it2->second, it.rhs[i].data);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			for (auto bit: val) {
 | 
						|
				if (bit == State::Sm) {
 | 
						|
					log_debug("rejecting switch: lhs not uniform\n");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			for (auto &addr: cs->compare) {
 | 
						|
				if (!addr.is_fully_def()) {
 | 
						|
					log_debug("rejecting switch: case value has undef bits\n");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				Const c = addr.as_const();
 | 
						|
				while (GetSize(c) && c.back() == State::S0)
 | 
						|
					c.resize(c.size() - 1, State::S0);
 | 
						|
				if (GetSize(c) > swsigbits)
 | 
						|
					continue;
 | 
						|
				if (GetSize(c) > 30) {
 | 
						|
					log_debug("rejecting switch: address too large\n");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				int a = c.as_int();
 | 
						|
				if (vals.count(a))
 | 
						|
					continue;
 | 
						|
				vals[a] = val;
 | 
						|
				if (a > maxaddr)
 | 
						|
					maxaddr = a;
 | 
						|
			}
 | 
						|
			if (cs->compare.empty()) {
 | 
						|
				default_val = val;
 | 
						|
				got_default = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		int abits = ceil_log2(maxaddr + 1);
 | 
						|
		if (!got_default && (swsigbits > 30 || GetSize(vals) != (1 << swsigbits))) {
 | 
						|
			log_debug("rejecting switch: not all values are covered\n");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// TODO: better density heuristic?
 | 
						|
		if (GetSize(vals) < 8) {
 | 
						|
			log_debug("rejecting switch: not enough values\n");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		if ((1 << abits) / GetSize(vals) > 4) {
 | 
						|
			log_debug("rejecting switch: not enough density\n");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// Ok, let's do it.
 | 
						|
		SigSpec rdata = module->addWire(NEW_ID, GetSize(lhs));
 | 
						|
		Mem mem(module, NEW_ID, GetSize(lhs), 0, 1 << abits);
 | 
						|
		mem.attributes = sw->attributes;
 | 
						|
 | 
						|
		Const::Builder builder(mem.size * GetSize(lhs));
 | 
						|
		for (int i = 0; i < mem.size; i++) {
 | 
						|
			auto it = vals.find(i);
 | 
						|
			if (it == vals.end()) {
 | 
						|
				log_assert(got_default);
 | 
						|
				for (auto bit: default_val)
 | 
						|
					builder.push_back(bit);
 | 
						|
			} else {
 | 
						|
				for (auto bit: it->second)
 | 
						|
					builder.push_back(bit);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		MemInit init;
 | 
						|
		init.addr = 0;
 | 
						|
		init.data = builder.build();
 | 
						|
		init.en = Const(State::S1, GetSize(lhs));
 | 
						|
		mem.inits.push_back(std::move(init));
 | 
						|
 | 
						|
		MemRd rd;
 | 
						|
		rd.addr = sw->signal.extract(0, abits);
 | 
						|
		rd.data = rdata;
 | 
						|
		rd.init_value = Const(State::Sx, GetSize(lhs));
 | 
						|
		rd.arst_value = Const(State::Sx, GetSize(lhs));
 | 
						|
		rd.srst_value = Const(State::Sx, GetSize(lhs));
 | 
						|
		mem.rd_ports.push_back(std::move(rd));
 | 
						|
 | 
						|
		mem.emit();
 | 
						|
 | 
						|
		if (sw->has_attribute(ID::src)) {
 | 
						|
			mem.inits[0].cell->attributes[ID::src] = sw->attributes[ID::src];
 | 
						|
			mem.rd_ports[0].cell->attributes[ID::src] = sw->attributes[ID::src];
 | 
						|
		}
 | 
						|
 | 
						|
		for (auto cs: sw->cases)
 | 
						|
			delete cs;
 | 
						|
		sw->cases.clear();
 | 
						|
		sw->signal = sw->signal.extract(0, swsigbits);
 | 
						|
		Const action_src = mem.has_attribute(ID::src) ? mem.attributes[ID::src] : Const("");
 | 
						|
		if (abits == GetSize(sw->signal)) {
 | 
						|
			sw->signal = SigSpec();
 | 
						|
			RTLIL::CaseRule *cs = new RTLIL::CaseRule;
 | 
						|
			cs->actions.push_back({lhs, rdata, std::move(action_src)});
 | 
						|
			sw->cases.push_back(cs);
 | 
						|
		} else {
 | 
						|
			sw->signal = sw->signal.extract_end(abits);
 | 
						|
			RTLIL::CaseRule *cs = new RTLIL::CaseRule;
 | 
						|
			cs->compare.push_back(Const(State::S0, GetSize(sw->signal)));
 | 
						|
			cs->actions.push_back({lhs, rdata, action_src});
 | 
						|
			sw->cases.push_back(cs);
 | 
						|
			RTLIL::CaseRule *cs2 = new RTLIL::CaseRule;
 | 
						|
			cs2->actions.push_back({lhs, default_val, std::move(action_src)});
 | 
						|
			sw->cases.push_back(cs2);
 | 
						|
		}
 | 
						|
 | 
						|
		count += 1;
 | 
						|
	}
 | 
						|
 | 
						|
	void do_case(RTLIL::CaseRule *cs)
 | 
						|
	{
 | 
						|
		for (auto sw: cs->switches) {
 | 
						|
			do_switch(sw);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	void do_process(RTLIL::Process *pr)
 | 
						|
	{
 | 
						|
		do_case(&pr->root_case);
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
struct ProcRomPass : public Pass {
 | 
						|
	ProcRomPass() : Pass("proc_rom", "convert switches to ROMs") { }
 | 
						|
	void help() override
 | 
						|
	{
 | 
						|
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
						|
		log("\n");
 | 
						|
		log("    proc_rom [selection]\n");
 | 
						|
		log("\n");
 | 
						|
		log("This pass converts switches into read-only memories when appropriate.\n");
 | 
						|
		log("\n");
 | 
						|
	}
 | 
						|
	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 | 
						|
	{
 | 
						|
		int total_count = 0;
 | 
						|
		log_header(design, "Executing PROC_ROM pass (convert switches to ROMs).\n");
 | 
						|
 | 
						|
		extra_args(args, 1, design);
 | 
						|
 | 
						|
		for (auto mod : design->all_selected_modules()) {
 | 
						|
			RomWorker worker(mod);
 | 
						|
			for (auto proc : mod->selected_processes())
 | 
						|
				worker.do_process(proc);
 | 
						|
			total_count += worker.count;
 | 
						|
		}
 | 
						|
 | 
						|
		log("Converted %d switch%s.\n",
 | 
						|
		    total_count, total_count == 1 ? "" : "es");
 | 
						|
	}
 | 
						|
} ProcRomPass;
 | 
						|
 | 
						|
PRIVATE_NAMESPACE_END
 |