/* * This file is part of the flashrom project. * * Copyright (C) 2011 Sven Schnelle * * 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; version 2 of the License. * * 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 #include #include #include #include #include #include #include #include #include #include "flash.h" #include "chipdrivers.h" #include "programmer.h" #include "spi.h" /* * Linux versions prior to v4.14-rc7 may need linux/ioctl.h included here due * to missing from linux/spi/spidev.h. This was fixed in the following commit: * a2b4a79b88b2 spi: uapi: spidev: add missing ioctl header */ #include #include /* Devices known to work with this module (FIXME: export as struct dev_entry): * Beagle Bone Black * Raspberry Pi * HummingBoard */ #define BUF_SIZE_FROM_SYSFS "/sys/module/spidev/parameters/bufsiz" struct linux_spi_data { int fd; size_t max_kernel_buf_size; }; static int linux_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) { struct linux_spi_data *spi_data = flash->mst->spi.data; /* Older kernels use a single buffer for combined input and output data. So account for longest possible command + address, too. */ return spi_read_chunked(flash, buf, start, len, spi_data->max_kernel_buf_size - 5); } static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) { struct linux_spi_data *spi_data = flash->mst->spi.data; /* 5 bytes must be reserved for longest possible command + address. */ return spi_write_chunked(flash, buf, start, len, spi_data->max_kernel_buf_size - 5); } static int linux_spi_shutdown(void *data) { struct linux_spi_data *spi_data = data; close(spi_data->fd); free(spi_data); return 0; } static int linux_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *txbuf, unsigned char *rxbuf) { struct linux_spi_data *spi_data = flash->mst->spi.data; int iocontrol_code; struct spi_ioc_transfer msg[2] = { { .tx_buf = (uint64_t)(uintptr_t)txbuf, .len = writecnt, }, { .rx_buf = (uint64_t)(uintptr_t)rxbuf, .len = readcnt, }, }; if (spi_data->fd == -1) return -1; /* The implementation currently does not support requests that don't start with sending a command. */ if (writecnt == 0) return SPI_INVALID_LENGTH; /* Just submit the first (write) request in case there is nothing to read. Otherwise submit both requests. */ if (readcnt == 0) iocontrol_code = SPI_IOC_MESSAGE(1); else iocontrol_code = SPI_IOC_MESSAGE(2); if (ioctl(spi_data->fd, iocontrol_code, msg) == -1) { msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); return -1; } return 0; } static const struct spi_master spi_master_linux = { .features = SPI_MASTER_4BA, .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */ .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */ .command = linux_spi_send_command, .multicommand = default_spi_send_multicommand, .read = linux_spi_read, .write_256 = linux_spi_write_256, .write_aai = default_spi_write_aai, .shutdown = linux_spi_shutdown, .probe_opcode = default_spi_probe_opcode, }; /* Read max buffer size from sysfs, or use page size as fallback. */ static size_t get_max_kernel_buf_size() { size_t result = 0; FILE *fp; fp = fopen(BUF_SIZE_FROM_SYSFS, "r"); if (!fp) { msg_pwarn("Cannot open %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); goto out; } char buf[10]; if (!fgets(buf, sizeof(buf), fp)) { if (feof(fp)) msg_pwarn("Cannot read %s: file is empty.\n", BUF_SIZE_FROM_SYSFS); else msg_pwarn("Cannot read %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); goto out; } long int tmp; errno = 0; tmp = strtol(buf, NULL, 0); if ((tmp < 0) || errno) { msg_pwarn("Buffer size %ld from %s seems wrong.\n", tmp, BUF_SIZE_FROM_SYSFS); } else { msg_pdbg("%s: Using value from %s as max buffer size.\n", __func__, BUF_SIZE_FROM_SYSFS); result = (size_t)tmp; } out: if (fp) fclose(fp); if (!result) { msg_pdbg("%s: Using page size as max buffer size.\n", __func__); result = (size_t)getpagesize(); } return result; } static int linux_spi_init(const struct programmer_cfg *cfg) { char *param_str, *endp; uint32_t speed_hz = 2 * 1000 * 1000; /* FIXME: make the following configurable by CLI options. */ /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */ const uint8_t mode = SPI_MODE_0; const uint8_t bits = 8; int fd; size_t max_kernel_buf_size; struct linux_spi_data *spi_data; param_str = extract_programmer_param_str(cfg, "spispeed"); if (param_str && strlen(param_str)) { speed_hz = (uint32_t)strtoul(param_str, &endp, 10) * 1000; if (param_str == endp || speed_hz == 0) { msg_perr("%s: invalid clock: %s kHz\n", __func__, param_str); free(param_str); return 1; } } else { msg_pinfo("Using default %"PRIu32 "kHz clock. Use 'spispeed' parameter to override.\n", speed_hz / 1000); } free(param_str); param_str = extract_programmer_param_str(cfg, "dev"); if (!param_str || !strlen(param_str)) { msg_perr("No SPI device given. Use flashrom -p " "linux_spi:dev=/dev/spidevX.Y\n"); free(param_str); return 1; } msg_pdbg("Using device %s\n", param_str); if ((fd = open(param_str, O_RDWR)) == -1) { msg_perr("%s: failed to open %s: %s\n", __func__, param_str, strerror(errno)); free(param_str); return 1; } free(param_str); if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) { msg_perr("%s: failed to set speed to %"PRIu32"Hz: %s\n", __func__, speed_hz, strerror(errno)); goto init_err; } msg_pdbg("Using %"PRIu32"kHz clock\n", speed_hz / 1000); if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n", __func__, mode, strerror(errno)); goto init_err; } if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) { msg_perr("%s: failed to set the number of bits per SPI word to %u: %s\n", __func__, bits == 0 ? 8 : bits, strerror(errno)); goto init_err; } max_kernel_buf_size = get_max_kernel_buf_size(); msg_pdbg("%s: max_kernel_buf_size: %zu\n", __func__, max_kernel_buf_size); spi_data = calloc(1, sizeof(*spi_data)); if (!spi_data) { msg_perr("Unable to allocated space for SPI master data\n"); goto init_err; } spi_data->fd = fd; spi_data->max_kernel_buf_size = max_kernel_buf_size; return register_spi_master(&spi_master_linux, spi_data); init_err: close(fd); return 1; } const struct programmer_entry programmer_linux_spi = { .name = "linux_spi", .type = OTHER, .devs.note = "Device files /dev/spidev*.*\n", .init = linux_spi_init, .map_flash_region = fallb
/*
 *  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/yosys.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

int autoname_worker(Module *module)
{
	dict<Cell*, pair<int, IdString>> proposed_cell_names;
	dict<Wire*, pair<int, IdString>> proposed_wire_names;
	dict<Wire*, int> wire_score;
	int best_score = -1;

	for (auto cell : module->selected_cells())
	for (auto &conn : cell->connections())
	for (auto bit : conn.second)
		if (bit.wire != nullptr)
			wire_score[bit.wire]++;

	for (auto cell : module->selected_cells()) {
		if (cell->name[0] == '$') {
			for (auto &conn : cell->connections()) {
				string suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
				for (auto bit : conn.second)
					if (bit.wire != nullptr && bit.wire->name[0] != '$') {
						IdString new_name(bit.wire->name.str() + suffix);
						int score = wire_score.at(bit.wire);
						if (cell->output(conn.first)) score = 0;
						score = 10000*score + new_name.size();
						if (!proposed_cell_names.count(cell) || score < proposed_cell_names.at(cell).first) {
							if (best_score < 0 || score < best_score)
								best_score = score;
							proposed_cell_names[cell] = make_pair(score, new_name);
						}
					}
			}
		} else {
			for (auto &conn : cell->connections()) {
				string suffix = stringf("_%s", log_id(conn.first));
				for (auto bit : conn.second)
					if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
						IdString new_name(cell->name.str() + suffix);
						int score = wire_score.at(bit.wire);
						if (cell->output(conn.first)) score = 0;
						score = 10000*score + new_name.size();
						if (!proposed_wire_names.count(bit.wire) || score < proposed_wire_names.at(bit.wire).first) {
							if (best_score < 0 || score < best_score)
								best_score = score;
							proposed_wire_names[bit.wire] = make_pair(score, new_name);
						}
					}
			}
		}
	}

	for (auto &it : proposed_cell_names) {
		if (best_score*2 < it.second.first)
			continue;
		IdString n = module->uniquify(it.second.second);
		log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
		module->rename(it.first, n);
	}

	for (auto &it : proposed_wire_names) {
		if (best_score*2 < it.second.first)
			continue;
		IdString n = module->uniquify(it.second.second);
		log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
		module->rename(it.first, n);
	}

	return proposed_cell_names.size() + proposed_wire_names.size();
}

struct AutonamePass : public Pass {
	AutonamePass() : Pass("autoname", "automatically assign names to objects") { }
	void help() override
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    autoname [selection]\n");
		log("\n");
		log("Assign auto-generated public names to objects with private names (the ones\n");
		log("with $-prefix).\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) override
	{
		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-foo") {
			// 	foo = true;
			// 	continue;
			// }
			break;
		}

		log_header(design, "Executing AUTONAME pass.\n");

		for (auto module : design->selected_modules())
		{
			int count = 0, iter = 0;
			while (1) {
				iter++;
				int n = autoname_worker(module);
				if (!n) break;
				count += n;
			}
			if (count > 0)
				log("Renamed %d objects in module %s (%d iterations).\n", count, log_id(module), iter);
		}
	}
} AutonamePass;

PRIVATE_NAMESPACE_END