/* * This file is part of the flashrom project. * * Copyright (C) 2000 Silicon Integrated System Corporation * Copyright (C) 2004 Tyan Corp * Copyright (C) 2005-2008 coresystems GmbH * Copyright (C) 2008,2009 Carl-Daniel Hailfinger * Copyright (C) 2016 secunet Security Networks AG * (Written by Nico Huber for secunet) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #ifndef __LIBPAYLOAD__ #include #include #endif #include #include #include #include #include #include #if HAVE_UTSNAME == 1 #include #endif #include "flash.h" #include "flashchips.h" #include "programmer.h" #include "hwaccess.h" #include "chipdrivers.h" const char flashrom_version[] = FLASHROM_VERSION; const char *chip_to_probe = NULL; static const struct programmer_entry *programmer = NULL; static const char *programmer_param = NULL; /* * Programmers supporting multiple buses can have differing size limits on * each bus. Store the limits for each bus in a common struct. */ struct decode_sizes max_rom_decode; /* If nonzero, used as the start address of bottom-aligned flash. */ unsigned long flashbase; /* Is writing allowed with this programmer? */ int programmer_may_write; #define SHUTDOWN_MAXFN 32 static int shutdown_fn_count = 0; /** @private */ static struct shutdown_func_data { int (*func) (void *data); void *data; } shutdown_fn[SHUTDOWN_MAXFN]; /* Initialize to 0 to make sure nobody registers a shutdown function before * programmer init. */ static int may_register_shutdown = 0; /* Did we change something or was every erase/write skipped (if any)? */ static bool all_skipped = true; /* Register a function to be executed on programmer shutdown. * The advantage over atexit() is that you can supply a void pointer which will * be used as parameter to the registered function upon programmer shutdown. * This pointer can point to arbitrary data used by said function, e.g. undo * information for GPIO settings etc. If unneeded, set data=NULL. * Please note that the first (void *data) belongs to the function signature of * the function passed as first parameter. */ int register_shutdown(int (*function) (void *data), void *data) { if (shutdown_fn_count >= SHUTDOWN_MAXFN) { msg_perr("Tried to register more than %i shutdown functions.\n", SHUTDOWN_MAXFN); return 1; } if (!may_register_shutdown) { msg_perr("Tried to register a shutdown function before " "programmer init.\n"); return 1; } shutdown_fn[shutdown_fn_count].func = function; shutdown_fn[shutdown_fn_count].data = data; shutdown_fn_count++; return 0; } int register_chip_restore(chip_restore_fn_cb_t func, struct flashctx *flash, uint8_t status) { if (flash->chip_restore_fn_count >= MAX_CHIP_RESTORE_FUNCTIONS) { msg_perr("Tried to register more than %i chip restore" " functions.\n", MAX_CHIP_RESTORE_FUNCTIONS); return 1; } flash->chip_restore_fn[flash->chip_restore_fn_count].func = func; flash->chip_restore_fn[flash->chip_restore_fn_count].status = status; flash->chip_restore_fn_count++; return 0; } static int deregister_chip_restore(struct flashctx *flash) { int rc = 0; while (flash->chip_restore_fn_count > 0) { int i = --flash->chip_restore_fn_count; rc |= flash->chip_restore_fn[i].func( flash, flash->chip_restore_fn[i].status); } return rc; } int programmer_init(const struct programmer_entry *prog, const char *param) { int ret; if (prog == NULL) { msg_perr("Invalid programmer specified!\n"); return -1; } programmer = prog; /* Initialize all programmer specific data. */ /* Default to unlimited decode sizes. */ max_rom_decode = (const struct decode_sizes) { .parallel = 0xffffffff, .lpc = 0xffffffff, .fwh = 0xffffffff, .spi = 0xffffffff, }; /* Default to top aligned flash at 4 GB. */ flashbase = 0; /* Registering shutdown functions is now allowed. */ may_register_shutdown = 1; /* Default to allowing writes. Broken programmers set this to 0. */ programmer_may_write = 1; programmer_param = param; msg_pdbg("Initializing %s programmer\n", programmer->name); ret = programmer->init(); if (programmer_param && strlen(programmer_param)) { if (ret != 0) { /* It is quite possible that any unhandled programmer parameter would have been valid, * but an error in actual programmer init happened before the parameter was evaluated. */ msg_pwarn("Unhandled programmer parameters (possibly due to another failure): %s\n", programmer_param); } else { /* Actual programmer init was successful, but the user specified an invalid or unusable * (for the current programmer configuration) parameter. */ msg_perr("Unhandled programmer parameters: %s\n", programmer_param); msg_perr("Aborting.\n"); ret = ERROR_FATAL; } } return ret; } /** Calls registered shutdown functions and resets internal programmer-related variables. * Calling it is safe even without previous initialization, but further interactions with programmer support * require a call to programmer_init() (afterwards). * * @return The OR-ed result values of all shutdown functions (i.e. 0 on success). */ int programmer_shutdown(void) { int ret = 0; /* Registering shutdown functions is no longer allowed. */ may_register_shutdown = 0; while (shutdown_fn_count > 0) { int i = --shutdown_fn_count; ret |= shutdown_fn[i].func(shutdown_fn[i].data); } programmer_param = NULL; registered_master_count = 0; return ret; } void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len) { void *ret = programmer->map_flash_region(descr, phys_addr, len); msg_gspew("%s: mapping %s from 0x%0*" PRIxPTR " to 0x%0*" PRIxPTR "\n", __func__, descr, PRIxPTR_WIDTH, phys_addr, PRIxPTR_WIDTH, (uintptr_t) ret); return ret; } void programmer_unmap_flash_region(void *virt_addr, size_t len) { programmer->unmap_flash_region(virt_addr, len); msg_gspew("%s: unmapped 0x%0*" PRIxPTR "\n", __func__, PRIxPTR_WIDTH, (uintptr_t)virt_addr); } void chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) { flash->mst->par.chip_writeb(flash, val, addr); } void chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr) { flash->mst->par.chip_writew(flash, val, addr); } void chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr) { flash->mst->par.chip_writel(flash, val, addr); } void chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len) { flash->mst->par.chip_writen(flash, buf, addr, len); } uint8_t chip_readb(const struct flashctx *flash, const chipaddr addr) { return flash->mst->par.chip_readb(flash, addr); } uint16_t chip_readw(const struct flashctx *flash, const chipaddr addr) { return flash->mst->par.chip_readw(flash, addr); } uint32_t chip_readl(const struct flashctx *flash, const chipaddr addr) { return flash->mst->par.chip_readl(flash, addr); } void chip_readn(const struct flashctx *flash, uint8_t *buf, chipaddr addr, size_t len) { flash->mst->par.chip_readn(flash, buf, addr, len); } void programmer_delay(unsigned int usecs) { if (usecs > 0) programmer->delay(usecs); } int read_memmapped(struct flashctx *flash, uint8_t *buf, unsigned int start, int unsigned len) { chip_readn(flash, buf, flash->virtual_memory + start, len); return 0; } /* This is a somewhat hacked function similar in some ways to strtok(). * It will look for needle with a subsequent '=' in haystack, return a copy of * needle and remove everything from the first occurrence of needle to the next * delimiter from haystack. */ static char *extract_param(const char *const *haystack, const char *needle, const char *delim) { char *param_pos, *opt_pos, *rest; char *opt = NULL; int optlen; int needlelen; needlelen = strlen(needle); if (!needlelen) { msg_gerr("%s: empty needle! Please report a bug at " "flashrom@flashrom.org\n", __func__); return NULL; } /* No programmer parameters given. */ if (*haystack == NULL) return NULL; param_pos = strstr(*haystack, needle); do { if (!param_pos) return NULL; /* Needle followed by '='? */ if (param_pos[needlelen] == '=') { /* Beginning of the string? */ if (param_pos == *haystack) break; /* After a delimiter? */ if (strchr(delim, *(param_pos - 1))) break; } /* Continue searching. */ param_pos++; param_pos = strstr(param_pos, needle); } while (1); if (param_pos) { /* Get the string after needle and '='. */ opt_pos = param_pos + needlelen + 1; optlen = strcspn(opt_pos, delim); /* Return an empty string if the parameter was empty. */ opt = malloc(optlen + 1); if (!opt) { msg_gerr("Out of memory!\n"); exit(1); } strncpy(opt, opt_pos, optlen); opt[optlen] = '\0'; rest = opt_pos + optlen; /* Skip all delimiters after the current parameter. */ rest += strspn(rest, delim); memmove(param_pos, rest, strlen(rest) + 1); /* We could shrink haystack, but the effort is not worth it. */ } return opt; } char *extract_programmer_param(const char *param_name) { return extract_param(&programmer_param, param_name, ","); } static int check_block_eraser(const struct flashctx *flash, int k, int log) { struct block_eraser eraser = flash->chip->block_erasers[k]; if (!eraser.block_erase && !eraser.eraseblocks[0].count) { if (log) msg_cdbg("not defined. "); return 1; } if (!eraser.block_erase && eraser.eraseblocks[0].count) { if (log) msg_cdbg("eraseblock layout is known, but matching " "block erase function is not implemented. "); return 1; } if (eraser.block_erase && !eraser.eraseblocks[0].count) { if (log) msg_cdbg("block erase function found, but " "eraseblock layout is not defined. "); return 1; } // TODO: Once erase functions are annotated with allowed buses, check that as well. return 0; } /* Returns the number of well-defined erasers for a chip. */ static unsigned int count_usable_erasers(const struct flashctx *flash) { unsigned int usable_erasefunctions = 0; int k; for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { if (!check_block_eraser(flash, k, 0)) usable_erasefunctions++; } return usable_erasefunctions; } static int compare_range(const uint8_t *wantbuf, const uint8_t *havebuf, unsigned int start, unsigned int len) { int ret = 0, failcount = 0; unsigned int i; for (i = 0; i < len; i++) { if (wantbuf[i] != havebuf[i]) { /* Only print the first failure. */ if (!failcount++) msg_cerr("FAILED at 0x%08x! Expected=0x%02x, Found=0x%02x,", start + i, wantbuf[i], havebuf[i]); } } if (failcount) { msg_cerr(" failed byte count from 0x%08x-0x%08x: 0x%x\n", start, start + len - 1, failcount); ret = -1; } return ret; } /* start is an offset to the base address of the flash chip */ static int check_erased_range(struct flashctx *flash, unsigned int start, unsigned int len) { int ret; uint8_t *cmpbuf = malloc(len); const uint8_t erased_value = ERASED_VALUE(flash); if (!cmpbuf) { msg_gerr("Could not allocate memory!\n"); exit(1); } memset(cmpbuf, erased_value, len); ret = verify_range(flash, cmpbuf, start, len); free(cmpbuf); return ret; } /* * @cmpbuf buffer to compare against, cmpbuf[0] is expected to match the * flash content at location start * @start offset to the base address of the flash chip * @len length of the verified area * @return 0 for success, -1 for failure */ int verify_range(struct flashctx *flash, const uint8_t *cmpbuf, unsigned int start, unsigned int len) { if (!len) return -1; if (!flash->chip->read) { msg_cerr("ERROR: flashrom has no read function for this flash chip.\n"); return -1; } uint8_t *readbuf = malloc(len); if (!readbuf) { msg_gerr("Could not allocate memory!\n"); return -1; } int ret = 0; if (start + len > flash->chip->total_size * 1024) { msg_gerr("Error: %s called with start 0x%x + len 0x%x >" " total_size 0x%x\n", __func__, start, len, flash->chip->total_size * 1024); ret = -1; goto out_free; } ret = flash->chip->read(flash, readbuf, start, len); if (ret) { msg_gerr("Verification impossible because read failed " "at 0x%x (len 0x%x)\n", start, len); ret = -1; goto out_free; } ret = compare_range(cmpbuf, readbuf, start, len); out_free: free(readbuf); return ret; } /* Helper function for need_erase() that focuses on granularities of gran bytes. */ static int need_erase_gran_bytes(const uint8_t *have, const uint8_t *want, unsigned int len, unsigned int gran, const uint8_t erased_value) { unsigned int i, j, limit; for (j = 0; j < len / gran; j++) { limit = min (gran, len - j * gran); /* Are 'have' and 'want' identical? */ if (!memcmp(have + j * gran, want + j * gran, limit)) continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) if (have[j * gran + i] != erased_value) return 1; } return 0; } /* * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. * * Warning: This function assumes that @have and @want point to naturally * aligned regions. * * @have buffer with current content * @want buffer with desired content * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ int need_erase(const uint8_t *have, const uint8_t *want, unsigned int len, enum write_granularity gran, const uint8_t erased_value) { int result = 0; unsigned int i; switch (gran) { case write_gran_1bit: for (i = 0; i < len; i++) if ((have[i] & want[i]) != want[i]) { result = 1; break; } break; case write_gran_1byte: for (i = 0; i < len; i++) if ((have[i] != want[i]) && (have[i] != erased_value)) { result = 1; break; } break; case write_gran_128bytes: result = need_erase_gran_bytes(have, want, len, 128, erased_value); break; case write_gran_256bytes: result = need_erase_gran_bytes(have, want, len, 256, erased_value); break; case write_gran_264bytes: result = need_erase_gran_bytes(have, want, len, 264, erased_value); break; case write_gran_512bytes: result = need_erase_gran_bytes(have, want, len, 512, erased_value); break; case write_gran_528bytes: result = need_erase_gran_bytes(have, want, len, 528, erased_value); break; case write_gran_1024bytes: result = need_erase_gran_bytes(have, want, len, 1024, erased_value); break; case write_gran_1056bytes: result = need_erase_gran_bytes(have, want, len, 1056, erased_value); break; case write_gran_1byte_implicit_erase: /* Do not erase, handle content changes from anything->0xff by writing 0xff. */ result = 0; break; default: msg_cerr("%s: Unsupported granularity! Please report a bug at " "flashrom@flashrom.org\n", __func__); } return result; } /** * Check if the buffer @have needs to be programmed to get the content of @want. * If yes, return 1 and fill in first_start with the start address of the * write operation and first_len with the length of the first to-be-written * chunk. If not, return 0 and leave first_start and first_len undefined. * * Warning: This function assumes that @have and @want point to naturally * aligned regions. * * @have buffer with current content * @want buffer with desired content * @len length of the checked area * @gran write granularity (enum, not count) * @first_start offset of the first byte which needs to be written (passed in * value is increased by the offset of the first needed write * relative to have/want or unchanged if no write is needed) * @return length of the first contiguous area which needs to be written * 0 if no write is needed * * FIXME: This function needs a parameter which tells it about coalescing * in relation to the max write length of the programmer and the max write * length of the chip. */ static unsigned int get_next_write(const uint8_t *have, const uint8_t *want, unsigned int len, unsigned int *first_start, enum write_granularity gran) { int need_write = 0; unsigned int rel_start = 0, first_len = 0; unsigned int i, limit, stride; switch (gran) { case write_gran_1bit: case write_gran_1byte: case write_gran_1byte_implicit_erase: stride = 1; break; case write_gran_128bytes: stride = 128; break; case write_gran_256bytes: stride = 256; break; case write_gran_264bytes: stride = 264; break; case write_gran_512bytes: stride = 512; break; case write_gran_528bytes: stride = 528; break; case write_gran_1024bytes: stride = 1024; break; case write_gran_1056bytes: stride = 1056; break; default: msg_cerr("%s: Unsupported granularity! Please report a bug at " "flashrom@flashrom.org\n", __func__); /* Claim that no write was needed. A write with unknown * granularity is too dangerous to try. */ return 0; } for (i = 0; i < len / stride; i++) { limit = min(stride, len - i * stride); /* Are 'have' and 'want' identical? */ if (memcmp(have + i * stride, want + i * stride, limit)) { if (!need_write) { /* First location where have and want differ. */ need_write = 1; rel_start = i * stride; } } else { if (need_write) { /* First location where have and want * do not differ anymore. */ break; } } } if (need_write) first_len = min(i * stride - rel_start, len); *first_start += rel_start; return first_len; } /* Returns the number of busses commonly supported by the current programmer and flash chip where the latter * can not be completely accessed due to size/address limits of the programmer. */ unsigned int count_max_decode_exceedings(const struct flashctx *flash) { unsigned int limitexceeded = 0; uint32_t size = flash->chip->total_size * 1024; enum chipbustype buses = flash->mst->buses_supported & flash->chip->bustype; if ((buses & BUS_PARALLEL) && (max_rom_decode.parallel < size)) { limitexceeded++; msg_pdbg("Chip size %u kB is bigger than supported " "size %u kB of chipset/board/programmer " "for %s interface, " "probe/read/erase/write may fail. ", size / 1024,
/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  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/cellaigs.h"

YOSYS_NAMESPACE_BEGIN

AigNode::AigNode()
{
	portbit = -1;
	inverter = false;
	left_parent = -1;
	right_parent = -1;
}

bool AigNode::operator==(const AigNode &other) const
{
	if (portname != other.portname) return false;
	if (portbit != other.portbit) return false;
	if (inverter != other.inverter) return false;
	if (left_parent != other.left_parent) return false;
	if (right_parent != other.right_parent) return false;
	return true;
}

unsigned int AigNode::hash() const
{
	unsigned int h = mkhash_init;
	h = mkhash(portname.hash(), portbit);
	h = mkhash(h, inverter);
	h = mkhash(h, left_parent);
	h = mkhash(h, right_parent);
	return h;
}

bool Aig::operator==(const Aig &other) const
{
	return name == other.name;
}

unsigned int Aig::hash() const
{
	return hash_ops<std::string>::hash(name);
}

struct AigMaker
{
	Aig *aig;
	Cell *cell;
	idict<AigNode> aig_indices;

	int the_true_node;
	int the_false_node;

	AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
	{
		the_true_node = -1;
		the_false_node = -1;
	}

	int node2index(const AigNode &node)
	{
		if (node.left_parent > node.right_parent) {
			AigNode n(node);
			std::swap(n.left_parent, n.right_parent);
			return node2index(n);
		}

		if (!aig_indices.count(node)) {
			aig_indices.expect(node, GetSize(aig->nodes));
			aig->nodes.push_back(node);
		}

		return aig_indices.at(node);
	}

	int bool_node(bool value)
	{
		AigNode node;
		node.inverter = value;
		return node2index(node);
	}

	int inport(IdString portname, int portbit = 0, bool inverter = false)
	{
		if (portbit >= GetSize(cell->getPort(portname))) {
			if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool())
				return inport(portname, GetSize(cell->getPort(portname))-1, inverter);
			return bool_node(inverter);
		}

		AigNode node;
		node.portname = portname;
		node.portbit = portbit;
		node.inverter = inverter;
		return node2index(node);
	}

	vector<int> inport_vec(IdString portname, int width)
	{
		vector<int> vec;
		for (int i = 0; i < width; i++)
			vec.push_back(inport(portname, i));
		return vec;
	}

	int not_inport(IdString portname, int portbit = 0)
	{
		return inport(portname, portbit, true);
	}

	int not_gate(int A)
	{
		AigNode node(aig_indices[A]);
		node.outports.clear();
		node.inverter = !node.inverter;
		return node2index(node);
	}

	int and_gate(int A, int B, bool inverter = false)
	{
		if (A == B)
			return inverter ? not_gate(A) : A;

		const AigNode &nA = aig_indices[A];
		const AigNode &nB = aig_indices[B];

		AigNode nB_inv(nB);
		nB_inv.inverter = !nB_inv.inverter;

		if (nA == nB_inv)
			return bool_node(inverter);

		bool nA_bool = nA.portbit < 0 && nA.left_parent < 0 && nA.right_parent < 0;
		bool nB_bool = nB.portbit < 0 && nB.left_parent < 0 && nB.right_parent < 0;

		if (nA_bool && nB_bool) {
			bool bA = nA.inverter;
			bool bB = nB.inverter;
			return bool_node(inverter != (bA && bB));
		}

		if (nA_bool) {
			bool bA = nA.inverter;
			if (inverter)
				return bA ? not_gate(B) : bool_node(true);
			return bA ? B : bool_node(false);
		}

		if (nB_bool) {
			bool bB = nB.inverter;
			if (inverter)
				return bB ? not_gate(A) : bool_node(true);
			return bB ? A : bool_node(false);
		}

		AigNode node;
		node.inverter = inverter;
		node.left_parent = A;
		node.right_parent = B;
		return node2index(node);
	}

	int nand_gate(int A, int B)
	{
		return and_gate(A, B, true);
	}

	int or_gate(int A, int B)
	{
		return nand_gate(not_gate(A), not_gate(B));
	}

	int nor_gate(int A, int B)
	{
		return and_gate(not_gate(A), not_gate(B));
	}

	int xor_gate(int A, int B)
	{
		return nor_gate(and_gate(A, B), nor_gate(A, B));
	}

	int xnor_gate(int A, int B)
	{
		return or_gate(and_gate(A, B), nor_gate(A, B));
	}

	int mux_gate(int A, int B, int S)
	{
		return or_gate(and_gate(A, not_gate(S)), and_gate(B, S));
	}

	vector<int> adder(const vector<int> &A, const vector<int> &B, int carry, vector<int> *X = nullptr, vector<int> *CO = nullptr)
	{
		vector<int> Y(GetSize(A));
		log_assert(GetSize(A) == GetSize(B));
		for (int i = 0; i < GetSize(A); i++) {
			Y[i] = xor_gate(xor_gate(A[i], B[i]), carry);
			carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry));
			if (X != nullptr)
				X->at(i) = xor_gate(A[i], B[i]);
			if (CO != nullptr)
				CO->at(i) = carry;
		}
		return Y;
	}

	void outport(int node, IdString portname, int portbit = 0)
	{
		if (portbit < GetSize(cell->getPort(portname)))
			aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit));
	}

	void outport_bool(int node, IdString portname)
	{
		outport(node, portname);
		for (int i = 1; i < GetSize(cell->getPort(portname)); i++)
			outport(bool_node(false), portname, i);
	}

	void outport_vec(const vector<int> &vec, IdString portname)
	{
		for (int i = 0; i < GetSize(vec); i++)
			outport(vec.at(i), portname, i);
	}
};

Aig::Aig(Cell *cell)
{
	if (cell->type[0] != '$')
		return;

	AigMaker mk(this, cell);
	name = cell->type.str();

	string mkname_last;
	bool mkname_a_signed = false;
	bool mkname_b_signed = false;
	bool mkname_is_signed = false;

	cell->parameters.sort();
	for (auto p : cell->parameters)
	{
		if (p.first == "\\A_WIDTH" && mkname_a_signed) {
			name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U');
		} else if (p.first == "\\B_WIDTH" && mkname_b_signed) {
			name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U');
		} else {
			mkname_last = name;
			name += stringf(":%d", p.second.as_int());
		}

		mkname_a_signed = false;
		mkname_b_signed = false;
		mkname_is_signed = false;
		if (p.first == "\\A_SIGNED") {
			mkname_a_signed = true;
			mkname_is_signed = p.second.as_bool();
		}
		if (p.first == "\\B_SIGNED") {
			mkname_b_signed = true;
			mkname_is_signed = p.second.as_bool();
		}
	}

	if (cell->type.in("$not", "$_NOT_", "$pos", "$_BUF_"))
	{
		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
			int A = mk.inport("\\A", i);
			int Y = cell->type.in("$not", "$_NOT_") ? mk.not_gate(A) : A;
			mk.outport(Y, "\\Y", i);
		}
		goto optimize;
	}

	if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_"))
	{
		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
			int A = mk.inport("\\A", i);
			int B = mk.inport("\\B", i);
			int Y = cell->type.in("$and", "$_AND_")   ? mk.and_gate(A, B) :
			        cell->type.in("$_NAND_")          ? mk.nand_gate(A, B) :
			        cell->type.in("$or", "$_OR_")     ? mk.or_gate(A, B) :
			        cell->type.in("$_NOR_")           ? mk.nor_gate(A, B) :
			        cell->type.in("$xor", "$_XOR_")   ? mk.xor_gate(A, B) :
			        cell->type.in("$xnor", "$_XNOR_") ? mk.xnor_gate(A, B) : -1;
			mk.outport(Y, "\\Y", i);
		}
		goto optimize;
	}

	if (cell->type.in("$mux", "$_MUX_"))
	{
		int S = mk.inport("\\S");
		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
			int A = mk.inport("\\A", i);
			int B = mk.inport("\\B", i);
			int Y = mk.mux_gate(A, B, S);
			mk.outport(Y, "\\Y", i);
		}
		goto optimize;
	}

	if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool"))
	{
		int Y = mk.inport("\\A", 0);
		for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) {
			int A = mk.inport("\\A", i);
			if (cell->type == "$reduce_and")  Y = mk.and_gate(A, Y);
			if (cell->type == "$reduce_or")   Y = mk.or_gate(A, Y);
			if (cell->type == "$reduce_bool") Y = mk.or_gate(A, Y);
			if (cell->type == "$reduce_xor")  Y = mk.xor_gate(A, Y);
			if (cell->type == "$reduce_xnor") Y = mk.xor_gate(A, Y);
		}
		if (cell->type == "$reduce_xnor")
			Y = mk.not_gate(Y);
		mk.outport(Y, "\\Y", 0);
		for (int i = 1; i < GetSize(cell->getPort("\\Y")); i++)
			mk.outport(mk.bool_node(false), "\\Y", i);
		goto optimize;
	}

	if (cell->type.in("$logic_not", "$logic_and", "$logic_or"))
	{
		int A = mk.inport("\\A", 0), Y = -1;
		for (int i = 1; i < GetSize(cell->getPort("\\A")); i++)
			A = mk.or_gate(mk.inport("\\A", i), A);
		if (cell->type.in("$logic_and", "$logic_or")) {
			int B = mk.inport("\\B", 0);
			for (int i = 1; i < GetSize(cell->getPort("\\B")); i++)
				B = mk.or_gate(mk.inport("\\B", i), B);
			if (cell->type == "$logic_and") Y = mk.and_gate(A, B);
			if (cell->type == "$logic_or")  Y = mk.or_gate(A, B);
		} else {
			if (cell->type == "$logic_not") Y = mk.not_gate(A);
		}
		mk.outport_bool(Y, "\\Y");
		goto optimize;
	}

	if (cell->type.in("$add", "$sub"))
	{
		int width = GetSize(cell->getPort("\\Y"));
		vector<int> A = mk.inport_vec("\\A", width);
		vector<int> B = mk.inport_vec("\\B", width);
		int carry = mk.bool_node(false);
		if (cell->type == "$sub") {
			for (auto &n : B)
				n = mk.not_gate(n);
			carry = mk.not_gate(carry);
		}
		vector<int> Y = mk.adder(A, B, carry);
		mk.outport_vec(Y, "\\Y");
		goto optimize;
	}

	if (cell->type == "$alu")
	{
		int width = GetSize(cell->getPort("\\Y"));
		vector<int> A = mk.inport_vec("\\A", width);
		vector<int> B = mk.inport_vec("\\B", width);
		int carry = mk.inport("\\CI");
		int binv = mk.inport("\\BI");
		for (auto &n : B)
			n = mk.xor_gate(n, binv);
		vector<int> X(width), CO(width);
		vector<int> Y = mk.adder(A, B, carry, &X, &CO);
		for (int i = 0; i < width; i++)
			X[i] = mk.xor_gate(A[i], B[i]);
		mk.outport_vec(Y, "\\Y");
		mk.outport_vec(X, "\\X");
		mk.outport_vec(CO, "\\CO");
		goto optimize;
	}

	if (cell->type.in("$eq", "$ne"))
	{
		int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B")));
		vector<int> A = mk.inport_vec("\\A", width);
		vector<int> B = mk.inport_vec("\\B", width);
		int Y = mk.bool_node(false);
		for (int i = 0; i < width; i++)
			Y = mk.or_gate(Y, mk.xor_gate(A[i], B[i]));
		if (cell->type == "$eq")
			Y = mk.not_gate(Y);
		mk.outport_bool(Y, "\\Y");
		goto optimize;
	}

	if (cell->type == "$_AOI3_")
	{
		int A = mk.inport("\\A");
		int B = mk.inport("\\B");
		int C = mk.inport("\\C");
		int Y = mk.nor_gate(mk.and_gate(A, B), C);
		mk.outport(Y, "\\Y");
		goto optimize;
	}

	if (cell->type == "$_OAI3_")
	{
		int A = mk.inport("\\A");
		int B = mk.inport("\\B");
		int C = mk.inport("\\C");
		int Y = mk.nand_gate(mk.or_gate(A, B), C);
		mk.outport(Y, "\\Y");
		goto optimize;
	}

	if (cell->type == "$_AOI4_")
	{
		int A = mk.inport("\\A");
		int B = mk.inport("\\B");
		int C = mk.inport("\\C");
		int D = mk.inport("\\D");
		int Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D));
		mk.outport(Y, "\\Y");
		goto optimize;
	}

	if (cell->type == "$_OAI4_")
	{
		int A = mk.inport("\\A");
		int B = mk.inport("\\B");
		int C = mk.inport("\\C");
		int D = mk.inport("\\D");
		int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D));
		mk.outport(Y, "\\Y");
		goto optimize;
	}

	name.clear();
	return;

optimize:;
	pool<int> used_old_ids;
	vector<AigNode> new_nodes;
	dict<int, int> old_to_new_ids;
	old_to_new_ids[-1] = -1;

	for (int i = GetSize(nodes)-1; i >= 0; i--) {
		if (!nodes[i].outports.empty())
			used_old_ids.insert(i);
		if (!used_old_ids.count(i))
			continue;
		if (nodes[i].left_parent >= 0)
			used_old_ids.insert(nodes[i].left_parent);
		if (nodes[i].right_parent >= 0)
			used_old_ids.insert(nodes[i].right_parent);
	}

	for (int i = 0; i < GetSize(nodes); i++) {
		if (!used_old_ids.count(i))
			continue;
		nodes[i].left_parent = old_to_new_ids.at(nodes[i].left_parent);
		nodes[i].right_parent = old_to_new_ids.at(nodes[i].right_parent);
		old_to_new_ids[i] = GetSize(new_nodes);
		new_nodes.push_back(nodes[i]);
	}

	new_nodes.swap(nodes);
}

YOSYS_NAMESPACE_END