/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2021 Symbiflow Authors * * 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 "luts.h" #include "log.h" #include "nextpnr.h" #include "site_lut_mapping_cache.h" //#define DEBUG_LUT_ROTATION NEXTPNR_NAMESPACE_BEGIN bool rotate_and_merge_lut_equation(std::vector *result, const LutBel &lut_bel, const DynamicBitarray<> &old_equation, const std::vector &pin_map, uint32_t used_pins) { // pin_map maps pin indicies from the old pin to the new pin. // So a reversal of a LUT4 would have a pin map of: // pin_map[0] = 3; // pin_map[1] = 2; // pin_map[2] = 1; // pin_map[3] = 0; size_t bel_width = 1 << lut_bel.pins.size(); for (size_t bel_address = 0; bel_address < bel_width; ++bel_address) { bool address_reachable = true; size_t cell_address = 0; for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { // This address line is 0, so don't translate this bit to the cell // address. if ((bel_address & (1 << bel_pin_idx)) == 0) { // This pin is unused, so the line will be tied high, this // address is unreachable. // // FIXME: The assumption is that unused pins are tied VCC. // This is not generally true. // // Use Arch::prefered_constant_net_type to determine what // constant net should be used for unused pins. if ((used_pins & (1 << bel_pin_idx)) == 0) { address_reachable = false; break; } continue; } auto cell_pin_idx = pin_map[bel_pin_idx]; // Is this BEL pin used for this cell? if (cell_pin_idx < 0) { // This BEL pin is not used for the LUT cell, skip continue; } cell_address |= (1 << cell_pin_idx); } if (!address_reachable) { continue; } bel_address += lut_bel.low_bit; if (old_equation.get(cell_address)) { if ((*result)[bel_address] == LL_Zero) { // Output equation has a conflict! return false; } (*result)[bel_address] = LL_One; } else { if ((*result)[bel_address] == LL_One) { // Output equation has a conflict! return false; } (*result)[bel_address] = LL_Zero; } } return true; } static constexpr bool kCheckOutputEquation = true; struct LutPin { struct LutPinUser { size_t cell_idx; size_t cell_pin_idx; }; const NetInfo *net = nullptr; std::vector users; int32_t min_pin = -1; int32_t max_pin = -1; void add_user(const LutBel &lut_bel, size_t cell_idx, size_t cell_pin_idx) { if (min_pin < 0) { min_pin = lut_bel.min_pin; max_pin = lut_bel.max_pin; } min_pin = std::max(min_pin, lut_bel.min_pin); max_pin = std::min(max_pin, lut_bel.max_pin); users.emplace_back(); users.back().cell_idx = cell_idx; users.back().cell_pin_idx = cell_pin_idx; } bool operator<(const LutPin &other) const { return max_pin < other.max_pin; } }; uint32_t LutMapper::check_wires(const Context *ctx) const { // Unlike the 3 argument version of check_wires, this version needs to // calculate following data based on current cell pin mapping, etc: // // - Index map from bel pins to cell pins, -1 for unmapped // - Mask of used pins // - Vector of unused LUT BELs. uint32_t used_pins = 0; std::vector> bel_to_cell_pin_remaps; std::vector lut_bels; bel_to_cell_pin_remaps.resize(cells.size()); lut_bels.resize(cells.size()); for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { const CellInfo *cell = cells[cell_idx]; auto &bel_data = bel_info(ctx->chip_info, cell->bel); IdString bel_name(bel_data.name); auto &lut_bel = element.lut_bels.at(bel_name); lut_bels[cell_idx] = &lut_bel; bel_to_cell_pin_remaps[cell_idx].resize(lut_bel.pins.size(), -1); for (size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { IdString lut_cell_pin = cell->lut_cell.pins[pin_idx]; const std::vector bel_pins = cell->cell_bel_pins.at(lut_cell_pin); NPNR_ASSERT(bel_pins.size() == 1); size_t bel_pin_idx = lut_bel.pin_to_index.at(bel_pins[0]); bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] = pin_idx; used_pins |= (1 << bel_pin_idx); } } pool blocked_luts; return check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, &blocked_luts); } uint32_t LutMapper::check_wires(const std::vector> &bel_to_cell_pin_remaps, const std::vector &lut_bels, uint32_t used_pins, pool *blocked_luts) const { std::vector unused_luts; for (auto &lut_bel_pair : element.lut_bels) { if (std::find(lut_bels.begin(), lut_bels.end(), &lut_bel_pair.second) == lut_bels.end()) { unused_luts.push_back(&lut_bel_pair.second); blocked_luts->emplace(&lut_bel_pair.second); } } // FIXME: The assumption is that unused pins are tied VCC. // This is not generally true. // // Use Arch::prefered_constant_net_type to determine what // constant net should be used for unused pins. uint32_t vcc_mask = 0; DynamicBitarray<> wire_equation; wire_equation.resize(2); wire_equation.set(0, false); wire_equation.set(1, true); std::vector wire_bel_to_cell_pin_map; std::vector equation_result; for (int32_t pin_idx = 0; pin_idx < (int32_t)element.pins.size(); ++pin_idx) { if (used_pins & (1 << pin_idx)) { // This pin is already used, so it cannot be used for a wire. continue; } bool valid_pin_for_wire = false; bool invalid_pin_for_wire = false; for (const LutBel *lut_bel : unused_luts) { if (pin_idx < lut_bel->min_pin) { continue; } if (pin_idx > lut_bel->max_pin) { continue; } wire_bel_to_cell_pin_map.clear(); wire_bel_to_cell_pin_map.resize(lut_bel->pins.size(), -1); wire_bel_to_cell_pin_map[lut_bel->pin_to_index.at(element.pins[pin_idx])] = 0; equation_result.clear(); equation_result.resize(element.width, LL_DontCare); uint32_t used_pins_with_wire = used_pins | (1 << pin_idx); for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { const CellInfo *cell = cells[cell_idx]; auto &lut_bel_for_cell = *lut_bels[cell_idx]; if (!rotate_and_merge_lut_equation(&equation_result, lut_bel_for_cell, cell->lut_cell.equation, bel_to_cell_pin_remaps[cell_idx], used_pins_with_wire)) { invalid_pin_for_wire = true; break; } } if (invalid_pin_for_wire) { break; } if (rotate_and_merge_lut_equation(&equation_result, *lut_bel, wire_equation, wire_bel_to_cell_pin_map, used_pins_with_wire)) { valid_pin_for_wire = true; blocked_luts->erase(lut_bel); } } bool good_for_wire = valid_pin_for_wire && !invalid_pin_for_wire; if (!good_for_wire) { vcc_mask |= (1 << pin_idx); } } return vcc_mask; } bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping, pool *blocked_luts) { dict lut_pin_map; std::vector lut_bels; lut_bels.resize(cells.size()); for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { const CellInfo *cell = cells[cell_idx]; #ifdef DEBUG_LUT_ROTATION log_info("Mapping %s %s eq = %s at %s\n", cell->type.c_str(ctx), cell->name.c_str(ctx), cell->params.at(ctx->id("INIT")).c_str(), ctx->nameOfBel(cell->bel)); #endif auto &bel_data = bel_info(ctx->chip_info, cell->bel); IdString bel_name(bel_data.name); auto &lut_bel = element.lut_bels.at(bel_name); lut_bels[cell_idx] = &lut_bel; for (size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { IdString lut_pin_name = cell->lut_cell.pins[pin_idx]; const PortInfo &port_info = cell->ports.at(lut_pin_name); NPNR_ASSERT(port_info.net != nullptr); auto result = lut_pin_map.emplace(port_info.net, LutPin()); LutPin &lut_pin = result.first->second; lut_pin.net = port_info.net; lut_pin.add_user(lut_bel, cell_idx, pin_idx); } } if (lut_pin_map.size() > element.pins.size()) { // Trival conflict, more nets entering element than pins are // available! #ifdef DEBUG_LUT_ROTATION log_info("Trival failure %zu > %zu, %zu %zu\n", lut_pin_map.size(), element.pins.size(), element.width, element.lut_bels.size()); #endif return false; } std::vector lut_pins; lut_pins.reserve(lut_pin_map.size()); for (auto lut_pin_pair : lut_pin_map) { lut_pins.push_back(std::move(lut_pin_pair.second)); } lut_pin_map.clear(); std::sort(lut_pins.begin(), lut_pins.end()); std::vector> cell_to_bel_pin_remaps; std::vector> bel_to_cell_pin_remaps; cell_to_bel_pin_remaps.resize(cells.size()); bel_to_cel
/*
 *  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.
 *
 *  ---
 *
 *  The Verilog frontend.
 *
 *  This frontend is using the AST frontend library (see frontends/ast/).
 *  Thus this frontend does not generate RTLIL code directly but creates an
 *  AST directly from the Verilog parse tree and then passes this AST to
 *  the AST frontend library.
 *
 *  ---
 *
 *  This file contains an ad-hoc parser for Verilog constants. The Verilog
 *  lexer does only recognize a constant but does not actually split it to its
 *  components. I.e. it just passes the Verilog code for the constant to the
 *  bison parser. The parser then uses the function const2ast() from this file
 *  to create an AST node for the constant.
 *
 */

#include "verilog_frontend.h"
#include "kernel/log.h"
#include <string.h>
#include <math.h>

YOSYS_NAMESPACE_BEGIN

using namespace AST;

// divide an arbitrary length decimal number by two and return the rest
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
{
	int carry = 0;
	for (size_t i = 0; i < digits.size(); i++) {
		if (digits[i] >= 10)
			log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
				current_filename.c_str(), get_line_num());
		digits[i] += carry * 10;
		carry = digits[i] % 2;
		digits[i] /= 2;
	}
	while (!digits.empty() && !digits.front())
		digits.erase(digits.begin());
	return carry;
}

// find the number of significant bits in a binary number (not including the sign bit)
static int my_ilog2(int x)
{
	int ret = 0;
	while (x != 0 && x != -1) {
		x = x >> 1;
		ret++;
	}
	return ret;
}

// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
{
	// all digits in string (MSB at index 0)
	std::vector<uint8_t> digits;

	while (*str) {
		if ('0' <= *str && *str <= '9')
			digits.push_back(*str - '0');
		else if ('a' <= *str && *str <= 'f')
			digits.push_back(10 + *str - 'a');
		else if ('A' <= *str && *str <= 'F')
			digits.push_back(10 + *str - 'A');
		else if (*str == 'x' || *str == 'X')
			digits.push_back(0xf0);
		else if (*str == 'z' || *str == 'Z')
			digits.push_back(0xf1);
		else if (*str == '?')
			digits.push_back(0xf2);
		str++;
	}

	if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
		base = 2;

	data.clear();

	if (base == 10) {
		while (!digits.empty())
			data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
	} else {
		int bits_per_digit = my_ilog2(base-1);
		for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
			if (*it > (base-1) && *it < 0xf0)
				log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n",
					base-1, base, current_filename.c_str(), get_line_num());
			for (int i = 0; i < bits_per_digit; i++) {
				int bitmask = 1 << i;
				if (*it == 0xf0)
					data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
				else if (*it == 0xf1)
					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
				else if (*it == 0xf2)
					data.push_back(RTLIL::Sa);
				else
					data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0);
			}
		}
	}

	int len = GetSize(data);
	RTLIL::State msb = data.empty() ? RTLIL::S0 : data.back();

	if (len_in_bits < 0) {
		if (len < 32)
			data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb);
		return;
	}

	for (len = len - 1; len >= 0; len--)
		if (data[len] == RTLIL::S1)
			break;
	if (msb == RTLIL::S0 || msb == RTLIL::S1) {
		len += 1;
		data.resize(len_in_bits, RTLIL::S0);
	} else {
		len += 2;
		data.resize(len_in_bits, msb);
	}

	if (len > len_in_bits)
		log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
			len_in_bits, len, current_filename.c_str(), get_line_num());
}

// convert the Verilog code for a constant to an AST node
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
{
	if (warn_z) {
		AstNode *ret = const2ast(code, case_type);
		if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
			log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
				current_filename.c_str(), get_line_num());
		return ret;
	}

	const char *str = code.c_str();

	// Strings
	if (*str == '"') {
		int len = strlen(str) - 2;
		std::vector<RTLIL::State> data;
		data.reserve(len * 8);
		for (int i = 0; i < len; i++) {
			unsigned char ch = str[len - i];
			for (int j = 0; j < 8; j++) {
				data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
				ch = ch >> 1;
			}
		}
		AstNode *ast = AstNode::mkconst_bits(data, false);
		ast->str = code;
		return ast;
	}

	for (size_t i = 0; i < code.size(); i++)
		if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
			code.erase(code.begin()+(i--));
	str = code.c_str();

	char *endptr;
	long len_in_bits = strtol(str, &endptr, 10);

	// Simple base-10 integer
	if (*endptr == 0) {
		std::vector<RTLIL::State> data;
		my_strtobin(data, str, -1, 10, case_type);
		if (data.back() == RTLIL::S1)
			data.push_back(RTLIL::S0);
		return AstNode::mkconst_bits(data, true);
	}

	// unsized constant
	if (str == endptr)
		len_in_bits = -1;

	// The "<bits>'s?[bodhBODH]<digits>" syntax
	if (*endptr == '\'')
	{
		std::vector<RTLIL::State> data;
		bool is_signed = false;
		if (*(endptr+1) == 's') {
			is_signed = true;
			endptr++;
		}
		switch (*(endptr+1))
		{
		case 'b':
		case 'B':
			my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
			break;
		case 'o':
		case 'O':
			my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
			break;
		case 'd':
		case 'D':
			my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
			break;
		case 'h':
		case 'H':
			my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
			break;
		default:
			return NULL;
		}
		if (len_in_bits < 0) {
			if (is_signed && data.back() == RTLIL::S1)
				data.push_back(RTLIL::S0);
		}
		return AstNode::mkconst_bits(data, is_signed);
	}

	return NULL;
}

YOSYS_NAMESPACE_END