aboutsummaryrefslogtreecommitdiffstats
path: root/backends/blif/blif.cc
blob: 0db5ff27c48a43bed19032a9694c184ea17ccd5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
Copyright 2014 Jun WAKO <wakojun@gmail.com>
*/
#ifndef IBM4704_H
#define IBM4704_H

#define IBM4704_ERR_NONE        0
#define IBM4704_ERR_PARITY      0x70


void ibm4704_init(void);
uint8_t ibm4704_send(uint8_t data);
uint8_t ibm4704_recv_response(void);
uint8_t ibm4704_recv(void);


/* Check pin configuration */
#if !(defined(IBM4704_CLOCK_PORT) && \
      defined(IBM4704_CLOCK_PIN) && \
      defined(IBM4704_CLOCK_DDR) && \
      defined(IBM4704_CLOCK_BIT))
#   error "ibm4704 clock pin configuration is required in config.h"
#endif

#if !(defined(IBM4704_DATA_PORT) && \
      defined(IBM4704_DATA_PIN) && \
      defined(IBM4704_DATA_DDR) && \
      defined(IBM4704_DATA_BIT))
#   error "ibm4704 data pin configuration is required in config.h"
#endif


/*--------------------------------------------------------------------
 * static functions
 *------------------------------------------------------------------*/
static inline void clock_lo(void)
{
    IBM4704_CLOCK_PORT &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_DDR  |=  (1<<IBM4704_CLOCK_BIT);
}
static inline void clock_hi(void)
{
    /* input with pull up */
    IBM4704_CLOCK_DDR  &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_PORT |=  (1<<IBM4704_CLOCK_BIT);
}
static inline bool clock_in(void)
{
    IBM4704_CLOCK_DDR  &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_PORT |=  (1<<IBM4704_CLOCK_BIT);
    _delay_us(1);
    return IBM4704_CLOCK_PIN&(1<<IBM4704_CLOCK_BIT);
}
static inline void data_lo(void)
{
    IBM4704_DATA_PORT &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_DDR  |=  (1<<IBM4704_DATA_BIT);
}
static inline void data_hi(void)
{
    /* input with pull up */
    IBM4704_DATA_DDR  &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_PORT |=  (1<<IBM4704_DATA_BIT);
}
static inline bool data_in(void)
{
    IBM4704_DATA_DDR  &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_PORT |=  (1<<IBM4704_DATA_BIT);
    _delay_us(1);
    return IBM4704_DATA_PIN&(1<<IBM4704_DATA_BIT);
}

static inline uint16_t wait_clock_lo(uint16_t us)
{
    while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_clock_hi(uint16_t us)
{
    while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_data_lo(uint16_t us)
{
    while (data_in() && us)  { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_data_hi(uint16_t us)
{
    while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
    return us;
}

/* idle state that device can send */
static inline void idle(void)
{
    clock_hi();
    data_hi();
}

/* inhibit device to send 
 * keyboard checks Data line on start bit(Data:hi) and it stops sending if Data line is low.
 */
static inline void inhibit(void)
{
    clock_hi();
    data_lo();
}

#endif
a> 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
/*
 *  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.
 *
 */

// [[CITE]] Berkeley Logic Interchange Format (BLIF)
// University of California. Berkeley. July 28, 1992
// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf

#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include <string>

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct BlifDumperConfig
{
	bool icells_mode;
	bool conn_mode;
	bool impltf_mode;
	bool gates_mode;
	bool cname_mode;
	bool iname_mode;
	bool param_mode;
	bool attr_mode;
	bool iattr_mode;
	bool blackbox_mode;
	bool noalias_mode;

	std::string buf_type, buf_in, buf_out;
	std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types;
	std::string true_type, true_out, false_type, false_out, undef_type, undef_out;

	BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
			cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false),
			blackbox_mode(false), noalias_mode(false) { }
};

struct BlifDumper
{
	std::ostream &f;
	RTLIL::Module *module;
	RTLIL::Design *design;
	BlifDumperConfig *config;
	CellTypes ct;

	SigMap sigmap;
	dict<SigBit, int> init_bits;

	BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) :
			f(f), module(module), design(design), config(config), ct(design), sigmap(module)
	{
		for (Wire *wire : module->wires())
			if (wire->attributes.count("\\init")) {
				SigSpec initsig = sigmap(wire);
				Const initval = wire->attributes.at("\\init");
				for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
					switch (initval[i]) {
						case State::S0:
							init_bits[initsig[i]] = 0;
							break;
						case State::S1:
							init_bits[initsig[i]] = 1;
							break;
						default:
							break;
					}
			}
	}

	vector<shared_str> cstr_buf;
	pool<SigBit> cstr_bits_seen;

	const char *cstr(RTLIL::IdString id)
	{
		std::string str = RTLIL::unescape_id(id);
		for (size_t i = 0; i < str.size(); i++)
			if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
				str[i] = '?';
		cstr_buf.push_back(str);
		return cstr_buf.back().c_str();
	}

	const char *cstr(RTLIL::SigBit sig)
	{
		cstr_bits_seen.insert(sig);

		if (sig.wire == NULL) {
			if (sig == RTLIL::State::S0) return config->false_type == "-" || config->false_type == "+" ? config->false_out.c_str() : "$false";
			if (sig == RTLIL::State::S1) return config->true_type == "-" || config->true_type == "+" ? config->true_out.c_str() : "$true";
			return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef";
		}

		std::string str = RTLIL::unescape_id(sig.wire->name);
		for (size_t i = 0; i < str.size(); i++)
			if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
				str[i] = '?';

		if (sig.wire->width != 1)
			str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset);

		cstr_buf.push_back(str);
		return cstr_buf.back().c_str();
	}

	const char *cstr_init(RTLIL::SigBit sig)
	{
		sigmap.apply(sig);

		if (init_bits.count(sig) == 0)
			return " 2";

		string str = stringf(" %d", init_bits.at(sig));

		cstr_buf.push_back(str);
		return cstr_buf.back().c_str();
	}

	const char *subckt_or_gate(std::string cell_type)
	{
		if (!config->gates_mode)
			return "subckt";
		if (!design->modules_.count(RTLIL::escape_id(cell_type)))
			return "gate";
		if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
			return "gate";
		return "subckt";
	}

	void dump_params(const char *command, dict<IdString, Const> &params)
	{
		for (auto &param : params) {
			f << stringf("%s %s ", command, RTLIL::id2cstr(param.first));
			if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
				std::string str = param.second.decode_string();
				f << stringf("\"");
				for (char ch : str)
					if (ch == '"' || ch == '\\')
						f << stringf("\\%c", ch);
					else if (ch < 32 || ch >= 127)
						f << stringf("\\%03o", ch);
					else
						f << stringf("%c", ch);
				f << stringf("\"\n");
			} else
				f << stringf("%s\n", param.second.as_string().c_str());
		}
	}

	void dump()
	{
		f << stringf("\n");
		f << stringf(".model %s\n", cstr(module->name));

		std::map<int, RTLIL::Wire*> inputs, outputs;

		for (auto &wire_it : module->wires_) {
			RTLIL::Wire *wire = wire_it.second;
			if (wire->port_input)
				inputs[wire->port_id] = wire;
			if (wire->port_output)
				outputs[wire->port_id] = wire;
		}

		f << stringf(".inputs");
		for (auto &it : inputs) {
			RTLIL::Wire *wire = it.second;
			for (int i = 0; i < wire->width; i++)
				f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
		}
		f << stringf("\n");

		f << stringf(".outputs");
		for (auto &it : outputs) {
			RTLIL::Wire *wire = it.second;
			for (int i = 0; i < wire->width; i++)
				f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
		}
		f << stringf("\n");

		if (module->get_bool_attribute("\\blackbox")) {
			f << stringf(".blackbox\n");
			f << stringf(".end\n");
			return;
		}

		if (!config->impltf_mode) {
			if (!config->false_type.empty()) {
				if (config->false_type == "+")
					f << stringf(".names %s\n", config->false_out.c_str());
				else if (config->false_type != "-")
					f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type),
							config->false_type.c_str(), config->false_out.c_str());
			} else
				f << stringf(".names $false\n");
			if (!config->true_type.empty()) {
				if (config->true_type == "+")
					f << stringf(".names %s\n1\n", config->true_out.c_str());
				else if (config->true_type != "-")
					f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type),
							config->true_type.c_str(), config->true_out.c_str());
			} else
				f << stringf(".names $true\n1\n");
			if (!config->undef_type.empty()) {
				if (config->undef_type == "+")
					f << stringf(".names %s\n", config->undef_out.c_str());
				else if (config->undef_type != "-")
					f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type),
							config->undef_type.c_str(), config->undef_out.c_str());
			} else
				f << stringf(".names $undef\n");
		}

		for (auto &cell_it : module->cells_)
		{
			RTLIL::Cell *cell = cell_it.second;

			if (config->unbuf_types.count(cell->type)) {
				auto portnames = config->unbuf_types.at(cell->type);
				f << stringf(".names %s %s\n1 1\n",
						cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second)));
				continue;
			}

			if (!config->icells_mode && cell->type == "$_NOT_") {
				f << stringf(".names %s %s\n0 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_AND_") {
				f << stringf(".names %s %s %s\n11 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_OR_") {
				f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_XOR_") {
				f << stringf(".names %s %s %s\n10 1\n01 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_NAND_") {
				f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_NOR_") {
				f << stringf(".names %s %s %s\n00 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_XNOR_") {
				f << stringf(".names %s %s %s\n11 1\n00 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_ANDNOT_") {
				f << stringf(".names %s %s %s\n10 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_ORNOT_") {
				f << stringf(".names %s %s %s\n1- 1\n-0 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_AOI3_") {
				f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_OAI3_") {
				f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_AOI4_") {
				f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
						cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_OAI4_") {
				f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
						cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_MUX_") {
				f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
						cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
						cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_FF_") {
				f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
						cstr_init(cell->getPort("\\Q")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_DFF_N_") {
				f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
						cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_DFF_P_") {
				f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
						cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
				f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
						cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
				f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
						cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$lut") {
				f << stringf(".names");
				auto &inputs = cell->getPort("\\A");
				auto width = cell->parameters.at("\\WIDTH").as_int();
				log_assert(inputs.size() == width);
				for (int i = width-1; i >= 0; i--)
					f << stringf(" %s", cstr(inputs.extract(i, 1)));
				auto &output = cell->getPort("\\Y");
				log_assert(output.size() == 1);
				f << stringf(" %s", cstr(output));
				f << stringf("\n");
				RTLIL::SigSpec mask = cell->parameters.at("\\LUT");
				for (int i = 0; i < (1 << width); i++)
					if (mask[i] == RTLIL::S1) {
						for (int j = width-1; j >= 0; j--) {
							f << ((i>>j)&1 ? '1' : '0');
						}
						f << " 1\n";
					}
				goto internal_cell;
			}

			if (!config->icells_mode && cell->type == "$sop") {
				f << stringf(".names");
				auto &inputs = cell->getPort("\\A");
				auto width = cell->parameters.at("\\WIDTH").as_int();
				auto depth = cell->parameters.at("\\DEPTH").as_int();
				vector<State> table = cell->parameters.at("\\TABLE").bits;
				while (GetSize(table) < 2*width*depth)
					table.push_back(State::S0);
				log_assert(inputs.size() == width);
				for (int i = 0; i < width; i++)
					f << stringf(" %s", cstr(inputs.extract(i, 1)));
				auto &output = cell->getPort("\\Y");
				log_assert(output.size() == 1);
				f << stringf(" %s", cstr(output));
				f << stringf("\n");
				for (int i = 0; i < depth; i++) {
					for (int j = 0; j < width; j++) {
						bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1;
						bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1;
						if (pat0 && !pat1) f << "0";
						else if (!pat0 && pat1) f << "1";
						else f << "-";
					}
					f << " 1\n";
				}
				goto internal_cell;
			}

			f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
			for (auto &conn : cell->connections())
			for (int i = 0; i < conn.second.size(); i++) {
				if (conn.second.size() == 1)
					f << stringf(" %s", cstr(conn.first));
				else
					f << stringf(" %s[%d]", cstr(conn.first), i);
				f << stringf("=%s", cstr(conn.second.extract(i, 1)));
			}
			f << stringf("\n");

			if (config->cname_mode)
				f << stringf(".cname %s\n", cstr(cell->name));
			if (config->attr_mode)
				dump_params(".attr", cell->attributes);
			if (config->param_mode)
				dump_params(".param", cell->parameters);

			if (0) {
		internal_cell:
				if (config->iname_mode)
					f << stringf(".cname %s\n", cstr(cell->name));
				if (config->iattr_mode)
					dump_params(".attr", cell->attributes);
			}
		}

		for (auto &conn : module->connections())
		for (int i = 0; i < conn.first.size(); i++)
		{
			SigBit lhs_bit = conn.first[i];
			SigBit rhs_bit = conn.second[i];

			if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0)
				continue;

			if (config->conn_mode)
				f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit));
			else if (!config->buf_type.empty())
				f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(),
						config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit));
			else
				f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit));
		}

		f << stringf(".end\n");
	}

	static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig &config)
	{
		BlifDumper dumper(f, module, design, &config);
		dumper.dump();
	}
};

struct BlifBackend : public Backend {
	BlifBackend() : Backend("blif", "write design to BLIF file") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    write_blif [options] [filename]\n");
		log("\n");
		log("Write the current design to an BLIF file.\n");
		log("\n");
		log("    -top top_module\n");
		log("        set the specified module as design top module\n");
		log("\n");
		log("    -buf <cell-type> <in-port> <out-port>\n");
		log("        use cells of type <cell-type> with the specified port names for buffers\n");
		log("\n");
		log("    -unbuf <cell-type> <in-port> <out-port>\n");
		log("        replace buffer cells with the specified name and port names with\n");
		log("        a .names statement that models a buffer\n");
		log("\n");
		log("    -true <cell-type> <out-port>\n");
		log("    -false <cell-type> <out-port>\n");
		log("    -undef <cell-type> <out-port>\n");
		log("        use the specified cell types to drive nets that are constant 1, 0, or\n");
		log("        undefined. when '-' is used as <cell-type>, then <out-port> specifies\n");
		log("        the wire name to be used for the constant signal and no cell driving\n");
		log("        that wire is generated. when '+' is used as <cell-type>, then <out-port>\n");
		log("        specifies the wire name to be used for the constant signal and a .names\n");
		log("        statement is generated to drive the wire.\n");
		log("\n");
		log("    -noalias\n");
		log("        if a net name is aliasing another net name, then by default a net\n");
		log("        without fanout is created that is driven by the other net. This option\n");
		log("        suppresses the generation of this nets without fanout.\n");
		log("\n");
		log("The following options can be useful when the generated file is not going to be\n");
		log("read by a BLIF parser but a custom tool. It is recommended to not name the output\n");
		log("file *.blif when any of this options is used.\n");
		log("\n");
		log("    -icells\n");
		log("        do not translate Yosys's internal gates to generic BLIF logic\n");
		log("        functions. Instead create .subckt or .gate lines for all cells.\n");
		log("\n");
		log("    -gates\n");
		log("        print .gate instead of .subckt lines for all cells that are not\n");
		log("        instantiations of other modules from this design.\n");
		log("\n");
		log("    -conn\n");
		log("        do not generate buffers for connected wires. instead use the\n");
		log("        non-standard .conn statement.\n");
		log("\n");
		log("    -attr\n");
		log("        use the non-standard .attr statement to write cell attributes\n");
		log("\n");
		log("    -param\n");
		log("        use the non-standard .param statement to write cell parameters\n");
		log("\n");
		log("    -cname\n");
		log("        use the non-standard .cname statement to write cell names\n");
		log("\n");
		log("    -iname, -iattr\n");
		log("        enable -cname and -attr functionality for .names statements\n");
		log("        (the .cname and .attr statements will be included in the BLIF\n");
		log("        output after the truth table for the .names statement)\n");
		log("\n");
		log("    -blackbox\n");
		log("        write blackbox cells with .blackbox statement.\n");
		log("\n");
		log("    -impltf\n");
		log("        do not write definitions for the $true, $false and $undef wires.\n");
		log("\n");
	}
	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		std::string top_module_name;
		std::string buf_type, buf_in, buf_out;
		std::string true_type, true_out;
		std::string false_type, false_out;
		BlifDumperConfig config;

		log_header(design, "Executing BLIF backend.\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			if (args[argidx] == "-top" && argidx+1 < args.size()) {
				top_module_name = args[++argidx];
				continue;
			}
			if (args[argidx] == "-buf" && argidx+3 < args.size()) {
				config.buf_type = args[++argidx];
				config.buf_in = args[++argidx];
				config.buf_out = args[++argidx];
				continue;
			}
			if (args[argidx] == "-unbuf" && argidx+3 < args.size()) {
				RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]);
				RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]);
				RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]);
				config.unbuf_types[unbuf_type] = std::pair<RTLIL::IdString, RTLIL::IdString>(unbuf_in, unbuf_out);
				continue;
			}
			if (args[argidx] == "-true" && argidx+2 < args.size()) {
				config.true_type = args[++argidx];
				config.true_out = args[++argidx];
				continue;
			}
			if (args[argidx] == "-false" && argidx+2 < args.size()) {
				config.false_type = args[++argidx];
				config.false_out = args[++argidx];
				continue;
			}
			if (args[argidx] == "-undef" && argidx+2 < args.size()) {
				config.undef_type = args[++argidx];
				config.undef_out = args[++argidx];
				continue;
			}
			if (args[argidx] == "-icells") {
				config.icells_mode = true;
				continue;
			}
			if (args[argidx] == "-gates") {
				config.gates_mode = true;
				continue;
			}
			if (args[argidx] == "-conn") {
				config.conn_mode = true;
				continue;
			}
			if (args[argidx] == "-cname") {
				config.cname_mode = true;
				continue;
			}
			if (args[argidx] == "-param") {
				config.param_mode = true;
				continue;
			}
			if (args[argidx] == "-attr") {
				config.attr_mode = true;
				continue;
			}
			if (args[argidx] == "-iname") {
				config.iname_mode = true;
				continue;
			}
			if (args[argidx] == "-iattr") {
				config.iattr_mode = true;
				continue;
			}
			if (args[argidx] == "-blackbox") {
				config.blackbox_mode = true;
				continue;
			}
			if (args[argidx] == "-impltf") {
				config.impltf_mode = true;
				continue;
			}
			if (args[argidx] == "-noalias") {
				config.noalias_mode = true;
				continue;
			}
			break;
		}
		extra_args(f, filename, args, argidx);

		if (top_module_name.empty())
			for (auto & mod_it:design->modules_)
				if (mod_it.second->get_bool_attribute("\\top"))
					top_module_name = mod_it.first.str();

		*f << stringf("# Generated by %s\n", yosys_version_str);

		std::vector<RTLIL::Module*> mod_list;

		design->sort();
		for (auto module_it : design->modules_)
		{
			RTLIL::Module *module = module_it.second;
			if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
				continue;

			if (module->processes.size() != 0)
				log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));
			if (module->memories.size() != 0)
				log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));

			if (module->name == RTLIL::escape_id(top_module_name)) {
				BlifDumper::dump(*f, module, design, config);
				top_module_name.clear();
				continue;
			}

			mod_list.push_back(module);
		}

		if (!top_module_name.empty())
			log_error("Can't find top module `%s'!\n", top_module_name.c_str());

		for (auto module : mod_list)
			BlifDumper::dump(*f, module, design, config);
	}
} BlifBackend;

PRIVATE_NAMESPACE_END