mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-23 09:05:32 +00:00
initial import
This commit is contained in:
commit
7764d0ba1d
481 changed files with 54634 additions and 0 deletions
4
passes/abc/Makefile.inc
Normal file
4
passes/abc/Makefile.inc
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
OBJS += passes/abc/abc.o
|
||||
OBJS += passes/abc/vlparse.o
|
||||
|
645
passes/abc/abc.cc
Normal file
645
passes/abc/abc.cc
Normal file
|
@ -0,0 +1,645 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "vlparse.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sstream>
|
||||
|
||||
struct gate_t
|
||||
{
|
||||
int id;
|
||||
char type;
|
||||
int in1, in2, in3;
|
||||
bool is_port;
|
||||
RTLIL::SigSpec sig;
|
||||
};
|
||||
|
||||
static int map_autoidx;
|
||||
static SigMap assign_map;
|
||||
static RTLIL::Module *module;
|
||||
static std::vector<gate_t> signal_list;
|
||||
static std::map<RTLIL::SigSpec, int> signal_map;
|
||||
|
||||
static int map_signal(RTLIL::SigSpec sig, char gate_type = -1, int in1 = -1, int in2 = -1, int in3 = -1)
|
||||
{
|
||||
assert(sig.width == 1);
|
||||
assert(sig.chunks.size() == 1);
|
||||
|
||||
assign_map.apply(sig);
|
||||
|
||||
if (signal_map.count(sig) == 0) {
|
||||
gate_t gate;
|
||||
gate.id = signal_list.size();
|
||||
gate.type = -1;
|
||||
gate.in1 = -1;
|
||||
gate.in2 = -1;
|
||||
gate.in3 = -1;
|
||||
gate.is_port = false;
|
||||
gate.sig = sig;
|
||||
signal_list.push_back(gate);
|
||||
signal_map[sig] = gate.id;
|
||||
}
|
||||
|
||||
gate_t &gate = signal_list[signal_map[sig]];
|
||||
|
||||
if (gate_type >= 0)
|
||||
gate.type = gate_type;
|
||||
if (in1 >= 0)
|
||||
gate.in1 = in1;
|
||||
if (in2 >= 0)
|
||||
gate.in2 = in2;
|
||||
if (in3 >= 0)
|
||||
gate.in3 = in3;
|
||||
|
||||
return gate.id;
|
||||
}
|
||||
|
||||
static void mark_port(RTLIL::SigSpec sig)
|
||||
{
|
||||
assign_map.apply(sig);
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire != NULL && signal_map.count(c) > 0)
|
||||
signal_list[signal_map[c]].is_port = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell->type == "$_INV_")
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->connections["\\A"];
|
||||
RTLIL::SigSpec sig_y = cell->connections["\\Y"];
|
||||
|
||||
assign_map.apply(sig_a);
|
||||
assign_map.apply(sig_y);
|
||||
|
||||
map_signal(sig_y, 'n', map_signal(sig_a));
|
||||
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_")
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->connections["\\A"];
|
||||
RTLIL::SigSpec sig_b = cell->connections["\\B"];
|
||||
RTLIL::SigSpec sig_y = cell->connections["\\Y"];
|
||||
|
||||
assign_map.apply(sig_a);
|
||||
assign_map.apply(sig_b);
|
||||
assign_map.apply(sig_y);
|
||||
|
||||
if (cell->type == "$_AND_")
|
||||
map_signal(sig_y, 'a', map_signal(sig_a), map_signal(sig_b));
|
||||
else if (cell->type == "$_OR_")
|
||||
map_signal(sig_y, 'o', map_signal(sig_a), map_signal(sig_b));
|
||||
else if (cell->type == "$_XOR_")
|
||||
map_signal(sig_y, 'x', map_signal(sig_a), map_signal(sig_b));
|
||||
else
|
||||
abort();
|
||||
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_")
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->connections["\\A"];
|
||||
RTLIL::SigSpec sig_b = cell->connections["\\B"];
|
||||
RTLIL::SigSpec sig_s = cell->connections["\\S"];
|
||||
RTLIL::SigSpec sig_y = cell->connections["\\Y"];
|
||||
|
||||
assign_map.apply(sig_a);
|
||||
assign_map.apply(sig_b);
|
||||
assign_map.apply(sig_s);
|
||||
assign_map.apply(sig_y);
|
||||
|
||||
map_signal(sig_y, 'm', map_signal(sig_a), map_signal(sig_b), map_signal(sig_s));
|
||||
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string remap_name(std::string abc_name)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
static void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts)
|
||||
{
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
log("Dumping loop state graph to slide %d.\n", ++nr);
|
||||
|
||||
fprintf(f, "digraph slide%d {\n", nr);
|
||||
fprintf(f, " rankdir=\"LR\";\n");
|
||||
|
||||
std::set<int> nodes;
|
||||
for (auto &e : edges) {
|
||||
nodes.insert(e.first);
|
||||
for (auto n : e.second)
|
||||
nodes.insert(n);
|
||||
}
|
||||
|
||||
for (auto n : nodes)
|
||||
fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].sig),
|
||||
n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
|
||||
|
||||
for (auto &e : edges)
|
||||
for (auto n : e.second)
|
||||
fprintf(f, " n%d -> n%d;\n", e.first, n);
|
||||
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
|
||||
static void handle_loops()
|
||||
{
|
||||
// http://en.wikipedia.org/wiki/Topological_sorting
|
||||
|
||||
std::map<int, std::set<int>> edges;
|
||||
std::vector<int> in_edges_count(signal_list.size());
|
||||
std::set<int> workpool;
|
||||
|
||||
FILE *dot_f = NULL;
|
||||
int dot_nr = 0;
|
||||
|
||||
// uncomment for troubleshooting the loop detection code
|
||||
// dot_f = fopen("test.dot", "w");
|
||||
|
||||
for (auto &g : signal_list) {
|
||||
if (g.type == -1) {
|
||||
workpool.insert(g.id);
|
||||
}
|
||||
if (g.in1 >= 0) {
|
||||
edges[g.in1].insert(g.id);
|
||||
in_edges_count[g.id]++;
|
||||
}
|
||||
if (g.in2 >= 0 && g.in2 != g.in1) {
|
||||
edges[g.in2].insert(g.id);
|
||||
in_edges_count[g.id]++;
|
||||
}
|
||||
if (g.in3 >= 0 && g.in3 != g.in2 && g.in3 != g.in1) {
|
||||
edges[g.in3].insert(g.id);
|
||||
in_edges_count[g.id]++;
|
||||
}
|
||||
}
|
||||
|
||||
dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
|
||||
|
||||
while (workpool.size() > 0)
|
||||
{
|
||||
int id = *workpool.begin();
|
||||
workpool.erase(id);
|
||||
|
||||
// log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].sig));
|
||||
|
||||
for (int id2 : edges[id]) {
|
||||
assert(in_edges_count[id2] > 0);
|
||||
if (--in_edges_count[id2] == 0)
|
||||
workpool.insert(id2);
|
||||
}
|
||||
edges.erase(id);
|
||||
|
||||
dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
|
||||
|
||||
while (workpool.size() == 0)
|
||||
{
|
||||
if (edges.size() == 0)
|
||||
break;
|
||||
|
||||
int id1 = edges.begin()->first;
|
||||
|
||||
for (auto &edge_it : edges) {
|
||||
int id2 = edge_it.first;
|
||||
RTLIL::Wire *w1 = signal_list[id1].sig.chunks[0].wire;
|
||||
RTLIL::Wire *w2 = signal_list[id2].sig.chunks[0].wire;
|
||||
if (w1 != NULL)
|
||||
continue;
|
||||
else if (w2 == NULL)
|
||||
id1 = id2;
|
||||
else if (w1->name[0] == '$' && w2->name[0] == '\\')
|
||||
id1 = id2;
|
||||
else if (w1->name[0] == '\\' && w2->name[0] == '$')
|
||||
continue;
|
||||
else if (edges[id1].size() < edges[id2].size())
|
||||
id1 = id2;
|
||||
else if (edges[id1].size() > edges[id2].size())
|
||||
continue;
|
||||
else if (w1->name > w2->name)
|
||||
id1 = id2;
|
||||
}
|
||||
|
||||
if (edges[id1].size() == 0) {
|
||||
edges.erase(id1);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Wire *wire = new RTLIL::Wire;
|
||||
std::stringstream sstr;
|
||||
sstr << "$abcloop$" << (RTLIL::autoidx++);
|
||||
wire->name = sstr.str();
|
||||
module->wires[wire->name] = wire;
|
||||
|
||||
bool first_line = true;
|
||||
for (int id2 : edges[id1]) {
|
||||
if (first_line)
|
||||
log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)),
|
||||
log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig));
|
||||
else
|
||||
log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "",
|
||||
log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig));
|
||||
first_line = false;
|
||||
}
|
||||
|
||||
int id3 = map_signal(RTLIL::SigSpec(wire));
|
||||
signal_list[id1].is_port = true;
|
||||
signal_list[id3].is_port = true;
|
||||
assert(id3 == int(in_edges_count.size()));
|
||||
in_edges_count.push_back(0);
|
||||
workpool.insert(id3);
|
||||
|
||||
for (int id2 : edges[id1]) {
|
||||
if (signal_list[id2].in1 == id1)
|
||||
signal_list[id2].in1 = id3;
|
||||
if (signal_list[id2].in2 == id1)
|
||||
signal_list[id2].in2 = id3;
|
||||
if (signal_list[id2].in3 == id1)
|
||||
signal_list[id2].in3 = id3;
|
||||
}
|
||||
edges[id1].swap(edges[id3]);
|
||||
|
||||
module->connections.push_back(RTLIL::SigSig(signal_list[id3].sig, signal_list[id1].sig));
|
||||
dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (dot_f != NULL)
|
||||
fclose(dot_f);
|
||||
}
|
||||
|
||||
static void abc_module(RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, bool cleanup)
|
||||
{
|
||||
module = current_module;
|
||||
map_autoidx = RTLIL::autoidx++;
|
||||
|
||||
signal_map.clear();
|
||||
signal_list.clear();
|
||||
assign_map.set(module);
|
||||
|
||||
char tempdir_name[] = "/tmp/yosys-abc-XXXXXX";
|
||||
if (!cleanup)
|
||||
tempdir_name[0] = tempdir_name[4] = '_';
|
||||
char *p = mkdtemp(tempdir_name);
|
||||
log_header("Extracting gate logic of module `%s' to `%s/input.v'..\n", module->name.c_str(), tempdir_name);
|
||||
assert(p != NULL);
|
||||
|
||||
std::vector<RTLIL::Cell*> cells;
|
||||
cells.reserve(module->cells.size());
|
||||
for (auto &it : module->cells)
|
||||
cells.push_back(it.second);
|
||||
for (auto c : cells)
|
||||
extract_cell(c);
|
||||
|
||||
for (auto &wire_it : module->wires) {
|
||||
if (wire_it.second->port_id > 0)
|
||||
mark_port(RTLIL::SigSpec(wire_it.second));
|
||||
}
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
for (auto &port_it : cell_it.second->connections)
|
||||
mark_port(port_it.second);
|
||||
|
||||
handle_loops();
|
||||
|
||||
if (asprintf(&p, "%s/input.v", tempdir_name) < 0) abort();
|
||||
FILE *f = fopen(p, "wt");
|
||||
assert(f != NULL);
|
||||
free(p);
|
||||
|
||||
fprintf(f, "module logic (");
|
||||
bool first = true;
|
||||
for (auto &si : signal_list) {
|
||||
if (!si.is_port)
|
||||
continue;
|
||||
if (!first)
|
||||
fprintf(f, ", ");
|
||||
fprintf(f, "n%d", si.id);
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, "); // %s\n", module->name.c_str());
|
||||
|
||||
int count_input = 0, count_output = 0;
|
||||
for (auto &si : signal_list) {
|
||||
if (si.is_port) {
|
||||
if (si.type >= 0)
|
||||
count_output++;
|
||||
else
|
||||
count_input++;
|
||||
}
|
||||
fprintf(f, "%s n%d; // %s\n", si.is_port ? si.type >= 0 ?
|
||||
"output" : "input" : "wire", si.id, log_signal(si.sig));
|
||||
}
|
||||
for (auto &si : signal_list) {
|
||||
assert(si.sig.width == 1 && si.sig.chunks.size() == 1);
|
||||
if (si.sig.chunks[0].wire == NULL)
|
||||
fprintf(f, "assign n%d = %c;\n", si.id, si.sig.chunks[0].data.bits[0] == RTLIL::State::S1 ? '1' : '0');
|
||||
}
|
||||
|
||||
int count_gates = 0;
|
||||
for (auto &si : signal_list) {
|
||||
if (si.type == 'n')
|
||||
fprintf(f, "not (n%d, n%d);\n", si.id, si.in1);
|
||||
else if (si.type == 'a')
|
||||
fprintf(f, "and (n%d, n%d, n%d);\n", si.id, si.in1, si.in2);
|
||||
else if (si.type == 'o')
|
||||
fprintf(f, "or (n%d, n%d, n%d);\n", si.id, si.in1, si.in2);
|
||||
else if (si.type == 'x')
|
||||
fprintf(f, "xor (n%d, n%d, n%d);\n", si.id, si.in1, si.in2);
|
||||
else if (si.type == 'm')
|
||||
fprintf(f, "assign n%d = n%d ? n%d : n%d;\n", si.id, si.in3, si.in2, si.in1);
|
||||
else if (si.type >= 0)
|
||||
abort();
|
||||
if (si.type >= 0)
|
||||
count_gates++;
|
||||
}
|
||||
|
||||
fprintf(f, "endmodule\n");
|
||||
fclose(f);
|
||||
|
||||
log("Extracted %d gates and %zd wires to a logic network with %d inputs and %d outputs.\n",
|
||||
count_gates, signal_list.size(), count_input, count_output);
|
||||
log_push();
|
||||
|
||||
if (count_output > 0)
|
||||
{
|
||||
log_header("Executing ABC.\n");
|
||||
|
||||
if (asprintf(&p, "%s/stdcells.genlib", tempdir_name) < 0) abort();
|
||||
f = fopen(p, "wt");
|
||||
assert(f != NULL);
|
||||
fprintf(f, "GATE ZERO 1 Y=CONST0;\n");
|
||||
fprintf(f, "GATE ONE 1 Y=CONST1;\n");
|
||||
fprintf(f, "GATE BUF 1 Y=A; PIN * NONINV 1 999 1 0 1 0\n");
|
||||
fprintf(f, "GATE INV 1 Y=!A; PIN * INV 1 999 1 0 1 0\n");
|
||||
fprintf(f, "GATE AND 1 Y=A*B; PIN * NONINV 1 999 1 0 1 0\n");
|
||||
fprintf(f, "GATE OR 1 Y=A+B; PIN * NONINV 1 999 1 0 1 0\n");
|
||||
fprintf(f, "GATE XOR 1 Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n");
|
||||
fprintf(f, "GATE MUX 1 Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n");
|
||||
fclose(f);
|
||||
free(p);
|
||||
|
||||
char buffer[1024];
|
||||
if (!liberty_file.empty())
|
||||
snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; read_liberty %s; "
|
||||
"map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, liberty_file.c_str(), tempdir_name);
|
||||
else
|
||||
if (!script_file.empty())
|
||||
snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; source %s; "
|
||||
"map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, script_file.c_str(), tempdir_name);
|
||||
else
|
||||
snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; read_library %s/stdcells.genlib; "
|
||||
"map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, tempdir_name, tempdir_name);
|
||||
f = popen(buffer, "r");
|
||||
while (fgets(buffer, 1024, f) != NULL)
|
||||
log("ABC: %s", buffer);
|
||||
fclose(f);
|
||||
|
||||
if (asprintf(&p, "%s/output.v", tempdir_name) < 0) abort();
|
||||
f = fopen(p, "rt");
|
||||
if (f == NULL)
|
||||
log_error("Can't open ABC output file `%s'.\n", p);
|
||||
#if 0
|
||||
RTLIL::Design *mapped_design = new RTLIL::Design;
|
||||
frontend_register["verilog"]->execute(f, p, std::vector<std::string>(), mapped_design);
|
||||
#else
|
||||
RTLIL::Design *mapped_design = abc_parse_verilog(f);
|
||||
#endif
|
||||
fclose(f);
|
||||
free(p);
|
||||
|
||||
log_header("Re-integrating ABC results.\n");
|
||||
RTLIL::Module *mapped_mod = mapped_design->modules["\\logic"];
|
||||
if (mapped_mod == NULL)
|
||||
log_error("ABC output file does not contain a module `logic'.\n");
|
||||
for (auto &it : mapped_mod->wires) {
|
||||
RTLIL::Wire *w = it.second;
|
||||
RTLIL::Wire *wire = new RTLIL::Wire;
|
||||
wire->name = remap_name(w->name);
|
||||
module->wires[wire->name] = wire;
|
||||
}
|
||||
|
||||
std::map<std::string, int> cell_stats;
|
||||
if (liberty_file.empty() && script_file.empty())
|
||||
{
|
||||
for (auto &it : mapped_mod->cells) {
|
||||
RTLIL::Cell *c = it.second;
|
||||
cell_stats[c->type.substr(1)]++;
|
||||
if (c->type == "\\ZERO" || c->type == "\\ONE") {
|
||||
RTLIL::SigSig conn;
|
||||
conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1);
|
||||
module->connections.push_back(conn);
|
||||
continue;
|
||||
}
|
||||
if (c->type == "\\BUF") {
|
||||
RTLIL::SigSig conn;
|
||||
conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
conn.second = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]);
|
||||
module->connections.push_back(conn);
|
||||
continue;
|
||||
}
|
||||
if (c->type == "\\INV") {
|
||||
RTLIL::Cell *cell = new RTLIL::Cell;
|
||||
cell->type = "$_INV_";
|
||||
cell->name = remap_name(c->name);
|
||||
cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]);
|
||||
cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
module->cells[cell->name] = cell;
|
||||
continue;
|
||||
}
|
||||
if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR") {
|
||||
RTLIL::Cell *cell = new RTLIL::Cell;
|
||||
cell->type = "$_" + c->type.substr(1) + "_";
|
||||
cell->name = remap_name(c->name);
|
||||
cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]);
|
||||
cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]);
|
||||
cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
module->cells[cell->name] = cell;
|
||||
continue;
|
||||
}
|
||||
if (c->type == "\\MUX") {
|
||||
RTLIL::Cell *cell = new RTLIL::Cell;
|
||||
cell->type = "$_MUX_";
|
||||
cell->name = remap_name(c->name);
|
||||
cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]);
|
||||
cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]);
|
||||
cell->connections["\\S"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\S"].chunks[0].wire->name)]);
|
||||
cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
module->cells[cell->name] = cell;
|
||||
continue;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &it : mapped_mod->cells) {
|
||||
RTLIL::Cell *c = it.second;
|
||||
cell_stats[c->type.substr(1)]++;
|
||||
if (c->type == "$_const0_" || c->type == "$_const1_") {
|
||||
RTLIL::SigSig conn;
|
||||
conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]);
|
||||
conn.second = RTLIL::SigSpec(c->type == "$_const0_" ? 0 : 1, 1);
|
||||
module->connections.push_back(conn);
|
||||
continue;
|
||||
}
|
||||
RTLIL::Cell *cell = new RTLIL::Cell;
|
||||
cell->type = c->type;
|
||||
cell->name = remap_name(c->name);
|
||||
for (auto &conn : c->connections)
|
||||
cell->connections[conn.first] = RTLIL::SigSpec(module->wires[remap_name(conn.second.chunks[0].wire->name)]);
|
||||
module->cells[cell->name] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : cell_stats)
|
||||
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
|
||||
int in_wires = 0, out_wires = 0;
|
||||
for (auto &si : signal_list)
|
||||
if (si.is_port) {
|
||||
char buffer[100];
|
||||
snprintf(buffer, 100, "\\n%d", si.id);
|
||||
RTLIL::SigSig conn;
|
||||
if (si.type >= 0) {
|
||||
conn.first = si.sig;
|
||||
conn.second = RTLIL::SigSpec(module->wires[remap_name(buffer)]);
|
||||
out_wires++;
|
||||
} else {
|
||||
conn.first = RTLIL::SigSpec(module->wires[remap_name(buffer)]);
|
||||
conn.second = si.sig;
|
||||
in_wires++;
|
||||
}
|
||||
module->connections.push_back(conn);
|
||||
}
|
||||
log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
|
||||
log("ABC RESULTS: input signals: %8d\n", in_wires);
|
||||
log("ABC RESULTS: output signals: %8d\n", out_wires);
|
||||
|
||||
delete mapped_design;
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Don't call ABC as there is nothing to map.\n");
|
||||
}
|
||||
|
||||
if (cleanup)
|
||||
{
|
||||
log_header("Removing temp directory `%s':\n", tempdir_name);
|
||||
|
||||
struct dirent **namelist;
|
||||
int n = scandir(tempdir_name, &namelist, 0, alphasort);
|
||||
assert(n >= 0);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) {
|
||||
if (asprintf(&p, "%s/%s", tempdir_name, namelist[i]->d_name) < 0) abort();
|
||||
log("Removing `%s'.\n", p);
|
||||
remove(p);
|
||||
free(p);
|
||||
}
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
log("Removing `%s'.\n", tempdir_name);
|
||||
rmdir(tempdir_name);
|
||||
}
|
||||
|
||||
log_pop();
|
||||
}
|
||||
|
||||
struct AbcPass : public Pass {
|
||||
AbcPass() : Pass("abc") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing ABC pass (technology mapping using ABC).\n");
|
||||
log_push();
|
||||
|
||||
std::string exe_file = "abc";
|
||||
std::string script_file, liberty_file;
|
||||
bool cleanup = true;
|
||||
|
||||
size_t argidx;
|
||||
char *pwd = get_current_dir_name();
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-exe" && argidx+1 < args.size()) {
|
||||
exe_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (arg == "-script" && argidx+1 < args.size() && liberty_file.empty()) {
|
||||
script_file = args[++argidx];
|
||||
if (!script_file.empty() && script_file[0] != '/')
|
||||
script_file = std::string(pwd) + "/" + script_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-liberty" && argidx+1 < args.size() && script_file.empty()) {
|
||||
liberty_file = args[++argidx];
|
||||
if (!liberty_file.empty() && liberty_file[0] != '/')
|
||||
liberty_file = std::string(pwd) + "/" + liberty_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nocleanup") {
|
||||
cleanup = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
free(pwd);
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
if (mod_it.second->processes.size() > 0)
|
||||
log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str());
|
||||
else
|
||||
abc_module(mod_it.second, script_file, exe_file, liberty_file, cleanup);
|
||||
}
|
||||
|
||||
assign_map.clear();
|
||||
signal_list.clear();
|
||||
signal_map.clear();
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} AbcPass;
|
||||
|
198
passes/abc/vlparse.cc
Normal file
198
passes/abc/vlparse.cc
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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 "vlparse.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
static int lex_line, lex_tok;
|
||||
static std::string lex_str;
|
||||
|
||||
static int token(int tok)
|
||||
{
|
||||
lex_tok = tok;
|
||||
#if 0
|
||||
if (lex_tok == 256)
|
||||
fprintf(stderr, "STR in line %d: >>%s<<\n", lex_line, lex_str.c_str());
|
||||
else if (tok >= 32 && tok < 255)
|
||||
fprintf(stderr, "CHAR in line %d: >>%c<<\n", lex_line, lex_tok);
|
||||
else
|
||||
fprintf(stderr, "CHAR in line %d: %d\n", lex_line, lex_tok);
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
static int lex(FILE *f)
|
||||
{
|
||||
int ch = getc(f);
|
||||
|
||||
while (ch == ' ' || ch == '\t' || ch == '\n') {
|
||||
if (ch == '\n')
|
||||
lex_line++;
|
||||
ch = getc(f);
|
||||
}
|
||||
|
||||
if (ch <= 0 || 255 < ch)
|
||||
return token(lex_tok);
|
||||
|
||||
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
|
||||
('0' <= ch && ch <= '9') || ch == '_') {
|
||||
lex_str = char(ch);
|
||||
while (1) {
|
||||
ch = getc(f);
|
||||
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
|
||||
('0' <= ch && ch <= '9') || ch == '_') {
|
||||
lex_str += char(ch);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ungetc(ch, f);
|
||||
return token(256);
|
||||
}
|
||||
|
||||
if (ch == '/') {
|
||||
ch = getc(f);
|
||||
if (ch == '/') {
|
||||
while (ch != '\n')
|
||||
ch = getc(f);
|
||||
ungetc(ch, f);
|
||||
return lex(f);
|
||||
}
|
||||
ungetc(ch, f);
|
||||
return token('/');
|
||||
}
|
||||
|
||||
return token(ch);
|
||||
}
|
||||
|
||||
RTLIL::Design *abc_parse_verilog(FILE *f)
|
||||
{
|
||||
RTLIL::Design *design = new RTLIL::Design;
|
||||
RTLIL::Module *module;
|
||||
RTLIL::Wire *wire;
|
||||
RTLIL::Cell *cell;
|
||||
|
||||
int port_count = 1;
|
||||
lex_line = 1;
|
||||
|
||||
// parse module header
|
||||
if (lex(f) != 256 || lex_str != "module")
|
||||
goto error;
|
||||
if (lex(f) != 256)
|
||||
goto error;
|
||||
|
||||
module = new RTLIL::Module;
|
||||
module->name = "\\" + lex_str;
|
||||
design->modules[module->name] = module;
|
||||
|
||||
if (lex(f) != '(')
|
||||
goto error;
|
||||
while (lex(f) != ')') {
|
||||
if (lex_tok != 256 && lex_tok != ',')
|
||||
goto error;
|
||||
}
|
||||
if (lex(f) != ';')
|
||||
goto error;
|
||||
|
||||
// parse module body
|
||||
while (1)
|
||||
{
|
||||
if (lex(f) != 256)
|
||||
goto error;
|
||||
|
||||
if (lex_str == "endmodule")
|
||||
return design;
|
||||
|
||||
if (lex_str == "input" || lex_str == "output" || lex_str == "wire")
|
||||
{
|
||||
std::string mode = lex_str;
|
||||
while (lex(f) != ';') {
|
||||
if (lex_tok != 256 && lex_tok != ',')
|
||||
goto error;
|
||||
if (lex_tok == 256) {
|
||||
// printf("%s [%s]\n", mode.c_str(), lex_str.c_str());
|
||||
wire = new RTLIL::Wire;
|
||||
wire->name = "\\" + lex_str;
|
||||
if (mode == "input") {
|
||||
wire->port_id = port_count++;
|
||||
wire->port_input = true;
|
||||
}
|
||||
if (mode == "output") {
|
||||
wire->port_id = port_count++;
|
||||
wire->port_output = true;
|
||||
}
|
||||
module->wires[wire->name] = wire;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string cell_type = lex_str;
|
||||
|
||||
if (lex(f) != 256)
|
||||
goto error;
|
||||
|
||||
std::string cell_name = lex_str;
|
||||
|
||||
if (lex(f) != '(')
|
||||
goto error;
|
||||
|
||||
// printf("cell [%s] [%s]\n", cell_type.c_str(), cell_name.c_str());
|
||||
cell = new RTLIL::Cell;
|
||||
cell->type = "\\" + cell_type;
|
||||
cell->name = "\\" + cell_name;
|
||||
module->cells[cell->name] = cell;
|
||||
|
||||
lex(f);
|
||||
while (lex_tok != ')')
|
||||
{
|
||||
if (lex_tok != '.' || lex(f) != 256)
|
||||
goto error;
|
||||
|
||||
std::string cell_port = lex_str;
|
||||
|
||||
if (lex(f) != '(' || lex(f) != 256)
|
||||
goto error;
|
||||
|
||||
std::string wire_name = lex_str;
|
||||
|
||||
// printf(" [%s] <- [%s]\n", cell_port.c_str(), wire_name.c_str());
|
||||
if (module->wires.count("\\" + wire_name) == 0)
|
||||
goto error;
|
||||
cell->connections["\\" + cell_port] = RTLIL::SigSpec(module->wires["\\" + wire_name]);
|
||||
|
||||
if (lex(f) != ')' || (lex(f) != ',' && lex_tok != ')'))
|
||||
goto error;
|
||||
while (lex_tok == ',')
|
||||
lex(f);
|
||||
}
|
||||
|
||||
if (lex(f) != ';')
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
log_error("Syntax error in line %d!\n", lex_line);
|
||||
// delete design;
|
||||
// return NULL;
|
||||
}
|
||||
|
28
passes/abc/vlparse.h
Normal file
28
passes/abc/vlparse.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ABC_VLPARSE
|
||||
#define ABC_VLPARSE
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
|
||||
extern RTLIL::Design *abc_parse_verilog(FILE *f);
|
||||
|
||||
#endif
|
||||
|
10
passes/dfflibmap/Makefile.inc
Normal file
10
passes/dfflibmap/Makefile.inc
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
OBJS += passes/dfflibmap/dfflibmap.o
|
||||
OBJS += passes/dfflibmap/libparse.o
|
||||
|
||||
TARGETS += filterlib
|
||||
GENFILES += passes/dfflibmap/filterlib.o
|
||||
|
||||
filterlib: passes/dfflibmap/filterlib.o
|
||||
$(CXX) -o filterlib $(LDFLAGS) $^ $(LDLIBS)
|
||||
|
326
passes/dfflibmap/dfflibmap.cc
Normal file
326
passes/dfflibmap/dfflibmap.cc
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include "libparse.h"
|
||||
#include <string.h>
|
||||
|
||||
using namespace PASS_DFFLIBMAP;
|
||||
|
||||
struct cell_mapping {
|
||||
std::string cell_name;
|
||||
std::map<std::string, char> ports;
|
||||
};
|
||||
static std::map<std::string, cell_mapping> cell_mappings;
|
||||
|
||||
static void logmap(std::string dff)
|
||||
{
|
||||
if (cell_mappings.count(dff) == 0) {
|
||||
log(" unmapped dff cell: %s\n", dff.c_str());
|
||||
} else {
|
||||
log(" %s %s(", cell_mappings[dff].cell_name.c_str(), dff.substr(1).c_str());
|
||||
bool first = true;
|
||||
for (auto &port : cell_mappings[dff].ports) {
|
||||
char arg[3] = { port.second, 0, 0 };
|
||||
if ('a' <= arg[0] && arg[0] <= 'z')
|
||||
arg[1] = arg[0] - ('a' - 'A'), arg[0] = '~';
|
||||
log("%s.%s(%s)", first ? "" : ", ", port.first.c_str(), arg);
|
||||
first = false;
|
||||
}
|
||||
log(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void logmap_all()
|
||||
{
|
||||
logmap("$_DFF_N_");
|
||||
logmap("$_DFF_P_");
|
||||
logmap("$_DFF_NN0_");
|
||||
logmap("$_DFF_NN1_");
|
||||
logmap("$_DFF_NP0_");
|
||||
logmap("$_DFF_NP1_");
|
||||
logmap("$_DFF_PN0_");
|
||||
logmap("$_DFF_PN1_");
|
||||
logmap("$_DFF_PP0_");
|
||||
logmap("$_DFF_PP1_");
|
||||
}
|
||||
|
||||
static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol)
|
||||
{
|
||||
if (cell == NULL || attr == NULL || attr->value.empty())
|
||||
return false;
|
||||
|
||||
std::string value = attr->value;
|
||||
|
||||
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
|
||||
value.erase(pos, 1);
|
||||
|
||||
if (value[value.size()-1] == '\'') {
|
||||
pin_name = value.substr(0, value.size()-1);
|
||||
pin_pol = false;
|
||||
} else {
|
||||
pin_name = value;
|
||||
pin_pol = true;
|
||||
}
|
||||
|
||||
for (auto child : cell->children)
|
||||
if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval)
|
||||
{
|
||||
LibertyAst *best_cell = NULL;
|
||||
std::map<std::string, char> best_cell_ports;
|
||||
int best_cell_pins = 0;
|
||||
|
||||
if (ast->id != "library")
|
||||
log_error("Format error in liberty file.\n");
|
||||
|
||||
for (auto cell : ast->children)
|
||||
{
|
||||
if (cell->id != "cell" || cell->args.size() != 1)
|
||||
continue;
|
||||
|
||||
LibertyAst *ff = cell->find("ff");
|
||||
if (ff == NULL)
|
||||
continue;
|
||||
|
||||
std::string cell_clk_pin, cell_rst_pin, cell_next_pin;
|
||||
bool cell_clk_pol, cell_rst_pol, cell_next_pol;
|
||||
|
||||
if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
|
||||
continue;
|
||||
if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
|
||||
continue;
|
||||
if (has_reset && rstval == false) {
|
||||
if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
|
||||
continue;
|
||||
}
|
||||
if (has_reset && rstval == true) {
|
||||
if (!parse_pin(cell, ff->find("preset"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
|
||||
continue;
|
||||
}
|
||||
|
||||
std::map<std::string, char> this_cell_ports;
|
||||
this_cell_ports[cell_clk_pin] = 'C';
|
||||
if (has_reset)
|
||||
this_cell_ports[cell_rst_pin] = 'R';
|
||||
this_cell_ports[cell_next_pin] = 'D';
|
||||
|
||||
int num_pins = 0;
|
||||
bool found_output = false;
|
||||
for (auto pin : cell->children)
|
||||
{
|
||||
if (pin->id != "pin" || pin->args.size() != 1)
|
||||
continue;
|
||||
|
||||
LibertyAst *dir = pin->find("direction");
|
||||
if (dir == NULL || dir->value == "internal")
|
||||
continue;
|
||||
num_pins++;
|
||||
|
||||
if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0)
|
||||
goto continue_cell_loop;
|
||||
|
||||
LibertyAst *func = pin->find("function");
|
||||
if (dir->value == "output" && func != NULL) {
|
||||
std::string value = func->value;
|
||||
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
|
||||
value.erase(pos, 1);
|
||||
if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) {
|
||||
this_cell_ports[pin->args[0]] = 'Q';
|
||||
found_output = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this_cell_ports.count(pin->args[0]) == 0)
|
||||
this_cell_ports[pin->args[0]] = 0;
|
||||
}
|
||||
|
||||
if (!found_output || (best_cell != NULL && num_pins > best_cell_pins))
|
||||
continue;
|
||||
|
||||
best_cell = cell;
|
||||
best_cell_pins = num_pins;
|
||||
best_cell_ports.swap(this_cell_ports);
|
||||
continue_cell_loop:;
|
||||
}
|
||||
|
||||
if (best_cell != NULL) {
|
||||
log(" cell %s is a direct match for cell type %s.\n", best_cell->args[0].c_str(), cell_type.substr(1).c_str());
|
||||
cell_mappings[cell_type].cell_name = best_cell->args[0];
|
||||
cell_mappings[cell_type].ports = best_cell_ports;
|
||||
}
|
||||
}
|
||||
|
||||
static bool expand_cellmap_worker(std::string from, std::string to, std::string inv)
|
||||
{
|
||||
if (cell_mappings.count(to) > 0)
|
||||
return false;
|
||||
|
||||
log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
|
||||
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
|
||||
cell_mappings[to].ports = cell_mappings[from].ports;
|
||||
|
||||
for (auto &it : cell_mappings[to].ports) {
|
||||
if (inv.find(it.second) == std::string::npos)
|
||||
continue;
|
||||
if ('a' <= it.second && it.second <= 'z')
|
||||
it.second -= 'a' - 'A';
|
||||
else if ('A' <= it.second && it.second <= 'Z')
|
||||
it.second += 'a' - 'A';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool expand_cellmap(std::string pattern, std::string inv)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> from_to_list;
|
||||
bool return_status = false;
|
||||
|
||||
for (auto &it : cell_mappings) {
|
||||
std::string from = it.first, to = it.first;
|
||||
if (from.size() != pattern.size())
|
||||
continue;
|
||||
for (size_t i = 0; i < from.size(); i++) {
|
||||
if (pattern[i] == '*') {
|
||||
to[i] = from[i] == 'P' ? 'N' :
|
||||
from[i] == 'N' ? 'P' :
|
||||
from[i] == '1' ? '0' :
|
||||
from[i] == '0' ? '1' : '*';
|
||||
} else
|
||||
if (pattern[i] != '?' && pattern[i] != from[i])
|
||||
goto pattern_failed;
|
||||
}
|
||||
from_to_list.push_back(std::pair<std::string, std::string>(from, to));
|
||||
pattern_failed:;
|
||||
}
|
||||
|
||||
for (auto &it : from_to_list)
|
||||
return_status = return_status || expand_cellmap_worker(it.first, it.second, inv);
|
||||
return return_status;
|
||||
}
|
||||
|
||||
static void dfflibmap(RTLIL::Module *module)
|
||||
{
|
||||
log("Mapping DFF cells in module `%s':\n", module->name.c_str());
|
||||
|
||||
std::vector<RTLIL::Cell*> cell_list;
|
||||
for (auto &it : module->cells) {
|
||||
if (cell_mappings.count(it.second->type) > 0)
|
||||
cell_list.push_back(it.second);
|
||||
}
|
||||
|
||||
std::map<std::string, int> stats;
|
||||
for (auto cell : cell_list) {
|
||||
cell_mapping &cm = cell_mappings[cell->type];
|
||||
RTLIL::Cell *new_cell = new RTLIL::Cell;
|
||||
new_cell->name = cell->name;
|
||||
new_cell->type = "\\" + cm.cell_name;
|
||||
for (auto &port : cm.ports) {
|
||||
RTLIL::SigSpec sig;
|
||||
if ('A' <= port.second && port.second <= 'Z') {
|
||||
sig = cell->connections[std::string("\\") + port.second];
|
||||
} else
|
||||
if ('a' <= port.second && port.second <= 'z') {
|
||||
sig = cell->connections[std::string("\\") + char(port.second - ('a' - 'A'))];
|
||||
RTLIL::Cell *inv_cell = new RTLIL::Cell;
|
||||
RTLIL::Wire *inv_wire = new RTLIL::Wire;
|
||||
inv_cell->name = stringf("$dfflibmap$inv$%d", RTLIL::autoidx);
|
||||
inv_wire->name = stringf("$dfflibmap$sig$%d", RTLIL::autoidx++);
|
||||
inv_cell->type = "$_INV_";
|
||||
inv_cell->connections[port.second == 'q' ? "\\Y" : "\\A"] = sig;
|
||||
sig = RTLIL::SigSpec(inv_wire);
|
||||
inv_cell->connections[port.second == 'q' ? "\\A" : "\\Y"] = sig;
|
||||
module->cells[inv_cell->name] = inv_cell;
|
||||
module->wires[inv_wire->name] = inv_wire;
|
||||
}
|
||||
new_cell->connections["\\" + port.first] = sig;
|
||||
}
|
||||
stats[stringf(" mapped %%d %s cells to %s cells.\n", cell->type.c_str(), new_cell->type.c_str())]++;
|
||||
module->cells[cell->name] = new_cell;
|
||||
delete cell;
|
||||
}
|
||||
|
||||
for (auto &stat: stats)
|
||||
log(stat.first.c_str(), stat.second);
|
||||
}
|
||||
|
||||
struct DfflibmapPass : public Pass {
|
||||
DfflibmapPass() : Pass("dfflibmap") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
|
||||
|
||||
std::string liberty_file;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-liberty" && argidx+1 < args.size()) {
|
||||
liberty_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (liberty_file.empty())
|
||||
log_cmd_error("Missing `-liberty liberty_file' option!\n");
|
||||
|
||||
FILE *f = fopen(liberty_file.c_str(), "r");
|
||||
if (f == NULL)
|
||||
log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
|
||||
LibertyParer libparser(f);
|
||||
fclose(f);
|
||||
|
||||
find_cell(libparser.ast, "$_DFF_N_", false, false, false, false);
|
||||
find_cell(libparser.ast, "$_DFF_P_", true, false, false, false);
|
||||
find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false);
|
||||
find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true);
|
||||
find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false);
|
||||
find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true);
|
||||
find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false);
|
||||
find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true);
|
||||
find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false);
|
||||
find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true);
|
||||
|
||||
bool keep_running = true;
|
||||
while (keep_running) {
|
||||
keep_running = false;
|
||||
keep_running |= expand_cellmap("$_DFF_*_", "C");
|
||||
keep_running |= expand_cellmap("$_DFF_*??_", "C");
|
||||
keep_running |= expand_cellmap("$_DFF_?*?_", "R");
|
||||
keep_running |= expand_cellmap("$_DFF_??*_", "DQ");
|
||||
}
|
||||
|
||||
log(" final dff cell mappings:\n");
|
||||
logmap_all();
|
||||
|
||||
for (auto &it : design->modules)
|
||||
dfflibmap(it.second);
|
||||
|
||||
cell_mappings.clear();
|
||||
}
|
||||
} DfflibmapPass;
|
||||
|
4
passes/dfflibmap/filterlib.cc
Normal file
4
passes/dfflibmap/filterlib.cc
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
#define FILTERLIB
|
||||
#include "libparse.cc"
|
||||
|
629
passes/dfflibmap/libparse.cc
Normal file
629
passes/dfflibmap/libparse.cc
Normal file
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* 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 "libparse.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef FILTERLIB
|
||||
#include "kernel/log.h"
|
||||
#endif
|
||||
|
||||
using namespace PASS_DFFLIBMAP;
|
||||
|
||||
std::set<std::string> LibertyAst::blacklist;
|
||||
std::set<std::string> LibertyAst::whitelist;
|
||||
|
||||
LibertyAst::~LibertyAst()
|
||||
{
|
||||
for (auto child : children)
|
||||
delete child;
|
||||
children.clear();
|
||||
}
|
||||
|
||||
LibertyAst *LibertyAst::find(std::string name)
|
||||
{
|
||||
if (this == NULL)
|
||||
return NULL;
|
||||
for (auto child : children)
|
||||
if (child->id == name)
|
||||
return child;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_ok)
|
||||
{
|
||||
if (whitelist.count(path + "/*") > 0)
|
||||
path_ok = true;
|
||||
|
||||
path += "/" + id;
|
||||
|
||||
if (blacklist.count(id) > 0 || blacklist.count(path) > 0)
|
||||
return;
|
||||
if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) {
|
||||
fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str());
|
||||
blacklist.insert(id);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "%s%s", indent.c_str(), id.c_str());
|
||||
if (!args.empty()) {
|
||||
for (size_t i = 0; i < args.size(); i++)
|
||||
fprintf(f, "%s%s", i > 0 ? ", " : "(", args[i].c_str());
|
||||
fprintf(f, ")");
|
||||
}
|
||||
if (!value.empty())
|
||||
fprintf(f, " : %s", value.c_str());
|
||||
if (!children.empty()) {
|
||||
fprintf(f, " {\n");
|
||||
for (size_t i = 0; i < children.size(); i++)
|
||||
children[i]->dump(f, indent + " ", path, path_ok);
|
||||
fprintf(f, "%s}\n", indent.c_str());
|
||||
} else
|
||||
fprintf(f, " ;\n");
|
||||
}
|
||||
|
||||
int LibertyParer::lexer(std::string &str)
|
||||
{
|
||||
int c;
|
||||
|
||||
do {
|
||||
c = fgetc(f);
|
||||
} while (c == ' ' || c == '\t' || c == '\r');
|
||||
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '.') {
|
||||
str = c;
|
||||
while (1) {
|
||||
c = fgetc(f);
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '.')
|
||||
str += c;
|
||||
else
|
||||
break;
|
||||
}
|
||||
ungetc(c, f);
|
||||
// fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
|
||||
return 'v';
|
||||
}
|
||||
|
||||
if (c == '"') {
|
||||
str = c;
|
||||
while (1) {
|
||||
c = fgetc(f);
|
||||
if (c == '\n')
|
||||
line++;
|
||||
str += c;
|
||||
if (c == '"')
|
||||
break;
|
||||
}
|
||||
// fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
|
||||
return 'v';
|
||||
}
|
||||
|
||||
if (c == '/') {
|
||||
c = fgetc(f);
|
||||
if (c == '*') {
|
||||
int last_c = 0;
|
||||
while (c > 0 && (last_c != '*' || c != '/')) {
|
||||
last_c = c;
|
||||
c = fgetc(f);
|
||||
if (c == '\n')
|
||||
line++;
|
||||
}
|
||||
return lexer(str);
|
||||
}
|
||||
ungetc(c, f);
|
||||
// fprintf(stderr, "LEX: char >>/<<\n");
|
||||
return '/';
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
c = fgetc(f);
|
||||
if (c == '\r')
|
||||
c = fgetc(f);
|
||||
if (c == '\n')
|
||||
return lexer(str);
|
||||
ungetc(c, f);
|
||||
return '\\';
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
line++;
|
||||
return ';';
|
||||
}
|
||||
|
||||
// if (c >= 32 && c < 255)
|
||||
// fprintf(stderr, "LEX: char >>%c<<\n", c);
|
||||
// else
|
||||
// fprintf(stderr, "LEX: char %d\n", c);
|
||||
return c;
|
||||
}
|
||||
|
||||
LibertyAst *LibertyParer::parse()
|
||||
{
|
||||
std::string str;
|
||||
|
||||
int tok = lexer(str);
|
||||
|
||||
while (tok == ';')
|
||||
tok = lexer(str);
|
||||
|
||||
if (tok == '}' || tok < 0)
|
||||
return NULL;
|
||||
|
||||
if (tok != 'v')
|
||||
error();
|
||||
|
||||
LibertyAst *ast = new LibertyAst;
|
||||
ast->id = str;
|
||||
|
||||
while (1)
|
||||
{
|
||||
tok = lexer(str);
|
||||
|
||||
if (tok == ';')
|
||||
break;
|
||||
|
||||
if (tok == ':' && ast->value.empty()) {
|
||||
tok = lexer(ast->value);
|
||||
if (tok != 'v')
|
||||
error();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == '(') {
|
||||
while (1) {
|
||||
std::string arg;
|
||||
tok = lexer(arg);
|
||||
if (tok == ',')
|
||||
continue;
|
||||
if (tok == ')')
|
||||
break;
|
||||
if (tok != 'v')
|
||||
error();
|
||||
ast->args.push_back(arg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == '{') {
|
||||
while (1) {
|
||||
LibertyAst *child = parse();
|
||||
if (child == NULL)
|
||||
break;
|
||||
ast->children.push_back(child);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
error();
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
#ifndef FILTERLIB
|
||||
|
||||
void LibertyParer::error()
|
||||
{
|
||||
log_error("Syntax error in line %d.\n", line);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void LibertyParer::error()
|
||||
{
|
||||
fprintf(stderr, "Syntax error in line %d.\n", line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
|
||||
|
||||
// This is to not confuse the VIM syntax highlighting
|
||||
#define CHECK_VAL_OPEN (
|
||||
#define CHECK_VAL_CLOSE )
|
||||
|
||||
#define CHECK(result, check) \
|
||||
CHECK_VAL_OPEN{ \
|
||||
auto _R = (result); \
|
||||
if (!(_R check)) { \
|
||||
fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
|
||||
#result, (long int)_R, #check, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
_R; \
|
||||
}CHECK_VAL_CLOSE
|
||||
|
||||
#define CHECK_NV(result, check) \
|
||||
do { \
|
||||
auto _R = (result); \
|
||||
if (!(_R check)) { \
|
||||
fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
|
||||
#result, (long int)_R, #check, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define CHECK_COND(result) \
|
||||
do { \
|
||||
if (!(result)) { \
|
||||
fprintf(stderr, "Error from '%s' in %s:%d.\n", \
|
||||
#result, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
|
||||
|
||||
std::string func2vl(std::string str)
|
||||
{
|
||||
for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
|
||||
char c_left = pos > 0 ? str[pos-1] : ' ';
|
||||
char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
|
||||
if (std::string("\" \t*+").find(c_left) != std::string::npos)
|
||||
str.erase(pos, 1);
|
||||
else if (std::string("\" \t*+").find(c_right) != std::string::npos)
|
||||
str.erase(pos, 1);
|
||||
else
|
||||
str[pos] = '*';
|
||||
}
|
||||
|
||||
std::vector<size_t> group_start;
|
||||
for (size_t pos = 0; pos < str.size(); pos++) {
|
||||
if (str[pos] == '(')
|
||||
group_start.push_back(pos);
|
||||
if (str[pos] == ')' && group_start.size() > 0) {
|
||||
if (pos+1 < str.size() && str[pos+1] == '\'') {
|
||||
std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
|
||||
str[group_start.back()] = '~';
|
||||
str.replace(group_start.back()+1, group.size(), group);
|
||||
pos++;
|
||||
}
|
||||
group_start.pop_back();
|
||||
}
|
||||
if (str[pos] == '\'' && pos > 0) {
|
||||
size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
|
||||
std::string group = str.substr(start, pos-start);
|
||||
str[start] = '~';
|
||||
str.replace(start+1, group.size(), group);
|
||||
}
|
||||
if (str[pos] == '*')
|
||||
str[pos] = '&';
|
||||
if (str[pos] == '+')
|
||||
str[pos] = '|';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void event2vl(LibertyAst *ast, std::string &edge, std::string &expr)
|
||||
{
|
||||
edge.clear();
|
||||
expr.clear();
|
||||
|
||||
if (ast != NULL) {
|
||||
expr = func2vl(ast->value);
|
||||
if (expr.size() > 0 && expr[0] == '~')
|
||||
edge = "negedge " + expr.substr(1);
|
||||
else
|
||||
edge = "posedge " + expr;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_preset_var(std::string var, std::string type)
|
||||
{
|
||||
if (type.find('L') != std::string::npos) {
|
||||
printf(" %s <= 0;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('H') != std::string::npos) {
|
||||
printf(" %s <= 1;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('T') != std::string::npos) {
|
||||
printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('X') != std::string::npos) {
|
||||
printf(" %s <= 'bx;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void gen_verilogsim_cell(LibertyAst *ast)
|
||||
{
|
||||
if (ast->find("statetable") != NULL)
|
||||
return;
|
||||
|
||||
CHECK_NV(ast->args.size(), == 1);
|
||||
printf("module %s (", ast->args[0].c_str());
|
||||
bool first = true;
|
||||
for (auto child : ast->children) {
|
||||
if (child->id != "pin")
|
||||
continue;
|
||||
CHECK_NV(child->args.size(), == 1);
|
||||
printf("%s%s", first ? "" : ", ", child->args[0].c_str());
|
||||
first = false;
|
||||
}
|
||||
printf(");\n");
|
||||
|
||||
for (auto child : ast->children) {
|
||||
if (child->id != "ff" && child->id != "latch")
|
||||
continue;
|
||||
printf(" reg ");
|
||||
first = true;
|
||||
for (auto arg : child->args) {
|
||||
printf("%s%s", first ? "" : ", ", arg.c_str());
|
||||
first = false;
|
||||
}
|
||||
printf(";\n");
|
||||
}
|
||||
|
||||
for (auto child : ast->children) {
|
||||
if (child->id != "pin")
|
||||
continue;
|
||||
CHECK_NV(child->args.size(), == 1);
|
||||
LibertyAst *dir = CHECK(child->find("direction"), != NULL);
|
||||
LibertyAst *func = child->find("function");
|
||||
printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
|
||||
if (func != NULL)
|
||||
printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
|
||||
}
|
||||
|
||||
for (auto child : ast->children)
|
||||
{
|
||||
if (child->id != "ff" || child->args.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string iq_var = child->args[0];
|
||||
std::string iqn_var = child->args[1];
|
||||
|
||||
std::string clock_edge, clock_expr;
|
||||
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
|
||||
|
||||
std::string clear_edge, clear_expr;
|
||||
event2vl(child->find("clear"), clear_edge, clear_expr);
|
||||
|
||||
std::string preset_edge, preset_expr;
|
||||
event2vl(child->find("preset"), preset_edge, preset_expr);
|
||||
|
||||
std::string edge = "";
|
||||
if (!clock_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clock_edge;
|
||||
if (!clear_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clear_edge;
|
||||
if (!preset_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + preset_edge;
|
||||
|
||||
if (edge.empty())
|
||||
continue;
|
||||
|
||||
printf(" always @(%s) begin\n", edge.c_str());
|
||||
|
||||
const char *else_prefix = "";
|
||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
||||
clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value);
|
||||
clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value);
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!clear_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
|
||||
printf(" %s <= 0;\n", iq_var.c_str());
|
||||
printf(" %s <= 1;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!preset_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
|
||||
printf(" %s <= 1;\n", iq_var.c_str());
|
||||
printf(" %s <= 0;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (*else_prefix)
|
||||
printf(" %sbegin\n", else_prefix);
|
||||
std::string expr = CHECK(child->find("next_state"), != NULL)->value;
|
||||
printf(" // %s\n", expr.c_str());
|
||||
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
|
||||
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
|
||||
if (*else_prefix)
|
||||
printf(" end\n");
|
||||
|
||||
printf(" end\n");
|
||||
}
|
||||
|
||||
for (auto child : ast->children)
|
||||
{
|
||||
if (child->id != "latch" || child->args.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string iq_var = child->args[0];
|
||||
std::string iqn_var = child->args[1];
|
||||
|
||||
std::string enable_edge, enable_expr;
|
||||
event2vl(child->find("enable"), enable_edge, enable_expr);
|
||||
|
||||
std::string clear_edge, clear_expr;
|
||||
event2vl(child->find("clear"), clear_edge, clear_expr);
|
||||
|
||||
std::string preset_edge, preset_expr;
|
||||
event2vl(child->find("preset"), preset_edge, preset_expr);
|
||||
|
||||
printf(" always @* begin\n");
|
||||
|
||||
const char *else_prefix = "";
|
||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
||||
clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value);
|
||||
clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value);
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!clear_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
|
||||
printf(" %s <= 0;\n", iq_var.c_str());
|
||||
printf(" %s <= 1;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!preset_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
|
||||
printf(" %s <= 1;\n", iq_var.c_str());
|
||||
printf(" %s <= 0;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!enable_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str());
|
||||
std::string expr = CHECK(child->find("data_in"), != NULL)->value;
|
||||
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
|
||||
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
|
||||
printf(" end\n");
|
||||
}
|
||||
|
||||
printf("endmodule\n");
|
||||
}
|
||||
|
||||
void gen_verilogsim(LibertyAst *ast)
|
||||
{
|
||||
CHECK_COND(ast->id == "library");
|
||||
|
||||
for (auto child : ast->children)
|
||||
if (child->id == "cell" && !child->find("dont_use"))
|
||||
gen_verilogsim_cell(child);
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n");
|
||||
fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool flag_verilogsim = false;
|
||||
|
||||
if (argc > 3)
|
||||
usage();
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if (!strcmp(argv[1], "-verilogsim"))
|
||||
flag_verilogsim = true;
|
||||
if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim"))
|
||||
{
|
||||
LibertyAst::whitelist.insert("/library");
|
||||
LibertyAst::whitelist.insert("/library/cell");
|
||||
LibertyAst::whitelist.insert("/library/cell/area");
|
||||
LibertyAst::whitelist.insert("/library/cell/cell_footprint");
|
||||
LibertyAst::whitelist.insert("/library/cell/dont_touch");
|
||||
LibertyAst::whitelist.insert("/library/cell/dont_use");
|
||||
LibertyAst::whitelist.insert("/library/cell/ff");
|
||||
LibertyAst::whitelist.insert("/library/cell/ff/*");
|
||||
LibertyAst::whitelist.insert("/library/cell/latch");
|
||||
LibertyAst::whitelist.insert("/library/cell/latch/*");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/clock");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/direction");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/driver_type");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/function");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin_opposite");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/state_function");
|
||||
LibertyAst::whitelist.insert("/library/cell/pin/three_state");
|
||||
LibertyAst::whitelist.insert("/library/cell/statetable");
|
||||
LibertyAst::whitelist.insert("/library/cell/statetable/*");
|
||||
}
|
||||
else
|
||||
{
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]);
|
||||
usage();
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
while (fgets(buffer, 1024, f) != NULL)
|
||||
{
|
||||
char mode = 0;
|
||||
std::string id;
|
||||
for (char *p = buffer; *p; p++)
|
||||
{
|
||||
if (*p == '-' || *p == '+') {
|
||||
if (mode != 0)
|
||||
goto syntax_error;
|
||||
mode = *p;
|
||||
continue;
|
||||
}
|
||||
if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') {
|
||||
if (!id.empty()) {
|
||||
if (mode == '-')
|
||||
LibertyAst::blacklist.insert(id);
|
||||
else
|
||||
if (mode == '+')
|
||||
LibertyAst::whitelist.insert(id);
|
||||
else
|
||||
goto syntax_error;
|
||||
}
|
||||
id.clear();
|
||||
if (*p == '#')
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
id += *p;
|
||||
continue;
|
||||
|
||||
syntax_error:
|
||||
fprintf(stderr, "Syntax error in rules file:\n%s", buffer);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FILE *f = stdin;
|
||||
if (argc == 3) {
|
||||
f = fopen(argv[2], "r");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
LibertyParer parser(f);
|
||||
if (parser.ast) {
|
||||
if (flag_verilogsim)
|
||||
gen_verilogsim(parser.ast);
|
||||
else
|
||||
parser.ast->dump(stdout);
|
||||
}
|
||||
|
||||
if (argc == 3)
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
56
passes/dfflibmap/libparse.h
Normal file
56
passes/dfflibmap/libparse.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LIBPARSE_H
|
||||
#define LIBPARSE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace PASS_DFFLIBMAP
|
||||
{
|
||||
struct LibertyAst
|
||||
{
|
||||
std::string id, value;
|
||||
std::vector<std::string> args;
|
||||
std::vector<LibertyAst*> children;
|
||||
~LibertyAst();
|
||||
LibertyAst *find(std::string name);
|
||||
void dump(FILE *f, std::string indent = "", std::string path = "", bool path_ok = false);
|
||||
static std::set<std::string> blacklist;
|
||||
static std::set<std::string> whitelist;
|
||||
};
|
||||
|
||||
struct LibertyParer
|
||||
{
|
||||
FILE *f;
|
||||
int line;
|
||||
LibertyAst *ast;
|
||||
LibertyParer(FILE *f) : f(f), line(1), ast(parse()) {}
|
||||
~LibertyParer() { if (ast) delete ast; }
|
||||
int lexer(std::string &str);
|
||||
LibertyAst *parse();
|
||||
void error();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
11
passes/fsm/Makefile.inc
Normal file
11
passes/fsm/Makefile.inc
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
OBJS += passes/fsm/fsm.o
|
||||
OBJS += passes/fsm/fsm_detect.o
|
||||
OBJS += passes/fsm/fsm_extract.o
|
||||
OBJS += passes/fsm/fsm_opt.o
|
||||
OBJS += passes/fsm/fsm_expand.o
|
||||
OBJS += passes/fsm/fsm_recode.o
|
||||
OBJS += passes/fsm/fsm_info.o
|
||||
OBJS += passes/fsm/fsm_export.o
|
||||
OBJS += passes/fsm/fsm_map.o
|
||||
|
91
passes/fsm/fsm.cc
Normal file
91
passes/fsm/fsm.cc
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct FsmPass : public Pass {
|
||||
FsmPass() : Pass("fsm") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool flag_nomap = false;
|
||||
bool flag_norecode = false;
|
||||
bool flag_expand = false;
|
||||
bool flag_export = false;
|
||||
std::string fm_set_fsm_file_opt;
|
||||
|
||||
log_header("Executing FSM pass (extract and optimize FSM).\n");
|
||||
log_push();
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file_opt.empty()) {
|
||||
fm_set_fsm_file_opt = " -fm_set_fsm_file " + args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (arg == "-norecode") {
|
||||
flag_norecode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nomap") {
|
||||
flag_nomap = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-expand") {
|
||||
flag_expand = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-export") {
|
||||
flag_export = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
Pass::call(design, "fsm_detect");
|
||||
Pass::call(design, "fsm_extract");
|
||||
|
||||
Pass::call(design, "fsm_opt");
|
||||
Pass::call(design, "opt_rmunused");
|
||||
Pass::call(design, "fsm_opt");
|
||||
|
||||
if (flag_expand) {
|
||||
Pass::call(design, "fsm_expand");
|
||||
Pass::call(design, "opt_rmunused");
|
||||
Pass::call(design, "fsm_opt");
|
||||
}
|
||||
|
||||
if (!flag_norecode)
|
||||
Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt);
|
||||
Pass::call(design, "fsm_info");
|
||||
|
||||
if (!flag_nomap)
|
||||
Pass::call(design, "fsm_map");
|
||||
|
||||
if (flag_export)
|
||||
Pass::call(design, "fsm_export");
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} FsmPass;
|
||||
|
160
passes/fsm/fsm_detect.cc
Normal file
160
passes/fsm/fsm_detect.cc
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
|
||||
static RTLIL::Module *module;
|
||||
static SigMap assign_map;
|
||||
typedef std::pair<RTLIL::Cell*,std::string> sig2driver_entry_t;
|
||||
static SigSet<sig2driver_entry_t> sig2driver, sig2user;
|
||||
static std::set<RTLIL::Cell*> muxtree_cells;
|
||||
static SigPool sig_at_port;
|
||||
|
||||
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig)
|
||||
{
|
||||
if (sig_at_port.check_any(assign_map(sig)))
|
||||
return false;
|
||||
|
||||
if (sig.is_fully_const() || old_sig == sig)
|
||||
return true;
|
||||
|
||||
std::set<sig2driver_entry_t> cellport_list;
|
||||
sig2driver.find(sig, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y")
|
||||
return false;
|
||||
RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
|
||||
RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
|
||||
if (!check_state_mux_tree(old_sig, sig_a))
|
||||
return false;
|
||||
for (int i = 0; i < sig_b.width; i += sig_a.width)
|
||||
if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.width)))
|
||||
return false;
|
||||
muxtree_cells.insert(cellport.first);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_state_users(RTLIL::SigSpec sig)
|
||||
{
|
||||
if (sig_at_port.check_any(assign_map(sig)))
|
||||
return false;
|
||||
|
||||
std::set<sig2driver_entry_t> cellport_list;
|
||||
sig2user.find(sig, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
RTLIL::Cell *cell = cellport.first;
|
||||
if (muxtree_cells.count(cell) > 0)
|
||||
continue;
|
||||
if (cellport.second != "\\A" && cellport.second != "\\B")
|
||||
return false;
|
||||
if (cell->connections.count("\\A") == 0 || cell->connections.count("\\B") == 0 || cell->connections.count("\\Y") == 0)
|
||||
return false;
|
||||
for (auto &port_it : cell->connections)
|
||||
if (port_it.first != "\\A" && port_it.first != "\\B" && port_it.first != "\\Y")
|
||||
return false;
|
||||
if (assign_map(cell->connections["\\A"]) == sig && cell->connections["\\B"].is_fully_const())
|
||||
continue;
|
||||
if (assign_map(cell->connections["\\B"]) == sig && cell->connections["\\A"].is_fully_const())
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void detect_fsm(RTLIL::Wire *wire)
|
||||
{
|
||||
if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1)
|
||||
return;
|
||||
if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire))))
|
||||
return;
|
||||
|
||||
std::set<sig2driver_entry_t> cellport_list;
|
||||
sig2driver.find(RTLIL::SigSpec(wire), cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
|
||||
continue;
|
||||
muxtree_cells.clear();
|
||||
RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]);
|
||||
RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]);
|
||||
if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d) && check_state_users(sig_q)) {
|
||||
log("Found FSM state register %s in module %s.\n", wire->name.c_str(), module->name.c_str());
|
||||
wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FsmDetectPass : public Pass {
|
||||
FsmDetectPass() : Pass("fsm_detect") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_DETECT pass (finding FSMs in design).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
CellTypes ct;
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
module = mod_it.second;
|
||||
assign_map.set(module);
|
||||
|
||||
sig2driver.clear();
|
||||
sig2user.clear();
|
||||
sig_at_port.clear();
|
||||
for (auto &cell_it : module->cells)
|
||||
for (auto &conn_it : cell_it.second->connections) {
|
||||
if (ct.cell_output(cell_it.second->type, conn_it.first)) {
|
||||
RTLIL::SigSpec sig = conn_it.second;
|
||||
assign_map.apply(sig);
|
||||
sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
|
||||
}
|
||||
if (!ct.cell_known(cell_it.second->type) || ct.cell_input(cell_it.second->type, conn_it.first)) {
|
||||
RTLIL::SigSpec sig = conn_it.second;
|
||||
assign_map.apply(sig);
|
||||
sig2user.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &wire_it : module->wires)
|
||||
if (wire_it.second->port_id != 0)
|
||||
sig_at_port.add(assign_map(RTLIL::SigSpec(wire_it.second)));
|
||||
|
||||
for (auto &wire_it : module->wires)
|
||||
detect_fsm(wire_it.second);
|
||||
}
|
||||
|
||||
assign_map.clear();
|
||||
sig2driver.clear();
|
||||
sig2user.clear();
|
||||
muxtree_cells.clear();
|
||||
}
|
||||
} FsmDetectPass;
|
||||
|
255
passes/fsm/fsm_expand.cc
Normal file
255
passes/fsm/fsm_expand.cc
Normal file
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
struct FsmExpand
|
||||
{
|
||||
RTLIL::Module *module;
|
||||
RTLIL::Cell *fsm_cell;
|
||||
SigMap assign_map;
|
||||
SigSet<RTLIL::Cell*> sig2driver, sig2user;
|
||||
CellTypes ct;
|
||||
|
||||
std::set<RTLIL::Cell*> merged_set;
|
||||
std::set<RTLIL::Cell*> current_set;
|
||||
std::set<RTLIL::Cell*> no_candidate_set;
|
||||
|
||||
bool already_optimized;
|
||||
int limit_transitions;
|
||||
|
||||
bool is_cell_merge_candidate(RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec new_signals;
|
||||
if (cell->connections.count("\\A") > 0)
|
||||
new_signals.append(assign_map(cell->connections["\\A"]));
|
||||
if (cell->connections.count("\\B") > 0)
|
||||
new_signals.append(assign_map(cell->connections["\\B"]));
|
||||
if (cell->connections.count("\\S") > 0)
|
||||
new_signals.append(assign_map(cell->connections["\\S"]));
|
||||
|
||||
new_signals.sort_and_unify();
|
||||
new_signals.remove_const();
|
||||
|
||||
if (new_signals.width > 4)
|
||||
return false;
|
||||
|
||||
new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"]));
|
||||
new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"]));
|
||||
|
||||
if (cell->connections.count("\\Y") > 0) {
|
||||
new_signals.append(assign_map(cell->connections["\\Y"]));
|
||||
new_signals.sort_and_unify();
|
||||
new_signals.remove_const();
|
||||
new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"]));
|
||||
new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"]));
|
||||
}
|
||||
|
||||
if (new_signals.width > 2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void create_current_set()
|
||||
{
|
||||
std::vector<RTLIL::Cell*> cell_list;
|
||||
|
||||
for (auto c : sig2driver.find(assign_map(fsm_cell->connections["\\CTRL_IN"])))
|
||||
cell_list.push_back(c);
|
||||
|
||||
for (auto c : sig2user.find(assign_map(fsm_cell->connections["\\CTRL_OUT"])))
|
||||
cell_list.push_back(c);
|
||||
|
||||
current_set.clear();
|
||||
for (auto c : cell_list)
|
||||
{
|
||||
if (merged_set.count(c) > 0 || current_set.count(c) > 0 || no_candidate_set.count(c) > 0)
|
||||
continue;
|
||||
for (auto &p : c->connections) {
|
||||
if (p.first != "\\A" && p.first != "\\B" && p.first != "\\S" && p.first != "\\Y")
|
||||
goto next_cell;
|
||||
}
|
||||
if (!is_cell_merge_candidate(c)) {
|
||||
no_candidate_set.insert(c);
|
||||
continue;
|
||||
}
|
||||
current_set.insert(c);
|
||||
next_cell:;
|
||||
}
|
||||
}
|
||||
|
||||
void optimze_as_needed()
|
||||
{
|
||||
if (already_optimized)
|
||||
return;
|
||||
|
||||
int trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
|
||||
if (trans_num > limit_transitions)
|
||||
{
|
||||
log(" grown transition table to %d entries -> optimize.\n", trans_num);
|
||||
FsmData::optimize_fsm(fsm_cell, module);
|
||||
already_optimized = true;
|
||||
|
||||
trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
|
||||
log(" transition table size after optimizaton: %d\n", trans_num);
|
||||
limit_transitions = 16 * trans_num;
|
||||
}
|
||||
}
|
||||
|
||||
void merge_cell_into_fsm(RTLIL::Cell *cell)
|
||||
{
|
||||
optimze_as_needed();
|
||||
|
||||
log(" merging %s cell %s.\n", cell->type.c_str(), cell->name.c_str());
|
||||
merged_set.insert(cell);
|
||||
already_optimized = false;
|
||||
|
||||
RTLIL::SigSpec input_sig, output_sig;
|
||||
|
||||
for (auto &p : cell->connections)
|
||||
if (ct.cell_output(cell->type, p.first))
|
||||
output_sig.append(assign_map(p.second));
|
||||
else
|
||||
input_sig.append(assign_map(p.second));
|
||||
input_sig.sort_and_unify();
|
||||
input_sig.remove_const();
|
||||
|
||||
assert(input_sig.width <= 4);
|
||||
std::vector<RTLIL::Const> truth_tab;
|
||||
|
||||
for (int i = 0; i < (1 << input_sig.width); i++) {
|
||||
RTLIL::Const in_val(i, input_sig.width);
|
||||
RTLIL::SigSpec A, B, S;
|
||||
if (cell->connections.count("\\A") > 0)
|
||||
A = assign_map(cell->connections["\\A"]);
|
||||
if (cell->connections.count("\\B") > 0)
|
||||
B = assign_map(cell->connections["\\B"]);
|
||||
if (cell->connections.count("\\S") > 0)
|
||||
S = assign_map(cell->connections["\\S"]);
|
||||
A.replace(input_sig, RTLIL::SigSpec(in_val));
|
||||
B.replace(input_sig, RTLIL::SigSpec(in_val));
|
||||
S.replace(input_sig, RTLIL::SigSpec(in_val));
|
||||
assert(A.is_fully_const());
|
||||
assert(B.is_fully_const());
|
||||
assert(S.is_fully_const());
|
||||
truth_tab.push_back(ct.eval(cell, A.as_const(), B.as_const(), S.as_const()));
|
||||
}
|
||||
|
||||
FsmData fsm_data;
|
||||
fsm_data.copy_from_cell(fsm_cell);
|
||||
|
||||
fsm_data.num_inputs += input_sig.width;
|
||||
fsm_cell->connections["\\CTRL_IN"].append(input_sig);
|
||||
|
||||
fsm_data.num_outputs += output_sig.width;
|
||||
fsm_cell->connections["\\CTRL_OUT"].append(output_sig);
|
||||
|
||||
std::vector<FsmData::transition_t> new_transition_table;
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
for (int i = 0; i < (1 << input_sig.width); i++) {
|
||||
FsmData::transition_t new_tr = tr;
|
||||
RTLIL::Const in_val(i, input_sig.width);
|
||||
RTLIL::Const out_val = truth_tab[i];
|
||||
RTLIL::SigSpec ctrl_in = new_tr.ctrl_in;
|
||||
RTLIL::SigSpec ctrl_out = new_tr.ctrl_out;
|
||||
ctrl_in.append(in_val);
|
||||
ctrl_out.append(out_val);
|
||||
new_tr.ctrl_in = ctrl_in.as_const();
|
||||
new_tr.ctrl_out = ctrl_out.as_const();
|
||||
new_transition_table.push_back(new_tr);
|
||||
}
|
||||
}
|
||||
fsm_data.transition_table.swap(new_transition_table);
|
||||
new_transition_table.clear();
|
||||
|
||||
fsm_data.copy_to_cell(fsm_cell);
|
||||
}
|
||||
|
||||
FsmExpand(RTLIL::Cell *cell, RTLIL::Module *mod)
|
||||
{
|
||||
module = mod;
|
||||
fsm_cell = cell;
|
||||
|
||||
assign_map.set(module);
|
||||
ct.setup_internals();
|
||||
|
||||
for (auto &cell_it : module->cells) {
|
||||
RTLIL::Cell *c = cell_it.second;
|
||||
if (ct.cell_known(c->type))
|
||||
for (auto &p : c->connections) {
|
||||
if (ct.cell_output(c->type, p.first))
|
||||
sig2driver.insert(assign_map(p.second), c);
|
||||
else
|
||||
sig2user.insert(assign_map(p.second), c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
log("\n");
|
||||
log("Expanding FSM `%s' from module `%s':\n", fsm_cell->name.c_str(), module->name.c_str());
|
||||
|
||||
already_optimized = false;
|
||||
limit_transitions = 16 * fsm_cell->parameters["\\TRANS_NUM"].as_int();
|
||||
|
||||
for (create_current_set(); current_set.size() > 0; create_current_set()) {
|
||||
for (auto c : current_set)
|
||||
merge_cell_into_fsm(c);
|
||||
}
|
||||
|
||||
for (auto c : merged_set) {
|
||||
module->cells.erase(c->name);
|
||||
delete c;
|
||||
}
|
||||
|
||||
if (merged_set.size() > 0 && !already_optimized)
|
||||
FsmData::optimize_fsm(fsm_cell, module);
|
||||
|
||||
log(" merged %zd cells into FSM.\n", merged_set.size());
|
||||
}
|
||||
};
|
||||
|
||||
struct FsmExpandPass : public Pass {
|
||||
FsmExpandPass() : Pass("fsm_expand") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_EXPAND pass (re-assigning FSM state encoding).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
std::vector<RTLIL::Cell*> fsm_cells;
|
||||
for (auto &cell_it : mod_it.second->cells)
|
||||
if (cell_it.second->type == "$fsm")
|
||||
fsm_cells.push_back(cell_it.second);
|
||||
for (auto c : fsm_cells) {
|
||||
FsmExpand fsm_expand(c, mod_it.second);
|
||||
fsm_expand.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
} FsmExpandPass;
|
||||
|
103
passes/fsm/fsm_export.cc
Normal file
103
passes/fsm/fsm_export.cc
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
/**
|
||||
* Convert signal into a KISS-compatible textual representation.
|
||||
*/
|
||||
std::string kiss_convert_signal(const RTLIL::SigSpec &sig) {
|
||||
if (!sig.is_fully_const()) {
|
||||
throw 0;
|
||||
}
|
||||
|
||||
return sig.as_const().as_string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports each Finite State Machine (FSM) in the design to a file in KISS2 format.
|
||||
*/
|
||||
struct FsmExportPass : public Pass {
|
||||
FsmExportPass() : Pass("fsm_export") {
|
||||
}
|
||||
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
FsmData fsm_data;
|
||||
std::string kiss_name;
|
||||
std::ofstream kiss_file;
|
||||
size_t i;
|
||||
FsmData::transition_t tr;
|
||||
|
||||
log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &cell_it : mod_it.second->cells)
|
||||
if (cell_it.second->type == "$fsm") {
|
||||
kiss_name.assign(mod_it.first.c_str());
|
||||
kiss_name.append("-" + cell_it.second->name + ".kiss2");
|
||||
fsm_data.copy_from_cell(cell_it.second);
|
||||
|
||||
log("\n");
|
||||
log("Exporting FSM `%s' from module `%s' to file `%s'.\n",
|
||||
cell_it.second->name.c_str(),
|
||||
mod_it.first.c_str(),
|
||||
kiss_name.c_str());
|
||||
|
||||
kiss_file.open(kiss_name, std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!kiss_file.is_open()) {
|
||||
log_error("Could not open file \"%s\" with write access.\n", kiss_name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
kiss_file << ".start_kiss" << std::endl;
|
||||
kiss_file << ".i " << std::dec << fsm_data.num_inputs << std::endl;
|
||||
kiss_file << ".o " << std::dec << fsm_data.num_outputs << std::endl;
|
||||
kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl;
|
||||
|
||||
for (i = 0; i < fsm_data.transition_table.size(); i++) {
|
||||
tr = fsm_data.transition_table[i];
|
||||
|
||||
try {
|
||||
kiss_file << kiss_convert_signal(tr.ctrl_in) << ' ';
|
||||
kiss_file << 's' << tr.state_in << ' ';
|
||||
kiss_file << 's' << tr.state_out << ' ';
|
||||
kiss_file << kiss_convert_signal(tr.ctrl_out) << std::endl;
|
||||
}
|
||||
catch (int) {
|
||||
log_error("exporting an FSM input or output signal failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
kiss_file << ".end_kiss" << std::endl << ".end" << std::endl;
|
||||
kiss_file.close();
|
||||
}
|
||||
}
|
||||
} FsmExportPass;
|
359
passes/fsm/fsm_extract.cc
Normal file
359
passes/fsm/fsm_extract.cc
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
|
||||
static RTLIL::Module *module;
|
||||
static SigMap assign_map;
|
||||
typedef std::pair<RTLIL::Cell*,std::string> sig2driver_entry_t;
|
||||
static SigSet<sig2driver_entry_t> sig2driver, sig2trigger;
|
||||
|
||||
static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL::SigSpec &ctrl, std::map<RTLIL::Const, int> &states, RTLIL::Const *reset_state = NULL)
|
||||
{
|
||||
sig.extend(dff_out.width, false);
|
||||
|
||||
if (sig == dff_out)
|
||||
return true;
|
||||
|
||||
assign_map.apply(sig);
|
||||
if (sig.is_fully_const()) {
|
||||
sig.optimize();
|
||||
assert(sig.chunks.size() == 1);
|
||||
if (states.count(sig.chunks[0].data) == 0) {
|
||||
log(" found state code: %s\n", log_signal(sig));
|
||||
states[sig.chunks[0].data] = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::set<sig2driver_entry_t> cellport_list;
|
||||
sig2driver.find(sig, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y") {
|
||||
log(" unexpected cell type %s (%s) found in state selection tree.\n",
|
||||
cellport.first->type.c_str(), cellport.first->name.c_str());
|
||||
return false;
|
||||
}
|
||||
RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
|
||||
RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
|
||||
RTLIL::SigSpec sig_s = assign_map(cellport.first->connections["\\S"]);
|
||||
if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef())
|
||||
do {
|
||||
if (sig_a.is_fully_def())
|
||||
*reset_state = sig_a.as_const();
|
||||
else if (sig_b.is_fully_def())
|
||||
*reset_state = sig_b.as_const();
|
||||
else
|
||||
break;
|
||||
log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state));
|
||||
} while (0);
|
||||
if (ctrl.extract(sig_s).width == 0) {
|
||||
log(" found ctrl input: %s\n", log_signal(sig_s));
|
||||
ctrl.append(sig_s);
|
||||
}
|
||||
if (!find_states(sig_a, dff_out, ctrl, states))
|
||||
return false;
|
||||
for (int i = 0; i < sig_b.width/sig_a.width; i++) {
|
||||
if (!find_states(sig_b.extract(i*sig_a.width, sig_a.width), dff_out, ctrl, states))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State noconst_state, RTLIL::SigSpec dont_care = RTLIL::SigSpec())
|
||||
{
|
||||
if (dont_care.width > 0) {
|
||||
sig.expand();
|
||||
for (auto &chunk : sig.chunks) {
|
||||
assert(chunk.width == 1);
|
||||
if (dont_care.extract(chunk).width > 0)
|
||||
chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state);
|
||||
}
|
||||
sig.optimize();
|
||||
}
|
||||
|
||||
ce.assign_map.apply(sig);
|
||||
ce.values_map.apply(sig);
|
||||
|
||||
sig.expand();
|
||||
for (auto &chunk : sig.chunks) {
|
||||
assert(chunk.width == 1);
|
||||
if (chunk.wire != NULL)
|
||||
chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state);
|
||||
}
|
||||
sig.optimize();
|
||||
|
||||
if (sig.width == 0)
|
||||
return RTLIL::Const();
|
||||
assert(sig.chunks.size() == 1 && sig.chunks[0].wire == NULL);
|
||||
return sig.chunks[0].data;
|
||||
}
|
||||
|
||||
static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_data, std::map<RTLIL::Const, int> &states, int state_in, RTLIL::SigSpec ctrl_in, RTLIL::SigSpec ctrl_out, RTLIL::SigSpec dff_in, RTLIL::SigSpec dont_care)
|
||||
{
|
||||
RTLIL::SigSpec undef, constval;
|
||||
|
||||
if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef)) {
|
||||
assert(ctrl_out.is_fully_const() && dff_in.is_fully_const());
|
||||
FsmData::transition_t tr;
|
||||
tr.state_in = state_in;
|
||||
tr.state_out = states[ce.values_map(ce.assign_map(dff_in)).as_const()];
|
||||
tr.ctrl_in = sig2const(ce, ctrl_in, RTLIL::State::Sa, dont_care);
|
||||
tr.ctrl_out = sig2const(ce, ctrl_out, RTLIL::State::Sx);
|
||||
RTLIL::Const log_state_in = RTLIL::Const(RTLIL::State::Sx, fsm_data.state_bits);
|
||||
if (state_in >= 0)
|
||||
log_state_in = fsm_data.state_table[tr.state_in];
|
||||
if (dff_in.is_fully_def()) {
|
||||
fsm_data.transition_table.push_back(tr);
|
||||
log(" transition: %10s %s -> %10s %s\n",
|
||||
log_signal(log_state_in), log_signal(tr.ctrl_in),
|
||||
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
|
||||
} else {
|
||||
log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\n",
|
||||
log_signal(log_state_in), log_signal(tr.ctrl_in),
|
||||
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
assert(undef.width > 0);
|
||||
assert(ce.stop_signals.check_all(undef));
|
||||
|
||||
undef = undef.extract(0, 1);
|
||||
constval = undef;
|
||||
|
||||
if (ce_nostop.eval(constval))
|
||||
{
|
||||
ce.push();
|
||||
dont_care.append(undef);
|
||||
ce.set(undef, constval.as_const());
|
||||
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
|
||||
ce.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
ce.push(), ce_nostop.push();
|
||||
ce.set(undef, RTLIL::Const(0, 1));
|
||||
ce_nostop.set(undef, RTLIL::Const(0, 1));
|
||||
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
|
||||
ce.pop(), ce_nostop.pop();
|
||||
|
||||
ce.push(), ce_nostop.push();
|
||||
ce.set(undef, RTLIL::Const(1, 1));
|
||||
ce_nostop.set(undef, RTLIL::Const(1, 1));
|
||||
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
|
||||
ce.pop(), ce_nostop.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_fsm(RTLIL::Wire *wire)
|
||||
{
|
||||
log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str());
|
||||
|
||||
// get input and output signals for state ff
|
||||
|
||||
RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire));
|
||||
RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width);
|
||||
RTLIL::Const reset_state(RTLIL::State::Sx, wire->width);
|
||||
|
||||
RTLIL::SigSpec clk = RTLIL::SigSpec(0, 1);
|
||||
RTLIL::SigSpec arst = RTLIL::SigSpec(0, 1);
|
||||
bool clk_polarity = true;
|
||||
bool arst_polarity = true;
|
||||
|
||||
std::set<sig2driver_entry_t> cellport_list;
|
||||
sig2driver.find(dff_out, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
|
||||
continue;
|
||||
log(" found %s cell for state register: %s\n", cellport.first->type.c_str(), cellport.first->name.c_str());
|
||||
RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]);
|
||||
RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]);
|
||||
clk = cellport.first->connections["\\CLK"];
|
||||
clk_polarity = cellport.first->parameters["\\CLK_POLARITY"].as_bool();
|
||||
if (cellport.first->type == "$adff") {
|
||||
arst = cellport.first->connections["\\ARST"];
|
||||
arst_polarity = cellport.first->parameters["\\ARST_POLARITY"].as_bool();
|
||||
reset_state = cellport.first->parameters["\\ARST_VALUE"];
|
||||
}
|
||||
sig_q.replace(dff_out, sig_d, &dff_in);
|
||||
break;
|
||||
}
|
||||
|
||||
log(" root of input selection tree: %s\n", log_signal(dff_in));
|
||||
if (dff_in.has_marked_bits()) {
|
||||
log(" fsm extraction failed: incomplete input selection tree root.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// find states and control inputs
|
||||
|
||||
RTLIL::SigSpec ctrl_in;
|
||||
std::map<RTLIL::Const, int> states;
|
||||
if (!arst.is_fully_const()) {
|
||||
log(" found reset state: %s (from async reset)\n", log_signal(reset_state));
|
||||
states[reset_state] = -1;
|
||||
}
|
||||
if (!find_states(dff_in, dff_out, ctrl_in, states, &reset_state)) {
|
||||
log(" fsm extraction failed: state selection tree is not closed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// find control outputs
|
||||
// (add the state signals to the list of control outputs. if everything goes right, this signals
|
||||
// become unused and can then be removed from the fsm control output)
|
||||
|
||||
RTLIL::SigSpec ctrl_out = dff_in;
|
||||
cellport_list.clear();
|
||||
sig2trigger.find(dff_out, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]);
|
||||
RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]);
|
||||
RTLIL::SigSpec sig_y = assign_map(cellport.first->connections["\\Y"]);
|
||||
if (cellport.second == "\\A" && !sig_b.is_fully_const())
|
||||
continue;
|
||||
if (cellport.second == "\\B" && !sig_a.is_fully_const())
|
||||
continue;
|
||||
log(" found ctrl output: %s\n", log_signal(sig_y));
|
||||
ctrl_out.append(sig_y);
|
||||
}
|
||||
ctrl_in.remove(ctrl_out);
|
||||
|
||||
log(" ctrl inputs: %s\n", log_signal(ctrl_in));
|
||||
log(" ctrl outputs: %s\n", log_signal(ctrl_out));
|
||||
|
||||
// Initialize fsm data struct
|
||||
|
||||
FsmData fsm_data;
|
||||
fsm_data.num_inputs = ctrl_in.width;
|
||||
fsm_data.num_outputs = ctrl_out.width;
|
||||
fsm_data.state_bits = wire->width;
|
||||
fsm_data.reset_state = -1;
|
||||
for (auto &it : states) {
|
||||
it.second = fsm_data.state_table.size();
|
||||
fsm_data.state_table.push_back(it.first);
|
||||
}
|
||||
if (!arst.is_fully_const() || RTLIL::SigSpec(reset_state).is_fully_def())
|
||||
fsm_data.reset_state = states[reset_state];
|
||||
|
||||
// Create transition table
|
||||
|
||||
ConstEval ce(module), ce_nostop(module);
|
||||
ce.stop(ctrl_in);
|
||||
for (int state_idx = 0; state_idx < int(fsm_data.state_table.size()); state_idx++) {
|
||||
ce.push(), ce_nostop.push();
|
||||
ce.set(dff_out, fsm_data.state_table[state_idx]);
|
||||
ce_nostop.set(dff_out, fsm_data.state_table[state_idx]);
|
||||
find_transitions(ce, ce_nostop, fsm_data, states, state_idx, ctrl_in, ctrl_out, dff_in, RTLIL::SigSpec());
|
||||
ce.pop(), ce_nostop.pop();
|
||||
}
|
||||
|
||||
// create fsm cell
|
||||
|
||||
RTLIL::Cell *fsm_cell = new RTLIL::Cell;
|
||||
fsm_cell->name = stringf("$fsm$%s$%d", wire->name.c_str(), RTLIL::autoidx++);
|
||||
fsm_cell->type = "$fsm";
|
||||
fsm_cell->connections["\\CLK"] = clk;
|
||||
fsm_cell->connections["\\ARST"] = arst;
|
||||
fsm_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity ? 1 : 0, 1);
|
||||
fsm_cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity ? 1 : 0, 1);
|
||||
fsm_cell->connections["\\CTRL_IN"] = ctrl_in;
|
||||
fsm_cell->connections["\\CTRL_OUT"] = ctrl_out;
|
||||
fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name);
|
||||
fsm_data.copy_to_cell(fsm_cell);
|
||||
module->cells[fsm_cell->name] = fsm_cell;
|
||||
|
||||
// rename original state wire
|
||||
|
||||
module->wires.erase(wire->name);
|
||||
wire->attributes.erase("\\fsm_encoding");
|
||||
wire->name = stringf("$fsm$oldstate%s", wire->name.c_str());
|
||||
module->wires[wire->name] = wire;
|
||||
|
||||
// unconnect control outputs from old drivers
|
||||
|
||||
cellport_list.clear();
|
||||
sig2driver.find(ctrl_out, cellport_list);
|
||||
for (auto &cellport : cellport_list) {
|
||||
RTLIL::SigSpec port_sig = assign_map(cellport.first->connections[cellport.second]);
|
||||
RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out);
|
||||
RTLIL::Wire *unconn_wire = new RTLIL::Wire;
|
||||
unconn_wire->name = stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), RTLIL::autoidx++);
|
||||
unconn_wire->width = unconn_sig.width;
|
||||
module->wires[unconn_wire->name] = unconn_wire;
|
||||
port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cellport.first->connections[cellport.second]);
|
||||
}
|
||||
}
|
||||
|
||||
struct FsmExtractPass : public Pass {
|
||||
FsmExtractPass() : Pass("fsm_extract") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
CellTypes ct;
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
module = mod_it.second;
|
||||
assign_map.set(module);
|
||||
|
||||
sig2driver.clear();
|
||||
sig2trigger.clear();
|
||||
for (auto &cell_it : module->cells)
|
||||
for (auto &conn_it : cell_it.second->connections) {
|
||||
if (ct.cell_output(cell_it.second->type, conn_it.first)) {
|
||||
RTLIL::SigSpec sig = conn_it.second;
|
||||
assign_map.apply(sig);
|
||||
sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
|
||||
}
|
||||
if (ct.cell_input(cell_it.second->type, conn_it.first) && cell_it.second->connections.count("\\Y") > 0 &&
|
||||
cell_it.second->connections["\\Y"].width == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) {
|
||||
RTLIL::SigSpec sig = conn_it.second;
|
||||
assign_map.apply(sig);
|
||||
sig2trigger.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RTLIL::Wire*> wire_list;
|
||||
for (auto &wire_it : module->wires)
|
||||
if (wire_it.second->attributes.count("\\fsm_encoding") > 0 && wire_it.second->attributes["\\fsm_encoding"].str != "none")
|
||||
wire_list.push_back(wire_it.second);
|
||||
for (auto wire : wire_list)
|
||||
extract_fsm(wire);
|
||||
}
|
||||
|
||||
assign_map.clear();
|
||||
sig2driver.clear();
|
||||
sig2trigger.clear();
|
||||
}
|
||||
} FsmExtractPass;
|
||||
|
46
passes/fsm/fsm_info.cc
Normal file
46
passes/fsm/fsm_info.cc
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
struct FsmInfoPass : public Pass {
|
||||
FsmInfoPass() : Pass("fsm_info") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &cell_it : mod_it.second->cells)
|
||||
if (cell_it.second->type == "$fsm") {
|
||||
log("\n");
|
||||
log("FSM `%s' from module `%s':\n", cell_it.second->name.c_str(), mod_it.first.c_str());
|
||||
FsmData fsm_data;
|
||||
fsm_data.copy_from_cell(cell_it.second);
|
||||
fsm_data.log_info(cell_it.second);
|
||||
}
|
||||
}
|
||||
} FsmInfoPass;
|
||||
|
356
passes/fsm/fsm_map.cc
Normal file
356
passes/fsm/fsm_map.cc
Normal file
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const, std::set<int>> &pattern_cache, std::set<int> &fullstate_cache, int num_states, RTLIL::Wire *state_onehot, RTLIL::SigSpec &ctrl_in, RTLIL::SigSpec output)
|
||||
{
|
||||
RTLIL::SigSpec cases_vector;
|
||||
|
||||
for (int in_state : fullstate_cache)
|
||||
cases_vector.append(RTLIL::SigSpec(state_onehot, 1, in_state));
|
||||
|
||||
for (auto &it : pattern_cache)
|
||||
{
|
||||
RTLIL::Const pattern = it.first;
|
||||
RTLIL::SigSpec eq_sig_a, eq_sig_b, or_sig;
|
||||
|
||||
for (size_t j = 0; j < pattern.bits.size(); j++)
|
||||
if (pattern.bits[j] == RTLIL::State::S0 || pattern.bits[j] == RTLIL::State::S1) {
|
||||
eq_sig_a.append(ctrl_in.extract(j, 1));
|
||||
eq_sig_b.append(RTLIL::SigSpec(pattern.bits[j]));
|
||||
}
|
||||
eq_sig_a.optimize();
|
||||
eq_sig_b.optimize();
|
||||
|
||||
for (int in_state : it.second)
|
||||
if (fullstate_cache.count(in_state) == 0)
|
||||
or_sig.append(RTLIL::SigSpec(state_onehot, 1, in_state));
|
||||
or_sig.optimize();
|
||||
|
||||
if (or_sig.width == 0)
|
||||
continue;
|
||||
|
||||
RTLIL::SigSpec and_sig;
|
||||
|
||||
if (eq_sig_a.width > 0)
|
||||
{
|
||||
RTLIL::Wire *eq_wire = new RTLIL::Wire;
|
||||
eq_wire->name = NEW_ID;
|
||||
module->add(eq_wire);
|
||||
|
||||
RTLIL::Cell *eq_cell = new RTLIL::Cell;
|
||||
eq_cell->name = NEW_ID;
|
||||
eq_cell->type = "$eq";
|
||||
eq_cell->connections["\\A"] = eq_sig_a;
|
||||
eq_cell->connections["\\B"] = eq_sig_b;
|
||||
eq_cell->connections["\\Y"] = RTLIL::SigSpec(eq_wire);
|
||||
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
|
||||
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
|
||||
eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.width);
|
||||
eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.width);
|
||||
eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->add(eq_cell);
|
||||
|
||||
and_sig.append(RTLIL::SigSpec(eq_wire));
|
||||
}
|
||||
|
||||
if (or_sig.width == 1)
|
||||
{
|
||||
and_sig.append(or_sig);
|
||||
}
|
||||
else if (or_sig.width < num_states && int(it.second.size()) < num_states)
|
||||
{
|
||||
RTLIL::Wire *or_wire = new RTLIL::Wire;
|
||||
or_wire->name = NEW_ID;
|
||||
module->add(or_wire);
|
||||
|
||||
RTLIL::Cell *or_cell = new RTLIL::Cell;
|
||||
or_cell->name = NEW_ID;
|
||||
or_cell->type = "$reduce_or";
|
||||
or_cell->connections["\\A"] = or_sig;
|
||||
or_cell->connections["\\Y"] = RTLIL::SigSpec(or_wire);
|
||||
or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
|
||||
or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.width);
|
||||
or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->add(or_cell);
|
||||
|
||||
and_sig.append(RTLIL::SigSpec(or_wire));
|
||||
}
|
||||
|
||||
switch (and_sig.width)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
RTLIL::Wire *and_wire = new RTLIL::Wire;
|
||||
and_wire->name = NEW_ID;
|
||||
module->add(and_wire);
|
||||
|
||||
RTLIL::Cell *and_cell = new RTLIL::Cell;
|
||||
and_cell->name = NEW_ID;
|
||||
and_cell->type = "$and";
|
||||
and_cell->connections["\\A"] = and_sig.extract(0, 1);
|
||||
and_cell->connections["\\B"] = and_sig.extract(1, 1);
|
||||
and_cell->connections["\\Y"] = RTLIL::SigSpec(and_wire);
|
||||
and_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
|
||||
and_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
|
||||
and_cell->parameters["\\A_WIDTH"] = RTLIL::Const(1);
|
||||
and_cell->parameters["\\B_WIDTH"] = RTLIL::Const(1);
|
||||
and_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->add(and_cell);
|
||||
|
||||
cases_vector.append(RTLIL::SigSpec(and_wire));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
cases_vector.append(and_sig);
|
||||
break;
|
||||
case 0:
|
||||
cases_vector.append(RTLIL::SigSpec(1, 1));
|
||||
break;
|
||||
default:
|
||||
assert(!"This should never happen!");
|
||||
}
|
||||
}
|
||||
|
||||
if (cases_vector.width > 1) {
|
||||
RTLIL::Cell *or_cell = new RTLIL::Cell;
|
||||
or_cell->name = NEW_ID;
|
||||
or_cell->type = "$reduce_or";
|
||||
or_cell->connections["\\A"] = cases_vector;
|
||||
or_cell->connections["\\Y"] = output;
|
||||
or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
|
||||
or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.width);
|
||||
or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->add(or_cell);
|
||||
} else if (cases_vector.width == 1) {
|
||||
module->connections.push_back(RTLIL::SigSig(output, cases_vector));
|
||||
} else {
|
||||
module->connections.push_back(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module)
|
||||
{
|
||||
log("Mapping FSM `%s' from module `%s'.\n", fsm_cell->name.c_str(), module->name.c_str());
|
||||
|
||||
FsmData fsm_data;
|
||||
fsm_data.copy_from_cell(fsm_cell);
|
||||
|
||||
RTLIL::SigSpec ctrl_in = fsm_cell->connections["\\CTRL_IN"];
|
||||
RTLIL::SigSpec ctrl_out = fsm_cell->connections["\\CTRL_OUT"];
|
||||
|
||||
// create state register
|
||||
|
||||
RTLIL::Wire *state_wire = new RTLIL::Wire;
|
||||
state_wire->name = fsm_cell->parameters["\\NAME"].str;
|
||||
while (module->count_id(state_wire->name) > 0)
|
||||
state_wire->name += "_";
|
||||
state_wire->width = fsm_data.state_bits;
|
||||
module->add(state_wire);
|
||||
|
||||
RTLIL::Wire *next_state_wire = new RTLIL::Wire;
|
||||
next_state_wire->name = NEW_ID;
|
||||
next_state_wire->width = fsm_data.state_bits;
|
||||
module->add(next_state_wire);
|
||||
|
||||
RTLIL::Cell *state_dff = new RTLIL::Cell;
|
||||
state_dff->name = NEW_ID;
|
||||
if (fsm_cell->connections["\\ARST"].is_fully_const()) {
|
||||
state_dff->type = "$dff";
|
||||
} else {
|
||||
state_dff->type = "$adff";
|
||||
state_dff->parameters["\\ARST_POLARITY"] = fsm_cell->parameters["\\ARST_POLARITY"];
|
||||
state_dff->parameters["\\ARST_VALUE"] = fsm_data.state_table[fsm_data.reset_state];
|
||||
state_dff->connections["\\ARST"] = fsm_cell->connections["\\ARST"];
|
||||
}
|
||||
state_dff->parameters["\\WIDTH"] = RTLIL::Const(fsm_data.state_bits);
|
||||
state_dff->parameters["\\CLK_POLARITY"] = fsm_cell->parameters["\\CLK_POLARITY"];
|
||||
state_dff->connections["\\CLK"] = fsm_cell->connections["\\CLK"];
|
||||
state_dff->connections["\\D"] = RTLIL::SigSpec(next_state_wire);
|
||||
state_dff->connections["\\Q"] = RTLIL::SigSpec(state_wire);
|
||||
module->add(state_dff);
|
||||
|
||||
// decode state register
|
||||
|
||||
bool encoding_is_onehot = true;
|
||||
|
||||
RTLIL::Wire *state_onehot = new RTLIL::Wire;
|
||||
state_onehot->name = NEW_ID;
|
||||
state_onehot->width = fsm_data.state_table.size();
|
||||
module->add(state_onehot);
|
||||
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++)
|
||||
{
|
||||
RTLIL::Const state = fsm_data.state_table[i];
|
||||
RTLIL::SigSpec sig_a, sig_b;
|
||||
|
||||
for (size_t j = 0; j < state.bits.size(); j++)
|
||||
if (state.bits[j] == RTLIL::State::S0 || state.bits[j] == RTLIL::State::S1) {
|
||||
sig_a.append(RTLIL::SigSpec(state_wire, 1, j));
|
||||
sig_b.append(RTLIL::SigSpec(state.bits[j]));
|
||||
}
|
||||
sig_a.optimize();
|
||||
sig_b.optimize();
|
||||
|
||||
if (sig_b == RTLIL::SigSpec(RTLIL::State::S1))
|
||||
{
|
||||
module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, 1, i), sig_a));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sig_b.as_bool() || sig_b.width != fsm_data.state_bits)
|
||||
encoding_is_onehot = false;
|
||||
|
||||
RTLIL::Cell *eq_cell = new RTLIL::Cell;
|
||||
eq_cell->name = NEW_ID;
|
||||
eq_cell->type = "$eq";
|
||||
eq_cell->connections["\\A"] = sig_a;
|
||||
eq_cell->connections["\\B"] = sig_b;
|
||||
eq_cell->connections["\\Y"] = RTLIL::SigSpec(state_onehot, 1, i);
|
||||
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
|
||||
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
|
||||
eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.width);
|
||||
eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.width);
|
||||
eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->add(eq_cell);
|
||||
}
|
||||
}
|
||||
|
||||
// generate next_state signal
|
||||
|
||||
RTLIL::Wire *next_state_onehot = new RTLIL::Wire;
|
||||
next_state_onehot->name = NEW_ID;
|
||||
next_state_onehot->width = fsm_data.state_table.size();
|
||||
module->add(next_state_onehot);
|
||||
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++)
|
||||
{
|
||||
std::map<RTLIL::Const, std::set<int>> pattern_cache;
|
||||
std::set<int> fullstate_cache;
|
||||
|
||||
for (size_t j = 0; j < fsm_data.state_table.size(); j++)
|
||||
fullstate_cache.insert(j);
|
||||
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
if (tr.state_out == int(i))
|
||||
pattern_cache[tr.ctrl_in].insert(tr.state_in);
|
||||
else
|
||||
fullstate_cache.erase(tr.state_in);
|
||||
}
|
||||
|
||||
implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, 1, i));
|
||||
}
|
||||
|
||||
if (encoding_is_onehot)
|
||||
{
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
|
||||
RTLIL::Const state = fsm_data.state_table[i];
|
||||
int bit_idx = -1;
|
||||
for (size_t j = 0; j < state.bits.size(); j++)
|
||||
if (state.bits[j] == RTLIL::State::S1)
|
||||
bit_idx = j;
|
||||
if (bit_idx >= 0)
|
||||
module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(next_state_wire, 1, bit_idx), RTLIL::SigSpec(next_state_onehot, 1, i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::SigSpec sig_a, sig_b, sig_s;
|
||||
int reset_state = fsm_data.reset_state;
|
||||
if (reset_state < 0)
|
||||
reset_state = 0;
|
||||
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
|
||||
RTLIL::Const state = fsm_data.state_table[i];
|
||||
if (int(i) == fsm_data.reset_state) {
|
||||
sig_a = RTLIL::SigSpec(state);
|
||||
} else {
|
||||
sig_b.append(RTLIL::SigSpec(state));
|
||||
sig_s.append(RTLIL::SigSpec(next_state_onehot, 1, i));
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *mux_cell = new RTLIL::Cell;
|
||||
mux_cell->name = NEW_ID;
|
||||
mux_cell->type = "$safe_pmux";
|
||||
mux_cell->connections["\\A"] = sig_a;
|
||||
mux_cell->connections["\\B"] = sig_b;
|
||||
mux_cell->connections["\\S"] = sig_s;
|
||||
mux_cell->connections["\\Y"] = RTLIL::SigSpec(next_state_wire);
|
||||
mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.width);
|
||||
mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.width);
|
||||
module->add(mux_cell);
|
||||
}
|
||||
|
||||
// Generate ctrl_out signal
|
||||
|
||||
RTLIL::Wire *ctrl_out_wire = new RTLIL::Wire;
|
||||
ctrl_out_wire->name = NEW_ID;
|
||||
ctrl_out_wire->width = fsm_data.num_outputs;
|
||||
module->add(ctrl_out_wire);
|
||||
|
||||
for (int i = 0; i < fsm_data.num_outputs; i++)
|
||||
{
|
||||
std::map<RTLIL::Const, std::set<int>> pattern_cache;
|
||||
std::set<int> fullstate_cache;
|
||||
|
||||
for (size_t j = 0; j < fsm_data.state_table.size(); j++)
|
||||
fullstate_cache.insert(j);
|
||||
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
if (tr.ctrl_out.bits[i] == RTLIL::State::S1)
|
||||
pattern_cache[tr.ctrl_in].insert(tr.state_in);
|
||||
else
|
||||
fullstate_cache.erase(tr.state_in);
|
||||
}
|
||||
|
||||
implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, ctrl_out.extract(i, 1));
|
||||
}
|
||||
|
||||
// Remove FSM cell
|
||||
|
||||
module->cells.erase(fsm_cell->name);
|
||||
delete fsm_cell;
|
||||
}
|
||||
|
||||
struct FsmMapPass : public Pass {
|
||||
FsmMapPass() : Pass("fsm_map") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_MAP pass (simple optimizations of FSMs).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
std::vector<RTLIL::Cell*> fsm_cells;
|
||||
for (auto &cell_it : mod_it.second->cells)
|
||||
if (cell_it.second->type == "$fsm")
|
||||
fsm_cells.push_back(cell_it.second);
|
||||
for (auto cell : fsm_cells)
|
||||
map_fsm(cell, mod_it.second);
|
||||
}
|
||||
}
|
||||
} FsmMapPass;
|
||||
|
285
passes/fsm/fsm_opt.cc
Normal file
285
passes/fsm/fsm_opt.cc
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
struct FsmOpt
|
||||
{
|
||||
FsmData fsm_data;
|
||||
RTLIL::Cell *cell;
|
||||
RTLIL::Module *module;
|
||||
|
||||
bool signal_is_unused(RTLIL::SigSpec sig)
|
||||
{
|
||||
assert(sig.width == 1);
|
||||
sig.optimize();
|
||||
|
||||
RTLIL::Wire *wire = sig.chunks[0].wire;
|
||||
int bit = sig.chunks[0].offset;
|
||||
|
||||
if (!wire || wire->attributes.count("\\unused_bits") == 0)
|
||||
return false;
|
||||
|
||||
char *str = strdup(wire->attributes["\\unused_bits"].str.c_str());
|
||||
for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
|
||||
if (tok[0] && bit == atoi(tok))
|
||||
return true;
|
||||
}
|
||||
free(str);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void opt_const_and_unused_inputs()
|
||||
{
|
||||
RTLIL::SigSpec ctrl_in = cell->connections["\\CTRL_IN"];
|
||||
std::vector<bool> ctrl_in_used(ctrl_in.width);
|
||||
|
||||
std::vector<FsmData::transition_t> new_transition_table;
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
for (int i = 0; i < ctrl_in.width; i++) {
|
||||
RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1);
|
||||
if (ctrl_bit.is_fully_const()) {
|
||||
if (tr.ctrl_in.bits[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in.bits[i]) != ctrl_bit)
|
||||
goto delete_this_transition;
|
||||
continue;
|
||||
}
|
||||
if (tr.ctrl_in.bits[i] <= RTLIL::State::S1)
|
||||
ctrl_in_used[i] = true;
|
||||
}
|
||||
new_transition_table.push_back(tr);
|
||||
delete_this_transition:;
|
||||
}
|
||||
|
||||
for (int i = int(ctrl_in_used.size())-1; i >= 0; i--) {
|
||||
if (!ctrl_in_used[i]) {
|
||||
log(" Removing unused input signal %s.\n", log_signal(cell->connections["\\CTRL_IN"].extract(i, 1)));
|
||||
for (auto &tr : new_transition_table) {
|
||||
RTLIL::SigSpec tmp(tr.ctrl_in);
|
||||
tmp.remove(i, 1);
|
||||
tr.ctrl_in = tmp.as_const();
|
||||
}
|
||||
cell->connections["\\CTRL_IN"].remove(i, 1);
|
||||
fsm_data.num_inputs--;
|
||||
}
|
||||
}
|
||||
|
||||
fsm_data.transition_table.swap(new_transition_table);
|
||||
new_transition_table.clear();
|
||||
}
|
||||
|
||||
void opt_unused_outputs()
|
||||
{
|
||||
for (int i = 0; i < fsm_data.num_outputs; i++) {
|
||||
RTLIL::SigSpec sig = cell->connections["\\CTRL_OUT"].extract(i, 1);
|
||||
if (signal_is_unused(sig)) {
|
||||
log(" Removing unused output signal %s.\n", log_signal(sig));
|
||||
cell->connections["\\CTRL_OUT"].remove(i, 1);
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
RTLIL::SigSpec tmp(tr.ctrl_out);
|
||||
tmp.remove(i, 1);
|
||||
tr.ctrl_out = tmp.as_const();
|
||||
}
|
||||
fsm_data.num_outputs--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void opt_alias_inputs()
|
||||
{
|
||||
RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"];
|
||||
|
||||
for (int i = 0; i < ctrl_in.width; i++)
|
||||
for (int j = i+1; j < ctrl_in.width; j++)
|
||||
if (ctrl_in.extract(i, 1) == ctrl_in.extract(j, 1))
|
||||
{
|
||||
log(" Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
|
||||
std::vector<FsmData::transition_t> new_transition_table;
|
||||
|
||||
for (auto tr : fsm_data.transition_table)
|
||||
{
|
||||
RTLIL::State &si = tr.ctrl_in.bits[i];
|
||||
RTLIL::State &sj = tr.ctrl_in.bits[j];
|
||||
|
||||
if (si > RTLIL::State::S1)
|
||||
si = sj;
|
||||
else if (sj > RTLIL::State::S1)
|
||||
sj = si;
|
||||
|
||||
if (si == sj) {
|
||||
RTLIL::SigSpec tmp(tr.ctrl_in);
|
||||
tmp.remove(j, 1);
|
||||
tr.ctrl_in = tmp.as_const();
|
||||
new_transition_table.push_back(tr);
|
||||
}
|
||||
}
|
||||
|
||||
ctrl_in.remove(j--, 1);
|
||||
fsm_data.num_inputs--;
|
||||
|
||||
fsm_data.transition_table.swap(new_transition_table);
|
||||
new_transition_table.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void opt_feedback_inputs()
|
||||
{
|
||||
RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"];
|
||||
RTLIL::SigSpec &ctrl_out = cell->connections["\\CTRL_OUT"];
|
||||
|
||||
for (int j = 0; j < ctrl_out.width; j++)
|
||||
for (int i = 0; i < ctrl_in.width; i++)
|
||||
if (ctrl_in.extract(i, 1) == ctrl_out.extract(j, 1))
|
||||
{
|
||||
log(" Optimize handling of signal %s that is connected to input %d and output %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
|
||||
std::vector<FsmData::transition_t> new_transition_table;
|
||||
|
||||
for (auto tr : fsm_data.transition_table)
|
||||
{
|
||||
RTLIL::State &si = tr.ctrl_in.bits[i];
|
||||
RTLIL::State &sj = tr.ctrl_out.bits[j];
|
||||
|
||||
if (si > RTLIL::State::S1 || si == sj) {
|
||||
RTLIL::SigSpec tmp(tr.ctrl_in);
|
||||
tmp.remove(i, 1);
|
||||
tr.ctrl_in = tmp.as_const();
|
||||
new_transition_table.push_back(tr);
|
||||
}
|
||||
}
|
||||
|
||||
ctrl_in.remove(i--, 1);
|
||||
fsm_data.num_inputs--;
|
||||
|
||||
fsm_data.transition_table.swap(new_transition_table);
|
||||
new_transition_table.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void opt_find_dont_care_worker(std::set<RTLIL::Const> &set, int bit, FsmData::transition_t &tr, bool &did_something)
|
||||
{
|
||||
std::set<RTLIL::Const> new_set;
|
||||
|
||||
for (auto &pattern : set)
|
||||
{
|
||||
if (pattern.bits[bit] > RTLIL::State::S1) {
|
||||
new_set.insert(pattern);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Const other_pattern = pattern;
|
||||
|
||||
if (pattern.bits[bit] == RTLIL::State::S1)
|
||||
other_pattern.bits[bit] = RTLIL::State::S0;
|
||||
else
|
||||
other_pattern.bits[bit] = RTLIL::State::S1;
|
||||
|
||||
if (set.count(other_pattern) > 0) {
|
||||
log(" Merging pattern %s and %s from group (%d %d %s).\n", log_signal(pattern), log_signal(other_pattern),
|
||||
tr.state_in, tr.state_out, log_signal(tr.ctrl_out));
|
||||
other_pattern.bits[bit] = RTLIL::State::Sa;
|
||||
new_set.insert(other_pattern);
|
||||
did_something = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
new_set.insert(pattern);
|
||||
}
|
||||
|
||||
set.swap(new_set);
|
||||
}
|
||||
|
||||
void opt_find_dont_care()
|
||||
{
|
||||
typedef std::pair<std::pair<int, int>, RTLIL::Const> group_t;
|
||||
std::map<group_t, std::set<RTLIL::Const>> transitions_by_group;
|
||||
|
||||
for (auto &tr : fsm_data.transition_table) {
|
||||
group_t group(std::pair<int, int>(tr.state_in, tr.state_out), tr.ctrl_out);
|
||||
transitions_by_group[group].insert(tr.ctrl_in);
|
||||
}
|
||||
|
||||
fsm_data.transition_table.clear();
|
||||
for (auto &it : transitions_by_group)
|
||||
{
|
||||
FsmData::transition_t tr;
|
||||
tr.state_in = it.first.first.first;
|
||||
tr.state_out = it.first.first.second;
|
||||
tr.ctrl_out = it.first.second;
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
for (int i = 0; i < fsm_data.num_inputs; i++)
|
||||
opt_find_dont_care_worker(it.second, i, tr, did_something);
|
||||
}
|
||||
|
||||
for (auto &ci : it.second) {
|
||||
tr.ctrl_in = ci;
|
||||
fsm_data.transition_table.push_back(tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module)
|
||||
{
|
||||
log("Optimizing FSM `%s' from module `%s'.\n", cell->name.c_str(), module->name.c_str());
|
||||
|
||||
fsm_data.copy_from_cell(cell);
|
||||
this->cell = cell;
|
||||
this->module = module;
|
||||
|
||||
opt_unused_outputs();
|
||||
|
||||
opt_alias_inputs();
|
||||
opt_feedback_inputs();
|
||||
opt_find_dont_care();
|
||||
|
||||
opt_const_and_unused_inputs();
|
||||
|
||||
fsm_data.copy_to_cell(cell);
|
||||
}
|
||||
};
|
||||
|
||||
void FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module)
|
||||
{
|
||||
FsmOpt fsmopt(cell, module);
|
||||
}
|
||||
|
||||
struct FsmOptPass : public Pass {
|
||||
FsmOptPass() : Pass("fsm_opt") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &cell_it : mod_it.second->cells) {
|
||||
if (cell_it.second->type == "$fsm")
|
||||
FsmData::optimize_fsm(cell_it.second, mod_it.second);
|
||||
}
|
||||
}
|
||||
} FsmOptPass;
|
||||
|
114
passes/fsm/fsm_recode.cc
Normal file
114
passes/fsm/fsm_recode.cc
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "fsmdata.h"
|
||||
#include <string.h>
|
||||
|
||||
static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f)
|
||||
{
|
||||
fprintf(f, "set_fsm_state_vector {");
|
||||
for (int i = fsm_data.state_bits-1; i >= 0; i--)
|
||||
fprintf(f, " %s_reg[%d]", cell->parameters["\\NAME"].str[0] == '\\' ?
|
||||
cell->parameters["\\NAME"].str.substr(1).c_str() : cell->parameters["\\NAME"].str.c_str(), i);
|
||||
fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
|
||||
prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(),
|
||||
prefix, RTLIL::unescape_id(module->name).c_str());
|
||||
|
||||
fprintf(f, "set_fsm_encoding {");
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
|
||||
fprintf(f, " s%zd=2#", i);
|
||||
for (int j = int(fsm_data.state_table[i].bits.size())-1; j >= 0; j--)
|
||||
fprintf(f, "%c", fsm_data.state_table[i].bits[j] == RTLIL::State::S1 ? '1' : '0');
|
||||
}
|
||||
fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
|
||||
prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(),
|
||||
prefix, RTLIL::unescape_id(module->name).c_str());
|
||||
}
|
||||
|
||||
static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file)
|
||||
{
|
||||
FsmData fsm_data;
|
||||
fsm_data.copy_from_cell(cell);
|
||||
|
||||
log("Recoding FSM `%s' from module `%s':\n", cell->name.c_str(), module->name.c_str());
|
||||
|
||||
if (fm_set_fsm_file != NULL)
|
||||
fm_set_fsm_print(cell, module, fsm_data, "r", fm_set_fsm_file);
|
||||
|
||||
fsm_data.state_bits = fsm_data.state_table.size();
|
||||
if (fsm_data.reset_state >= 0)
|
||||
fsm_data.state_bits--;
|
||||
|
||||
int bit_pos = 0;
|
||||
for (size_t i = 0; i < fsm_data.state_table.size(); i++)
|
||||
{
|
||||
RTLIL::Const new_code;
|
||||
if (int(i) == fsm_data.reset_state)
|
||||
new_code = RTLIL::Const(RTLIL::State::S0, fsm_data.state_bits);
|
||||
else {
|
||||
RTLIL::Const state_code(RTLIL::State::Sa, fsm_data.state_bits);
|
||||
state_code.bits[bit_pos++] = RTLIL::State::S1;
|
||||
new_code = state_code;
|
||||
}
|
||||
|
||||
log(" %s -> %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str());
|
||||
fsm_data.state_table[i] = new_code;
|
||||
}
|
||||
|
||||
if (fm_set_fsm_file != NULL)
|
||||
fm_set_fsm_print(cell, module, fsm_data, "i", fm_set_fsm_file);
|
||||
|
||||
fsm_data.copy_to_cell(cell);
|
||||
}
|
||||
|
||||
struct FsmRecodePass : public Pass {
|
||||
FsmRecodePass() : Pass("fsm_recode") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
FILE *fm_set_fsm_file = NULL;
|
||||
|
||||
log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n");
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file == NULL) {
|
||||
fm_set_fsm_file = fopen(args[++argidx].c_str(), "w");
|
||||
if (fm_set_fsm_file == NULL)
|
||||
log_error("Can't open fm_set_fsm_file `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &cell_it : mod_it.second->cells)
|
||||
if (cell_it.second->type == "$fsm")
|
||||
fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file);
|
||||
|
||||
if (fm_set_fsm_file != NULL)
|
||||
fclose(fm_set_fsm_file);
|
||||
}
|
||||
} FsmRecodePass;
|
||||
|
177
passes/fsm/fsmdata.h
Normal file
177
passes/fsm/fsmdata.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FSMDATA_H
|
||||
#define FSMDATA_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
struct FsmData
|
||||
{
|
||||
int num_inputs, num_outputs, state_bits, reset_state;
|
||||
struct transition_t { int state_in, state_out; RTLIL::Const ctrl_in, ctrl_out; };
|
||||
std::vector<transition_t> transition_table;
|
||||
std::vector<RTLIL::Const> state_table;
|
||||
|
||||
void copy_to_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
cell->parameters["\\CTRL_IN_WIDTH"] = RTLIL::Const(num_inputs);
|
||||
cell->parameters["\\CTRL_OUT_WIDTH"] = RTLIL::Const(num_outputs);
|
||||
|
||||
int state_num_log2 = 0;
|
||||
for (int i = state_table.size(); i > 0; i = i >> 1)
|
||||
state_num_log2++;
|
||||
state_num_log2 = std::max(state_num_log2, 1);
|
||||
|
||||
cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits);
|
||||
cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size());
|
||||
cell->parameters["\\STATE_NUM_LOG2"] = RTLIL::Const(state_num_log2);
|
||||
cell->parameters["\\STATE_RST"] = RTLIL::Const(reset_state);
|
||||
cell->parameters["\\STATE_TABLE"] = RTLIL::Const();
|
||||
|
||||
for (int i = 0; i < int(state_table.size()); i++) {
|
||||
std::vector<RTLIL::State> &bits_table = cell->parameters["\\STATE_TABLE"].bits;
|
||||
std::vector<RTLIL::State> &bits_state = state_table[i].bits;
|
||||
bits_table.insert(bits_table.end(), bits_state.begin(), bits_state.end());
|
||||
}
|
||||
|
||||
cell->parameters["\\TRANS_NUM"] = RTLIL::Const(transition_table.size());
|
||||
cell->parameters["\\TRANS_TABLE"] = RTLIL::Const();
|
||||
for (int i = 0; i < int(transition_table.size()); i++)
|
||||
{
|
||||
std::vector<RTLIL::State> &bits_table = cell->parameters["\\TRANS_TABLE"].bits;
|
||||
transition_t &tr = transition_table[i];
|
||||
|
||||
RTLIL::Const const_state_in = RTLIL::Const(tr.state_in, state_num_log2);
|
||||
RTLIL::Const const_state_out = RTLIL::Const(tr.state_out, state_num_log2);
|
||||
std::vector<RTLIL::State> &bits_state_in = const_state_in.bits;
|
||||
std::vector<RTLIL::State> &bits_state_out = const_state_out.bits;
|
||||
|
||||
std::vector<RTLIL::State> &bits_ctrl_in = tr.ctrl_in.bits;
|
||||
std::vector<RTLIL::State> &bits_ctrl_out = tr.ctrl_out.bits;
|
||||
|
||||
// append lsb first
|
||||
bits_table.insert(bits_table.end(), bits_ctrl_out.begin(), bits_ctrl_out.end());
|
||||
bits_table.insert(bits_table.end(), bits_state_out.begin(), bits_state_out.end());
|
||||
bits_table.insert(bits_table.end(), bits_ctrl_in.begin(), bits_ctrl_in.end());
|
||||
bits_table.insert(bits_table.end(), bits_state_in.begin(), bits_state_in.end());
|
||||
}
|
||||
}
|
||||
|
||||
void copy_from_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
num_inputs = cell->parameters["\\CTRL_IN_WIDTH"].as_int();
|
||||
num_outputs = cell->parameters["\\CTRL_OUT_WIDTH"].as_int();
|
||||
|
||||
state_bits = cell->parameters["\\STATE_BITS"].as_int();
|
||||
reset_state = cell->parameters["\\STATE_RST"].as_int();
|
||||
|
||||
int state_num = cell->parameters["\\STATE_NUM"].as_int();
|
||||
int state_num_log2 = cell->parameters["\\STATE_NUM_LOG2"].as_int();
|
||||
int trans_num = cell->parameters["\\TRANS_NUM"].as_int();
|
||||
|
||||
if (reset_state < 0 || reset_state >= state_num)
|
||||
reset_state = -1;
|
||||
|
||||
RTLIL::Const state_table = cell->parameters["\\STATE_TABLE"];
|
||||
RTLIL::Const trans_table = cell->parameters["\\TRANS_TABLE"];
|
||||
|
||||
for (int i = 0; i < state_num; i++) {
|
||||
RTLIL::Const state_code;
|
||||
int off_begin = i*state_bits, off_end = off_begin + state_bits;
|
||||
state_code.bits.insert(state_code.bits.begin(), state_table.bits.begin()+off_begin, state_table.bits.begin()+off_end);
|
||||
this->state_table.push_back(state_code);
|
||||
}
|
||||
|
||||
for (int i = 0; i < trans_num; i++)
|
||||
{
|
||||
auto off_ctrl_out = trans_table.bits.begin() + i*(num_inputs+num_outputs+2*state_num_log2);
|
||||
auto off_state_out = off_ctrl_out + num_outputs;
|
||||
auto off_ctrl_in = off_state_out + state_num_log2;
|
||||
auto off_state_in = off_ctrl_in + num_inputs;
|
||||
auto off_end = off_state_in + state_num_log2;
|
||||
|
||||
RTLIL::Const state_in, state_out, ctrl_in, ctrl_out;
|
||||
ctrl_out.bits.insert(state_in.bits.begin(), off_ctrl_out, off_state_out);
|
||||
state_out.bits.insert(state_out.bits.begin(), off_state_out, off_ctrl_in);
|
||||
ctrl_in.bits.insert(ctrl_in.bits.begin(), off_ctrl_in, off_state_in);
|
||||
state_in.bits.insert(state_in.bits.begin(), off_state_in, off_end);
|
||||
|
||||
transition_t tr;
|
||||
tr.state_in = state_in.as_int();
|
||||
tr.state_out = state_out.as_int();
|
||||
tr.ctrl_in = ctrl_in;
|
||||
tr.ctrl_out = ctrl_out;
|
||||
|
||||
if (tr.state_in < 0 || tr.state_in >= state_num)
|
||||
tr.state_in = -1;
|
||||
if (tr.state_out < 0 || tr.state_out >= state_num)
|
||||
tr.state_out = -1;
|
||||
|
||||
transition_table.push_back(tr);
|
||||
}
|
||||
}
|
||||
|
||||
void log_info(RTLIL::Cell *cell)
|
||||
{
|
||||
log("-------------------------------------\n");
|
||||
log("\n");
|
||||
log(" Information on FSM %s (%s):\n", cell->name.c_str(), cell->parameters["\\NAME"].str.c_str());
|
||||
log("\n");
|
||||
log(" Number of input signals: %3d\n", num_inputs);
|
||||
log(" Number of output signals: %3d\n", num_outputs);
|
||||
log(" Number of state bits: %3d\n", state_bits);
|
||||
|
||||
log("\n");
|
||||
log(" Input signals:\n");
|
||||
RTLIL::SigSpec sig_in = cell->connections["\\CTRL_IN"];
|
||||
sig_in.expand();
|
||||
for (size_t i = 0; i < sig_in.chunks.size(); i++)
|
||||
log(" %3zd: %s\n", i, log_signal(sig_in.chunks[i]));
|
||||
|
||||
log("\n");
|
||||
log(" Output signals:\n");
|
||||
RTLIL::SigSpec sig_out = cell->connections["\\CTRL_OUT"];
|
||||
sig_out.expand();
|
||||
for (size_t i = 0; i < sig_out.chunks.size(); i++)
|
||||
log(" %3zd: %s\n", i, log_signal(sig_out.chunks[i]));
|
||||
|
||||
log("\n");
|
||||
log(" State encoding:\n");
|
||||
for (size_t i = 0; i < state_table.size(); i++)
|
||||
log(" %3zd: %10s%s\n", i, log_signal(state_table[i], false),
|
||||
int(i) == reset_state ? " <RESET STATE>" : "");
|
||||
|
||||
log("\n");
|
||||
log(" Transition Table (state_in, ctrl_in, state_out, ctrl_out):\n");
|
||||
for (size_t i = 0; i < transition_table.size(); i++) {
|
||||
transition_t &tr = transition_table[i];
|
||||
log(" %5zd: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out));
|
||||
}
|
||||
|
||||
log("\n");
|
||||
log("-------------------------------------\n");
|
||||
}
|
||||
|
||||
// implemented in fsm_opt.cc
|
||||
static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module);
|
||||
};
|
||||
|
||||
#endif
|
3
passes/hierarchy/Makefile.inc
Normal file
3
passes/hierarchy/Makefile.inc
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += passes/hierarchy/hierarchy.o
|
||||
|
194
passes/hierarchy/hierarchy.cc
Normal file
194
passes/hierarchy/hierarchy.cc
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check)
|
||||
{
|
||||
bool did_something = false;
|
||||
|
||||
for (auto &cell_it : module->cells) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (design->modules.count(cell->type) == 0) {
|
||||
if (flag_check && cell->type[0] != '$')
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
|
||||
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
|
||||
continue;
|
||||
}
|
||||
if (cell->parameters.size() == 0)
|
||||
continue;
|
||||
RTLIL::Module *mod = design->modules[cell->type];
|
||||
cell->type = mod->derive(design, cell->parameters);
|
||||
cell->parameters.clear();
|
||||
}
|
||||
|
||||
if (did_something)
|
||||
return did_something;
|
||||
|
||||
std::map<RTLIL::SigSpec, int> auto_wires;
|
||||
|
||||
for (auto &wire_it : module->wires) {
|
||||
if (wire_it.second->auto_width)
|
||||
auto_wires[RTLIL::SigSpec(wire_it.second)] = -1;
|
||||
}
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
for (auto &conn : cell_it.second->connections)
|
||||
for (auto &awit : auto_wires) {
|
||||
if (awit.second >= 0 || conn.second != awit.first)
|
||||
continue;
|
||||
if (design->modules.count(cell_it.second->type) == 0) {
|
||||
log("WARNING: Module `%s' used in auto-delaration of the wire `%s.%s' cannot be found.\n",
|
||||
cell_it.second->type.c_str(), module->name.c_str(), log_signal(awit.first));
|
||||
continue;
|
||||
}
|
||||
RTLIL::Module *mod = design->modules[cell_it.second->type];
|
||||
RTLIL::Wire *wire = NULL;
|
||||
if (mod->wires.count(conn.first) == 0) {
|
||||
for (auto &wire_it : mod->wires) {
|
||||
if (wire_it.second->port_id == 0)
|
||||
continue;
|
||||
char buffer[100];
|
||||
snprintf(buffer, 100, "$%d", wire_it.second->port_id);
|
||||
if (buffer == conn.first) {
|
||||
wire = wire_it.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
wire = mod->wires[conn.first];
|
||||
if (!wire || wire->port_id == 0)
|
||||
log_error("No port `%s' found in `%s' but used by instanciation in `%s'!\n",
|
||||
conn.first.c_str(), mod->name.c_str(), module->name.c_str());
|
||||
if (wire->auto_width)
|
||||
log_error("Signal `%s' found in `%s' and used by instanciation in `%s' for an auto wire is an auto-wire itself!\n",
|
||||
log_signal(awit.first), mod->name.c_str(), module->name.c_str());
|
||||
awit.second = wire->width;
|
||||
}
|
||||
|
||||
std::map<RTLIL::IdString, int> auto_sizes;
|
||||
for (auto &awit : auto_wires) {
|
||||
if (awit.second < 0)
|
||||
log("Can't further resolve auto-wire `%s.%s' (width %d) using cell ports.\n",
|
||||
module->name.c_str(), awit.first.chunks[0].wire->name.c_str(),
|
||||
awit.first.chunks[0].wire->width);
|
||||
else
|
||||
auto_sizes[awit.first.chunks[0].wire->name] = awit.second;
|
||||
}
|
||||
|
||||
if (auto_sizes.size() > 0) {
|
||||
module->update_auto_wires(auto_sizes);
|
||||
log_header("Continuing EXPAND pass.\n");
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
static void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, bool is_top = false)
|
||||
{
|
||||
if (used.count(mod) > 0)
|
||||
return;
|
||||
|
||||
log("%s module: %s\n", is_top ? "Top" : "Used", mod->name.c_str());
|
||||
used.insert(mod);
|
||||
|
||||
for (auto &it : mod->cells) {
|
||||
if (design->modules.count(it.second->type) > 0)
|
||||
hierarchy_worker(design, used, design->modules[it.second->type]);
|
||||
}
|
||||
}
|
||||
|
||||
static void hierarchy(RTLIL::Design *design, RTLIL::Module *top)
|
||||
{
|
||||
std::set<RTLIL::Module*> used;
|
||||
hierarchy_worker(design, used, top, true);
|
||||
|
||||
std::vector<RTLIL::Module*> del_modules;
|
||||
for (auto &it : design->modules)
|
||||
if (used.count(it.second) == 0)
|
||||
del_modules.push_back(it.second);
|
||||
|
||||
for (auto mod : del_modules) {
|
||||
log("Removing unused module `%s'.\n", mod->name.c_str());
|
||||
design->modules.erase(mod->name);
|
||||
delete mod;
|
||||
}
|
||||
|
||||
log("Removed %zd unused modules.\n", del_modules.size());
|
||||
}
|
||||
|
||||
struct HierarchyPass : public Pass {
|
||||
HierarchyPass() : Pass("hierarchy") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing HIERARCHY pass (removing modules outside design hierarchy).\n");
|
||||
|
||||
bool flag_check = false;
|
||||
RTLIL::Module *top_mod = NULL;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-check") {
|
||||
flag_check = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top") {
|
||||
if (++argidx >= args.size())
|
||||
log_cmd_error("Option -top requires an additional argument!\n");
|
||||
if (args[argidx][0] != '$' && args[argidx][0] != '\\')
|
||||
top_mod = design->modules.count("\\" + args[argidx]) > 0 ? design->modules["\\" + args[argidx]] : NULL;
|
||||
else
|
||||
top_mod = design->modules.count(args[argidx]) > 0 ? design->modules[args[argidx]] : NULL;
|
||||
if (top_mod == NULL)
|
||||
log_cmd_error("Module `%s' not found!\n", args[argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (top_mod != NULL)
|
||||
hierarchy(design, top_mod);
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
std::vector<std::string> modnames;
|
||||
modnames.reserve(design->modules.size());
|
||||
for (auto &mod_it : design->modules)
|
||||
modnames.push_back(mod_it.first);
|
||||
for (auto &modname : modnames) {
|
||||
if (design->modules.count(modname) == 0)
|
||||
continue;
|
||||
if (expand_module(design, design->modules[modname], flag_check))
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (top_mod != NULL)
|
||||
hierarchy(design, top_mod);
|
||||
}
|
||||
} HierarchyPass;
|
||||
|
6
passes/memory/Makefile.inc
Normal file
6
passes/memory/Makefile.inc
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
OBJS += passes/memory/memory.o
|
||||
OBJS += passes/memory/memory_dff.o
|
||||
OBJS += passes/memory/memory_collect.o
|
||||
OBJS += passes/memory/memory_map.o
|
||||
|
40
passes/memory/memory.cc
Normal file
40
passes/memory/memory.cc
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct MemoryPass : public Pass {
|
||||
MemoryPass() : Pass("memory") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing MEMORY pass.\n");
|
||||
log_push();
|
||||
|
||||
extra_args(args, 1, design);
|
||||
Pass::call(design, "memory_dff");
|
||||
Pass::call(design, "memory_collect");
|
||||
Pass::call(design, "memory_map");
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} MemoryPass;
|
||||
|
182
passes/memory/memory_collect.cc
Normal file
182
passes/memory/memory_collect.cc
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
|
||||
{
|
||||
log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n",
|
||||
memory->name.c_str(), module->name.c_str());
|
||||
|
||||
int addr_bits = 0;
|
||||
while ((1 << addr_bits) < memory->size)
|
||||
addr_bits++;
|
||||
|
||||
int wr_ports = 0;
|
||||
RTLIL::SigSpec sig_wr_clk;
|
||||
RTLIL::SigSpec sig_wr_clk_enable;
|
||||
RTLIL::SigSpec sig_wr_clk_polarity;
|
||||
RTLIL::SigSpec sig_wr_addr;
|
||||
RTLIL::SigSpec sig_wr_data;
|
||||
RTLIL::SigSpec sig_wr_en;
|
||||
|
||||
int rd_ports = 0;
|
||||
RTLIL::SigSpec sig_rd_clk;
|
||||
RTLIL::SigSpec sig_rd_clk_enable;
|
||||
RTLIL::SigSpec sig_rd_clk_polarity;
|
||||
RTLIL::SigSpec sig_rd_addr;
|
||||
RTLIL::SigSpec sig_rd_data;
|
||||
|
||||
std::vector<std::string> del_cell_ids;
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
|
||||
if (cell->type == "$memwr" && cell->parameters["\\MEMID"].str == memory->name)
|
||||
{
|
||||
wr_ports++;
|
||||
del_cell_ids.push_back(cell->name);
|
||||
|
||||
RTLIL::SigSpec clk = cell->connections["\\CLK"];
|
||||
RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
||||
RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
||||
RTLIL::SigSpec addr = cell->connections["\\ADDR"];
|
||||
RTLIL::SigSpec data = cell->connections["\\DATA"];
|
||||
RTLIL::SigSpec en = cell->connections["\\EN"];
|
||||
|
||||
clk.extend(1, false);
|
||||
clk_enable.extend(1, false);
|
||||
clk_polarity.extend(1, false);
|
||||
addr.extend(addr_bits, false);
|
||||
data.extend(memory->width, false);
|
||||
en.extend(1, false);
|
||||
|
||||
sig_wr_clk.append(clk);
|
||||
sig_wr_clk_enable.append(clk_enable);
|
||||
sig_wr_clk_polarity.append(clk_polarity);
|
||||
sig_wr_addr.append(addr);
|
||||
sig_wr_data.append(data);
|
||||
sig_wr_en.append(en);
|
||||
}
|
||||
|
||||
if (cell->type == "$memrd" && cell->parameters["\\MEMID"].str == memory->name)
|
||||
{
|
||||
rd_ports++;
|
||||
del_cell_ids.push_back(cell->name);
|
||||
|
||||
RTLIL::SigSpec clk = cell->connections["\\CLK"];
|
||||
RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
|
||||
RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
|
||||
RTLIL::SigSpec addr = cell->connections["\\ADDR"];
|
||||
RTLIL::SigSpec data = cell->connections["\\DATA"];
|
||||
|
||||
clk.extend(1, false);
|
||||
clk_enable.extend(1, false);
|
||||
clk_polarity.extend(1, false);
|
||||
addr.extend(addr_bits, false);
|
||||
data.extend(memory->width, false);
|
||||
|
||||
sig_rd_clk.append(clk);
|
||||
sig_rd_clk_enable.append(clk_enable);
|
||||
sig_rd_clk_polarity.append(clk_polarity);
|
||||
sig_rd_addr.append(addr);
|
||||
sig_rd_data.append(data);
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$mem$" << memory->name << "$" << (RTLIL::autoidx++);
|
||||
|
||||
RTLIL::Cell *mem = new RTLIL::Cell;
|
||||
mem->name = sstr.str();
|
||||
mem->type = "$mem";
|
||||
|
||||
mem->parameters["\\MEMID"] = RTLIL::Const(memory->name);
|
||||
mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width);
|
||||
mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset);
|
||||
mem->parameters["\\SIZE"] = RTLIL::Const(memory->size);
|
||||
mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
|
||||
|
||||
sig_wr_clk_enable.optimize();
|
||||
sig_wr_clk_polarity.optimize();
|
||||
|
||||
assert(sig_wr_clk.width == wr_ports);
|
||||
assert(sig_wr_clk_enable.width == wr_ports && sig_wr_clk_enable.is_fully_const());
|
||||
assert(sig_wr_clk_polarity.width == wr_ports && sig_wr_clk_polarity.is_fully_const());
|
||||
assert(sig_wr_addr.width == wr_ports * addr_bits);
|
||||
assert(sig_wr_data.width == wr_ports * memory->width);
|
||||
assert(sig_wr_en.width == wr_ports);
|
||||
|
||||
mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports);
|
||||
mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.chunks[0].data : RTLIL::Const(0, 0);
|
||||
mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_enable.chunks[0].data : RTLIL::Const(0, 0);
|
||||
|
||||
mem->connections["\\WR_CLK"] = sig_wr_clk;
|
||||
mem->connections["\\WR_ADDR"] = sig_wr_addr;
|
||||
mem->connections["\\WR_DATA"] = sig_wr_data;
|
||||
mem->connections["\\WR_EN"] = sig_wr_en;
|
||||
|
||||
sig_rd_clk_enable.optimize();
|
||||
sig_rd_clk_polarity.optimize();
|
||||
|
||||
assert(sig_rd_clk.width == rd_ports);
|
||||
assert(sig_rd_clk_enable.width == rd_ports && sig_rd_clk_enable.is_fully_const());
|
||||
assert(sig_rd_clk_polarity.width == rd_ports && sig_rd_clk_polarity.is_fully_const());
|
||||
assert(sig_rd_addr.width == rd_ports * addr_bits);
|
||||
assert(sig_rd_data.width == rd_ports * memory->width);
|
||||
|
||||
mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports);
|
||||
mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.chunks[0].data : RTLIL::Const(0, 0);
|
||||
mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_enable.chunks[0].data : RTLIL::Const(0, 0);
|
||||
|
||||
mem->connections["\\RD_CLK"] = sig_rd_clk;
|
||||
mem->connections["\\RD_ADDR"] = sig_rd_addr;
|
||||
mem->connections["\\RD_DATA"] = sig_rd_data;
|
||||
|
||||
for (auto &id : del_cell_ids) {
|
||||
delete module->cells[id];
|
||||
module->cells.erase(id);
|
||||
}
|
||||
module->cells[mem->name] = mem;
|
||||
}
|
||||
|
||||
static void handle_module(RTLIL::Module *module)
|
||||
{
|
||||
for (auto &mem_it : module->memories) {
|
||||
handle_memory(module, mem_it.second);
|
||||
delete mem_it.second;
|
||||
}
|
||||
module->memories.clear();
|
||||
}
|
||||
|
||||
struct MemoryCollectPass : public Pass {
|
||||
MemoryCollectPass() : Pass("memory_collect") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto &mod_it : design->modules)
|
||||
handle_module(mod_it.second);
|
||||
}
|
||||
} MemoryCollectPass;
|
||||
|
200
passes/memory/memory_dff.cc
Normal file
200
passes/memory/memory_dff.cc
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sstream>
|
||||
|
||||
static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig)
|
||||
{
|
||||
for (auto &conn : module->connections)
|
||||
sig.replace(conn.first, conn.second);
|
||||
}
|
||||
|
||||
static bool find_sig_before_dff(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
|
||||
{
|
||||
bool replaced_bits = false;
|
||||
normalize_sig(module, sig);
|
||||
sig.expand();
|
||||
|
||||
for (size_t i = 0; i < sig.chunks.size(); i++)
|
||||
{
|
||||
RTLIL::SigChunk &chunk = sig.chunks[i];
|
||||
|
||||
if (chunk.wire == NULL)
|
||||
continue;
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
|
||||
if (cell->type != "$dff")
|
||||
continue;
|
||||
|
||||
if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
|
||||
if (cell->connections["\\CLK"] != clk)
|
||||
continue;
|
||||
if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity)
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec q_norm = cell->connections[after ? "\\D" : "\\Q"];
|
||||
normalize_sig(module, q_norm);
|
||||
|
||||
RTLIL::SigSpec d = q_norm.extract(chunk, &cell->connections[after ? "\\Q" : "\\D"]);
|
||||
if (d.width != 1)
|
||||
continue;
|
||||
|
||||
assert(d.chunks.size() == 1);
|
||||
chunk = d.chunks[0];
|
||||
clk = cell->connections["\\CLK"];
|
||||
clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
|
||||
replaced_bits = true;
|
||||
goto replaced_this_bit;
|
||||
}
|
||||
|
||||
return false;
|
||||
replaced_this_bit:;
|
||||
}
|
||||
|
||||
sig.optimize();
|
||||
return replaced_bits;
|
||||
}
|
||||
|
||||
static void handle_wr_cell(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||
{
|
||||
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
|
||||
|
||||
RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
|
||||
bool clk_polarity = 0;
|
||||
|
||||
RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"];
|
||||
if (!find_sig_before_dff(module, sig_addr, clk, clk_polarity)) {
|
||||
log("no (compatible) $dff for address input found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec sig_data = cell->connections["\\DATA"];
|
||||
if (!find_sig_before_dff(module, sig_data, clk, clk_polarity)) {
|
||||
log("no (compatible) $dff for data input found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec sig_en = cell->connections["\\EN"];
|
||||
if (!find_sig_before_dff(module, sig_en, clk, clk_polarity)) {
|
||||
log("no (compatible) $dff for enable input found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cell->connections["\\CLK"] = clk;
|
||||
cell->connections["\\ADDR"] = sig_addr;
|
||||
cell->connections["\\DATA"] = sig_data;
|
||||
cell->connections["\\EN"] = sig_en;
|
||||
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
|
||||
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
|
||||
log("merged $dff to cell.\n");
|
||||
}
|
||||
|
||||
#if 1
|
||||
static void handle_rd_cell(RTLIL::Module*, RTLIL::Cell*)
|
||||
{
|
||||
// merging dffs into read ports isn't neccessary for memory_map.
|
||||
// we'd loose the information if the register is on the address or
|
||||
// data port and wouldn't get any benefits.
|
||||
}
|
||||
#else
|
||||
static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
|
||||
{
|
||||
normalize_sig(module, sig);
|
||||
sig.sort_and_unify();
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$memory_dff_disconnected$" << (RTLIL::autoidx++);
|
||||
|
||||
RTLIL::Wire *wire = new RTLIL::Wire;
|
||||
wire->name = sstr.str();
|
||||
wire->width = sig.width;
|
||||
module->wires[wire->name] = wire;
|
||||
|
||||
RTLIL::SigSpec newsig(wire);
|
||||
|
||||
for (auto &cell_it : module->cells) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (cell->type == "$dff")
|
||||
cell->connections["\\Q"].replace(sig, newsig);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||
{
|
||||
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
|
||||
|
||||
bool clk_polarity = 0;
|
||||
|
||||
RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
|
||||
RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"];
|
||||
if (find_sig_before_dff(module, sig_addr, clk_addr, clk_polarity))
|
||||
{
|
||||
cell->connections["\\CLK"] = clk_addr;
|
||||
cell->connections["\\ADDR"] = sig_addr;
|
||||
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
|
||||
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
|
||||
log("merged address $dff to cell.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
|
||||
RTLIL::SigSpec sig_data = cell->connections["\\DATA"];
|
||||
if (find_sig_before_dff(module, sig_data, clk_data, clk_polarity, true))
|
||||
{
|
||||
disconnect_dff(module, sig_data);
|
||||
cell->connections["\\CLK"] = clk_data;
|
||||
cell->connections["\\DATA"] = sig_data;
|
||||
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
|
||||
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
|
||||
log("merged data $dff to cell.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log("no (compatible) $dff found.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_module(RTLIL::Module *module)
|
||||
{
|
||||
for (auto &cell_it : module->cells) {
|
||||
if (cell_it.second->type == "$memwr" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool())
|
||||
handle_wr_cell(module, cell_it.second);
|
||||
if (cell_it.second->type == "$memrd" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool())
|
||||
handle_rd_cell(module, cell_it.second);
|
||||
}
|
||||
}
|
||||
|
||||
struct MemoryDffPass : public Pass {
|
||||
MemoryDffPass() : Pass("memory_dff") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto &mod_it : design->modules)
|
||||
handle_module(mod_it.second);
|
||||
}
|
||||
} MemoryDffPass;
|
||||
|
334
passes/memory/memory_map.cc
Normal file
334
passes/memory/memory_map.cc
Normal file
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static std::string genid(std::string name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "")
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$memory" << name << token1;
|
||||
|
||||
if (i >= 0)
|
||||
sstr << "[" << i << "]";
|
||||
|
||||
sstr << token2;
|
||||
|
||||
if (j >= 0)
|
||||
sstr << "[" << j << "]";
|
||||
|
||||
sstr << token3;
|
||||
|
||||
if (k >= 0)
|
||||
sstr << "[" << k << "]";
|
||||
|
||||
sstr << token4 << "$" << (RTLIL::autoidx++);
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
static void handle_cell(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||
{
|
||||
std::set<int> static_ports;
|
||||
std::map<int, RTLIL::SigSpec> static_cells_map;
|
||||
int mem_size = cell->parameters["\\SIZE"].as_int();
|
||||
int mem_width = cell->parameters["\\WIDTH"].as_int();
|
||||
int mem_offset = cell->parameters["\\OFFSET"].as_int();
|
||||
int mem_abits = cell->parameters["\\ABITS"].as_int();
|
||||
|
||||
// delete unused memory cell
|
||||
if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) {
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
return;
|
||||
}
|
||||
|
||||
// all write ports must share the same clock
|
||||
RTLIL::SigSpec clocks = cell->connections["\\WR_CLK"];
|
||||
RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"];
|
||||
RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"];
|
||||
RTLIL::SigSpec refclock;
|
||||
RTLIL::State refclock_pol = RTLIL::State::Sx;
|
||||
for (int i = 0; i < clocks.width; i++) {
|
||||
RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(i, 1);
|
||||
if (wr_en.is_fully_const() && wr_en.as_int() == 0) {
|
||||
static_ports.insert(i);
|
||||
continue;
|
||||
}
|
||||
if (clocks_en.bits[i] != RTLIL::State::S1) {
|
||||
RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(i*mem_abits, mem_abits);
|
||||
RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(i*mem_width, mem_width);
|
||||
if (wr_addr.is_fully_const()) {
|
||||
// FIXME: Actually we should check for wr_en.is_fully_const() also and
|
||||
// create a $adff cell with this ports wr_en input as reset pin when wr_en
|
||||
// is not a simple static 1.
|
||||
static_cells_map[wr_addr.as_int()] = wr_data;
|
||||
static_ports.insert(i);
|
||||
continue;
|
||||
}
|
||||
log("Not mapping memory cell %s in module %s (write port %d has no clock).\n",
|
||||
cell->name.c_str(), module->name.c_str(), i);
|
||||
return;
|
||||
}
|
||||
if (refclock.width == 0) {
|
||||
refclock = clocks.extract(i, 1);
|
||||
refclock_pol = clocks_pol.bits[i];
|
||||
}
|
||||
if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) {
|
||||
log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n",
|
||||
cell->name.c_str(), module->name.c_str(), i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str());
|
||||
|
||||
std::vector<RTLIL::SigSpec> data_reg_in;
|
||||
std::vector<RTLIL::SigSpec> data_reg_out;
|
||||
|
||||
int count_static = 0;
|
||||
|
||||
for (int i = 0; i < mem_size; i++)
|
||||
{
|
||||
if (static_cells_map.count(i) > 0)
|
||||
{
|
||||
data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width));
|
||||
data_reg_out.push_back(static_cells_map[i]);
|
||||
count_static++;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::Cell *c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "", i);
|
||||
c->type = "$dff";
|
||||
c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
|
||||
c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]);
|
||||
c->connections["\\CLK"] = clocks.extract(0, 1);
|
||||
module->cells[c->name] = c;
|
||||
|
||||
RTLIL::Wire *w_in = new RTLIL::Wire;
|
||||
w_in->name = genid(cell->name, "", i, "$d");
|
||||
w_in->width = mem_width;
|
||||
module->wires[w_in->name] = w_in;
|
||||
data_reg_in.push_back(RTLIL::SigSpec(w_in));
|
||||
c->connections["\\D"] = data_reg_in.back();
|
||||
|
||||
RTLIL::Wire *w_out = new RTLIL::Wire;
|
||||
w_out->name = stringf("%s[%d]", cell->parameters["\\MEMID"].str.c_str(), i);
|
||||
if (module->wires.count(w_out->name) > 0)
|
||||
w_out->name = genid(cell->name, "", i, "$q");
|
||||
w_out->width = mem_width;
|
||||
w_out->start_offset = mem_offset;
|
||||
module->wires[w_out->name] = w_out;
|
||||
data_reg_out.push_back(RTLIL::SigSpec(w_out));
|
||||
c->connections["\\Q"] = data_reg_out.back();
|
||||
}
|
||||
}
|
||||
|
||||
log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width);
|
||||
|
||||
int count_dff = 0, count_mux = 0, count_wrmux = 0;
|
||||
|
||||
for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++)
|
||||
{
|
||||
RTLIL::SigSpec rd_addr = cell->connections["\\RD_ADDR"].extract(i*mem_abits, mem_abits);
|
||||
|
||||
std::vector<RTLIL::SigSpec> rd_signals;
|
||||
rd_signals.push_back(cell->connections["\\RD_DATA"].extract(i*mem_width, mem_width));
|
||||
|
||||
if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1)
|
||||
{
|
||||
#if 1
|
||||
RTLIL::Cell *c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$rdreg", i);
|
||||
c->type = "$dff";
|
||||
c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
|
||||
c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
|
||||
c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1);
|
||||
c->connections["\\D"] = rd_addr;
|
||||
module->cells[c->name] = c;
|
||||
count_dff++;
|
||||
|
||||
RTLIL::Wire *w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$rdreg", i, "$q");
|
||||
w->width = mem_abits;
|
||||
module->wires[w->name] = w;
|
||||
|
||||
c->connections["\\Q"] = RTLIL::SigSpec(w);
|
||||
rd_addr = RTLIL::SigSpec(w);
|
||||
#else
|
||||
RTLIL::Cell *c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$rdreg", i);
|
||||
c->type = "$dff";
|
||||
c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
|
||||
c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
|
||||
c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1);
|
||||
c->connections["\\Q"] = rd_signals.back();
|
||||
module->cells[c->name] = c;
|
||||
count_dff++;
|
||||
|
||||
RTLIL::Wire *w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$rdreg", i, "$d");
|
||||
w->width = mem_width;
|
||||
module->wires[w->name] = w;
|
||||
|
||||
rd_signals.clear();
|
||||
rd_signals.push_back(RTLIL::SigSpec(w));
|
||||
c->connections["\\D"] = rd_signals.back();
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int j = 0; j < mem_abits; j++)
|
||||
{
|
||||
std::vector<RTLIL::SigSpec> next_rd_signals;
|
||||
|
||||
for (size_t k = 0; k < rd_signals.size(); k++)
|
||||
{
|
||||
RTLIL::Cell *c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$rdmux", i, "", j, "", k);
|
||||
c->type = "$mux";
|
||||
c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
|
||||
c->connections["\\Y"] = rd_signals[k];
|
||||
c->connections["\\S"] = rd_addr.extract(mem_abits-j-1, 1);
|
||||
module->cells[c->name] = c;
|
||||
count_mux++;
|
||||
|
||||
RTLIL::Wire *w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$a");
|
||||
w->width = mem_width;
|
||||
module->wires[w->name] = w;
|
||||
c->connections["\\A"] = RTLIL::SigSpec(w);
|
||||
|
||||
w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$b");
|
||||
w->width = mem_width;
|
||||
module->wires[w->name] = w;
|
||||
c->connections["\\B"] = RTLIL::SigSpec(w);
|
||||
|
||||
next_rd_signals.push_back(c->connections["\\A"]);
|
||||
next_rd_signals.push_back(c->connections["\\B"]);
|
||||
}
|
||||
|
||||
next_rd_signals.swap(rd_signals);
|
||||
}
|
||||
|
||||
for (int j = 0; j < mem_size; j++)
|
||||
module->connections.push_back(RTLIL::SigSig(rd_signals[j], data_reg_out[j]));
|
||||
}
|
||||
|
||||
log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux);
|
||||
|
||||
for (int i = 0; i < mem_size; i++)
|
||||
{
|
||||
if (static_cells_map.count(i) > 0)
|
||||
continue;
|
||||
|
||||
RTLIL::SigSpec sig = data_reg_out[i];
|
||||
|
||||
for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++)
|
||||
{
|
||||
RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(j*mem_abits, mem_abits);
|
||||
RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(j*mem_width, mem_width);
|
||||
RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(j, 1);
|
||||
|
||||
RTLIL::Cell *c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$wreq", i, "", j);
|
||||
c->type = "$eq";
|
||||
c->parameters["\\A_SIGNED"] = RTLIL::Const(0);
|
||||
c->parameters["\\B_SIGNED"] = RTLIL::Const(0);
|
||||
c->parameters["\\A_WIDTH"] = cell->parameters["\\ABITS"];
|
||||
c->parameters["\\B_WIDTH"] = cell->parameters["\\ABITS"];
|
||||
c->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
c->connections["\\A"] = RTLIL::SigSpec(i, mem_abits);
|
||||
c->connections["\\B"] = wr_addr;
|
||||
module->cells[c->name] = c;
|
||||
count_wrmux++;
|
||||
|
||||
RTLIL::Wire *w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$wreq", i, "", j, "$y");
|
||||
module->wires[w->name] = w;
|
||||
c->connections["\\Y"] = RTLIL::SigSpec(w);
|
||||
|
||||
c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$wren", i, "", j);
|
||||
c->type = "$and";
|
||||
c->parameters["\\A_SIGNED"] = RTLIL::Const(0);
|
||||
c->parameters["\\B_SIGNED"] = RTLIL::Const(0);
|
||||
c->parameters["\\A_WIDTH"] = RTLIL::Const(1);
|
||||
c->parameters["\\B_WIDTH"] = RTLIL::Const(1);
|
||||
c->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
c->connections["\\A"] = RTLIL::SigSpec(w);
|
||||
c->connections["\\B"] = wr_en;
|
||||
module->cells[c->name] = c;
|
||||
|
||||
w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$wren", i, "", j, "$y");
|
||||
module->wires[w->name] = w;
|
||||
c->connections["\\Y"] = RTLIL::SigSpec(w);
|
||||
|
||||
c = new RTLIL::Cell;
|
||||
c->name = genid(cell->name, "$wrmux", i, "", j);
|
||||
c->type = "$mux";
|
||||
c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
|
||||
c->connections["\\A"] = sig;
|
||||
c->connections["\\B"] = wr_data;
|
||||
c->connections["\\S"] = RTLIL::SigSpec(w);
|
||||
module->cells[c->name] = c;
|
||||
|
||||
w = new RTLIL::Wire;
|
||||
w->name = genid(cell->name, "$wrmux", i, "", j, "$y");
|
||||
w->width = mem_width;
|
||||
module->wires[w->name] = w;
|
||||
c->connections["\\Y"] = RTLIL::SigSpec(w);
|
||||
sig = RTLIL::SigSpec(w);
|
||||
}
|
||||
|
||||
module->connections.push_back(RTLIL::SigSig(data_reg_in[i], sig));
|
||||
}
|
||||
|
||||
log(" write interface: %d blocks of $eq, $and and $mux cells.\n", count_wrmux);
|
||||
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_module(RTLIL::Module *module)
|
||||
{
|
||||
std::vector<RTLIL::Cell*> cells;
|
||||
for (auto &it : module->cells)
|
||||
if (it.second->type == "$mem")
|
||||
cells.push_back(it.second);
|
||||
for (auto cell : cells)
|
||||
handle_cell(module, cell);
|
||||
}
|
||||
|
||||
struct MemoryMapPass : public Pass {
|
||||
MemoryMapPass() : Pass("memory_map") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
|
||||
log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
|
||||
extra_args(args, 1, design);
|
||||
for (auto &mod_it : design->modules)
|
||||
handle_module(mod_it.second);
|
||||
}
|
||||
} MemoryMapPass;
|
||||
|
9
passes/opt/Makefile.inc
Normal file
9
passes/opt/Makefile.inc
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
OBJS += passes/opt/opt.o
|
||||
OBJS += passes/opt/opt_share.o
|
||||
OBJS += passes/opt/opt_muxtree.o
|
||||
OBJS += passes/opt/opt_reduce.o
|
||||
OBJS += passes/opt/opt_rmdff.o
|
||||
OBJS += passes/opt/opt_rmunused.o
|
||||
OBJS += passes/opt/opt_const.o
|
||||
|
62
passes/opt/opt.cc
Normal file
62
passes/opt/opt.cc
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
bool OPT_DID_SOMETHING;
|
||||
|
||||
struct OptPass : public Pass {
|
||||
OptPass() : Pass("opt") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT pass (performing simple optimizations).\n");
|
||||
log_push();
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
log_header("Optimizing in-memory representation of design.\n");
|
||||
design->optimize();
|
||||
|
||||
Pass::call(design, "opt_const");
|
||||
Pass::call(design, "opt_share -nomux");
|
||||
while (1) {
|
||||
OPT_DID_SOMETHING = false;
|
||||
Pass::call(design, "opt_muxtree");
|
||||
Pass::call(design, "opt_reduce");
|
||||
Pass::call(design, "opt_share");
|
||||
Pass::call(design, "opt_rmdff");
|
||||
Pass::call(design, "opt_rmunused");
|
||||
Pass::call(design, "opt_const");
|
||||
if (OPT_DID_SOMETHING == false)
|
||||
break;
|
||||
log_header("Rerunning OPT passes. (Maybe there is more to do..)\n");
|
||||
}
|
||||
|
||||
log_header("Optimizing in-memory representation of design.\n");
|
||||
design->optimize();
|
||||
|
||||
log_header("Finished OPT passes. (There is nothing left to do.)\n");
|
||||
log_pop();
|
||||
}
|
||||
} OptPass;
|
||||
|
263
passes/opt/opt_const.cc
Normal file
263
passes/opt/opt_const.cc
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#undef MUX_UNDEF_SEL_TO_UNDEF_RESULTS
|
||||
|
||||
#include "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
bool did_something;
|
||||
|
||||
void replace_cell(RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
|
||||
{
|
||||
RTLIL::SigSpec Y = cell->connections[out_port];
|
||||
log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
|
||||
cell->type.c_str(), cell->name.c_str(), info.c_str(),
|
||||
module->name.c_str(), log_signal(Y), log_signal(out_val));
|
||||
OPT_DID_SOMETHING = true;
|
||||
// ILANG_BACKEND::dump_cell(stderr, "--> ", cell);
|
||||
module->connections.push_back(RTLIL::SigSig(Y, out_val));
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
void replace_const_cells(RTLIL::Module *module)
|
||||
{
|
||||
SigMap assign_map(module);
|
||||
|
||||
std::vector<RTLIL::Cell*> cells;
|
||||
cells.reserve(module->cells.size());
|
||||
for (auto &cell_it : module->cells)
|
||||
cells.push_back(cell_it.second);
|
||||
|
||||
for (auto cell : cells)
|
||||
{
|
||||
#define ACTION_DO(_p_, _s_) do { replace_cell(module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
|
||||
#define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_))
|
||||
|
||||
if (cell->type == "$_INV_") {
|
||||
RTLIL::SigSpec input = cell->connections["\\A"];
|
||||
assign_map.apply(input);
|
||||
if (input.match("1")) ACTION_DO_Y(0);
|
||||
if (input.match("0")) ACTION_DO_Y(1);
|
||||
if (input.match("*")) ACTION_DO_Y(x);
|
||||
}
|
||||
|
||||
if (cell->type == "$_AND_") {
|
||||
RTLIL::SigSpec input;
|
||||
input.append(cell->connections["\\B"]);
|
||||
input.append(cell->connections["\\A"]);
|
||||
assign_map.apply(input);
|
||||
if (input.match(" 0")) ACTION_DO_Y(0);
|
||||
if (input.match("0 ")) ACTION_DO_Y(0);
|
||||
if (input.match("11")) ACTION_DO_Y(1);
|
||||
if (input.match(" *")) ACTION_DO_Y(x);
|
||||
if (input.match("* ")) ACTION_DO_Y(x);
|
||||
}
|
||||
|
||||
if (cell->type == "$_OR_") {
|
||||
RTLIL::SigSpec input;
|
||||
input.append(cell->connections["\\B"]);
|
||||
input.append(cell->connections["\\A"]);
|
||||
assign_map.apply(input);
|
||||
if (input.match(" 1")) ACTION_DO_Y(1);
|
||||
if (input.match("1 ")) ACTION_DO_Y(1);
|
||||
if (input.match("00")) ACTION_DO_Y(0);
|
||||
if (input.match(" *")) ACTION_DO_Y(x);
|
||||
if (input.match("* ")) ACTION_DO_Y(x);
|
||||
}
|
||||
|
||||
if (cell->type == "$_XOR_") {
|
||||
RTLIL::SigSpec input;
|
||||
input.append(cell->connections["\\B"]);
|
||||
input.append(cell->connections["\\A"]);
|
||||
assign_map.apply(input);
|
||||
if (input.match("00")) ACTION_DO_Y(0);
|
||||
if (input.match("01")) ACTION_DO_Y(1);
|
||||
if (input.match("10")) ACTION_DO_Y(1);
|
||||
if (input.match("11")) ACTION_DO_Y(0);
|
||||
if (input.match(" *")) ACTION_DO_Y(x);
|
||||
if (input.match("* ")) ACTION_DO_Y(x);
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_") {
|
||||
RTLIL::SigSpec input;
|
||||
input.append(cell->connections["\\S"]);
|
||||
input.append(cell->connections["\\B"]);
|
||||
input.append(cell->connections["\\A"]);
|
||||
assign_map.apply(input);
|
||||
if (input.extract(2, 1) == input.extract(1, 1))
|
||||
ACTION_DO("\\Y", input.extract(2, 1));
|
||||
if (input.match(" 0")) ACTION_DO("\\Y", input.extract(2, 1));
|
||||
if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
|
||||
#ifdef MUX_UNDEF_SEL_TO_UNDEF_RESULTS
|
||||
if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1));
|
||||
if (input.match(" *")) ACTION_DO_Y(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cell->type == "$eq" || cell->type == "$ne")
|
||||
{
|
||||
if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) {
|
||||
int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
|
||||
cell->connections["\\A"].extend(width, cell->parameters["\\A_SIGNED"].as_bool());
|
||||
cell->connections["\\B"].extend(width, cell->parameters["\\B_SIGNED"].as_bool());
|
||||
cell->parameters["\\A_WIDTH"] = width;
|
||||
cell->parameters["\\B_WIDTH"] = width;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec a = cell->connections["\\A"];
|
||||
RTLIL::SigSpec b = cell->connections["\\B"];
|
||||
RTLIL::SigSpec new_a, new_b;
|
||||
a.expand(), b.expand();
|
||||
|
||||
assert(a.chunks.size() == b.chunks.size());
|
||||
for (size_t i = 0; i < a.chunks.size(); i++) {
|
||||
if (a.chunks[i].wire == NULL && a.chunks[i].data.bits[0] > RTLIL::State::S1)
|
||||
continue;
|
||||
if (b.chunks[i].wire == NULL && b.chunks[i].data.bits[0] > RTLIL::State::S1)
|
||||
continue;
|
||||
new_a.append(a.chunks[i]);
|
||||
new_b.append(b.chunks[i]);
|
||||
}
|
||||
|
||||
if (new_a.width != a.width) {
|
||||
new_a.optimize();
|
||||
new_b.optimize();
|
||||
cell->connections["\\A"] = new_a;
|
||||
cell->connections["\\B"] = new_b;
|
||||
cell->parameters["\\A_WIDTH"] = new_a.width;
|
||||
cell->parameters["\\B_WIDTH"] = new_b.width;
|
||||
}
|
||||
|
||||
if (new_a.width == 0) {
|
||||
replace_cell(module, cell, "empty", "\\Y", RTLIL::SigSpec(cell->type == "$eq" ? RTLIL::State::S1 : RTLIL::State::S0));
|
||||
goto next_cell;
|
||||
}
|
||||
}
|
||||
|
||||
#define FOLD_1ARG_CELL(_t) \
|
||||
if (cell->type == "$" #_t) { \
|
||||
RTLIL::SigSpec a = cell->connections["\\A"]; \
|
||||
assign_map.apply(a); \
|
||||
if (a.is_fully_const()) { \
|
||||
a.optimize(); \
|
||||
RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \
|
||||
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, dummy_arg, \
|
||||
cell->parameters["\\A_SIGNED"].as_bool(), false, \
|
||||
cell->parameters["\\Y_WIDTH"].as_int())); \
|
||||
replace_cell(module, cell, stringf("%s", log_signal(a)), "\\Y", y); \
|
||||
goto next_cell; \
|
||||
} \
|
||||
}
|
||||
#define FOLD_2ARG_CELL(_t) \
|
||||
if (cell->type == "$" #_t) { \
|
||||
RTLIL::SigSpec a = cell->connections["\\A"]; \
|
||||
RTLIL::SigSpec b = cell->connections["\\B"]; \
|
||||
assign_map.apply(a), assign_map.apply(b); \
|
||||
if (a.is_fully_const() && b.is_fully_const()) { \
|
||||
a.optimize(), b.optimize(); \
|
||||
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, b.chunks[0].data, \
|
||||
cell->parameters["\\A_SIGNED"].as_bool(), \
|
||||
cell->parameters["\\B_SIGNED"].as_bool(), \
|
||||
cell->parameters["\\Y_WIDTH"].as_int())); \
|
||||
replace_cell(module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \
|
||||
goto next_cell; \
|
||||
} \
|
||||
}
|
||||
|
||||
FOLD_1ARG_CELL(not)
|
||||
FOLD_2ARG_CELL(and)
|
||||
FOLD_2ARG_CELL(or)
|
||||
FOLD_2ARG_CELL(xor)
|
||||
FOLD_2ARG_CELL(xnor)
|
||||
|
||||
FOLD_1ARG_CELL(reduce_and)
|
||||
FOLD_1ARG_CELL(reduce_or)
|
||||
FOLD_1ARG_CELL(reduce_xor)
|
||||
FOLD_1ARG_CELL(reduce_xnor)
|
||||
FOLD_1ARG_CELL(reduce_bool)
|
||||
|
||||
FOLD_1ARG_CELL(logic_not)
|
||||
FOLD_2ARG_CELL(logic_and)
|
||||
FOLD_2ARG_CELL(logic_or)
|
||||
|
||||
FOLD_2ARG_CELL(shl)
|
||||
FOLD_2ARG_CELL(shr)
|
||||
FOLD_2ARG_CELL(sshl)
|
||||
FOLD_2ARG_CELL(sshr)
|
||||
|
||||
FOLD_2ARG_CELL(lt)
|
||||
FOLD_2ARG_CELL(le)
|
||||
FOLD_2ARG_CELL(eq)
|
||||
FOLD_2ARG_CELL(ne)
|
||||
FOLD_2ARG_CELL(gt)
|
||||
FOLD_2ARG_CELL(ge)
|
||||
|
||||
FOLD_2ARG_CELL(add)
|
||||
FOLD_2ARG_CELL(sub)
|
||||
FOLD_2ARG_CELL(mul)
|
||||
FOLD_2ARG_CELL(div)
|
||||
FOLD_2ARG_CELL(mod)
|
||||
FOLD_2ARG_CELL(pow)
|
||||
|
||||
FOLD_1ARG_CELL(pos)
|
||||
FOLD_1ARG_CELL(neg)
|
||||
|
||||
if (cell->type == "$mux") {
|
||||
RTLIL::SigSpec input = cell->connections["\\S"];
|
||||
assign_map.apply(input);
|
||||
if (input.is_fully_const())
|
||||
ACTION_DO("\\Y", input.as_bool() ? cell->connections["\\B"] : cell->connections["\\A"]);
|
||||
}
|
||||
|
||||
next_cell:;
|
||||
#undef ACTION_DO
|
||||
#undef ACTION_DO_Y
|
||||
#undef FOLD_1ARG_CELL
|
||||
#undef FOLD_2ARG_CELL
|
||||
}
|
||||
}
|
||||
|
||||
struct OptConstPass : public Pass {
|
||||
OptConstPass() : Pass("opt_const") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT_CONST pass (perform const folding).\n");
|
||||
log_push();
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
do {
|
||||
did_something = false;
|
||||
replace_const_cells(mod_it.second);
|
||||
} while (did_something);
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} OptConstPass;
|
||||
|
417
passes/opt/opt_muxtree.cc
Normal file
417
passes/opt/opt_muxtree.cc
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
struct OptMuxtreeWorker
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
int removed_count;
|
||||
|
||||
typedef std::pair<RTLIL::Wire*,int> bitDef_t;
|
||||
|
||||
struct bitinfo_t {
|
||||
int num;
|
||||
bitDef_t bit;
|
||||
bool seen_non_mux;
|
||||
std::vector<int> mux_users;
|
||||
std::vector<int> mux_drivers;
|
||||
};
|
||||
|
||||
std::map<bitDef_t, int> bit2num;
|
||||
std::vector<bitinfo_t> bit2info;
|
||||
|
||||
struct portinfo_t {
|
||||
std::vector<int> ctrl_sigs;
|
||||
std::vector<int> input_sigs;
|
||||
std::vector<int> input_muxes;
|
||||
bool const_activated;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct muxinfo_t {
|
||||
RTLIL::Cell *cell;
|
||||
std::vector<portinfo_t> ports;
|
||||
};
|
||||
|
||||
std::vector<muxinfo_t> mux2info;
|
||||
|
||||
OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) :
|
||||
design(design), module(module), assign_map(module), removed_count(0)
|
||||
{
|
||||
log("Running muxtree optimizier on module %s..\n", module->name.c_str());
|
||||
|
||||
log(" Creating internal representation of mux trees.\n");
|
||||
|
||||
// Populate bit2info[]:
|
||||
// .seen_non_mux
|
||||
// .mux_users
|
||||
// .mux_drivers
|
||||
// Populate mux2info[].ports[]:
|
||||
// .ctrl_sigs
|
||||
// .input_sigs
|
||||
// .const_activated
|
||||
for (auto &cell_it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux")
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->connections["\\A"];
|
||||
RTLIL::SigSpec sig_b = cell->connections["\\B"];
|
||||
RTLIL::SigSpec sig_s = cell->connections["\\S"];
|
||||
RTLIL::SigSpec sig_y = cell->connections["\\Y"];
|
||||
|
||||
muxinfo_t muxinfo;
|
||||
muxinfo.cell = cell;
|
||||
|
||||
for (int i = 0; i < sig_s.width; i++) {
|
||||
RTLIL::SigSpec sig = sig_b.extract(i*sig_a.width, sig_a.width);
|
||||
RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1));
|
||||
portinfo_t portinfo;
|
||||
for (int idx : sig2bits(sig)) {
|
||||
add_to_list(bit2info[idx].mux_users, mux2info.size());
|
||||
add_to_list(portinfo.input_sigs, idx);
|
||||
}
|
||||
for (int idx : sig2bits(ctrl_sig))
|
||||
add_to_list(portinfo.ctrl_sigs, idx);
|
||||
portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool();
|
||||
portinfo.enabled = false;
|
||||
muxinfo.ports.push_back(portinfo);
|
||||
}
|
||||
|
||||
portinfo_t portinfo;
|
||||
for (int idx : sig2bits(sig_a)) {
|
||||
add_to_list(bit2info[idx].mux_users, mux2info.size());
|
||||
add_to_list(portinfo.input_sigs, idx);
|
||||
}
|
||||
portinfo.const_activated = false;
|
||||
portinfo.enabled = false;
|
||||
muxinfo.ports.push_back(portinfo);
|
||||
|
||||
for (int idx : sig2bits(sig_y))
|
||||
add_to_list(bit2info[idx].mux_drivers, mux2info.size());
|
||||
|
||||
for (int idx : sig2bits(sig_s))
|
||||
bit2info[idx].seen_non_mux = true;
|
||||
|
||||
mux2info.push_back(muxinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &it : cell->connections) {
|
||||
for (int idx : sig2bits(it.second))
|
||||
bit2info[idx].seen_non_mux = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &it : module->wires) {
|
||||
if (it.second->port_output)
|
||||
for (int idx : sig2bits(RTLIL::SigSpec(it.second)))
|
||||
bit2info[idx].seen_non_mux = true;
|
||||
}
|
||||
|
||||
if (mux2info.size() == 0) {
|
||||
log(" No muxes found in this module.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate mux2info[].ports[]:
|
||||
// .input_muxes
|
||||
for (size_t i = 0; i < bit2info.size(); i++)
|
||||
for (int j : bit2info[i].mux_users)
|
||||
for (auto &p : mux2info[j].ports) {
|
||||
if (is_in_list(p.input_sigs, i))
|
||||
for (int k : bit2info[i].mux_drivers)
|
||||
add_to_list(p.input_muxes, k);
|
||||
}
|
||||
|
||||
log(" Evaluating internal representation of mux trees.\n");
|
||||
|
||||
std::set<int> root_muxes;
|
||||
for (auto &bi : bit2info) {
|
||||
if (!bi.seen_non_mux)
|
||||
continue;
|
||||
for (int mux_idx : bi.mux_drivers)
|
||||
root_muxes.insert(mux_idx);
|
||||
}
|
||||
for (int mux_idx : root_muxes)
|
||||
eval_root_mux(mux_idx);
|
||||
|
||||
log(" Analyzing evaluation results.\n");
|
||||
|
||||
for (auto &mi : mux2info)
|
||||
{
|
||||
std::vector<int> live_ports;
|
||||
for (size_t port_idx = 0; port_idx < mi.ports.size(); port_idx++) {
|
||||
portinfo_t &pi = mi.ports[port_idx];
|
||||
if (pi.enabled) {
|
||||
live_ports.push_back(port_idx);
|
||||
} else {
|
||||
log(" dead port %zd/%zd on %s %s.\n", port_idx+1, mi.ports.size(),
|
||||
mi.cell->type.c_str(), mi.cell->name.c_str());
|
||||
OPT_DID_SOMETHING = true;
|
||||
removed_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (live_ports.size() == mi.ports.size())
|
||||
continue;
|
||||
|
||||
if (live_ports.size() == 0) {
|
||||
module->cells.erase(mi.cell->name);
|
||||
delete mi.cell;
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec sig_a = mi.cell->connections["\\A"];
|
||||
RTLIL::SigSpec sig_b = mi.cell->connections["\\B"];
|
||||
RTLIL::SigSpec sig_s = mi.cell->connections["\\S"];
|
||||
RTLIL::SigSpec sig_y = mi.cell->connections["\\Y"];
|
||||
|
||||
RTLIL::SigSpec sig_ports = sig_b;
|
||||
sig_ports.append(sig_a);
|
||||
|
||||
if (live_ports.size() == 1)
|
||||
{
|
||||
RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.width, sig_a.width);
|
||||
module->connections.push_back(RTLIL::SigSig(sig_y, sig_in));
|
||||
module->cells.erase(mi.cell->name);
|
||||
delete mi.cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s;
|
||||
|
||||
for (size_t i = 0; i < live_ports.size(); i++) {
|
||||
RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.width, sig_a.width);
|
||||
if (i == live_ports.size()-1) {
|
||||
new_sig_a = sig_in;
|
||||
} else {
|
||||
new_sig_b.append(sig_in);
|
||||
new_sig_s.append(sig_s.extract(live_ports[i], 1));
|
||||
}
|
||||
}
|
||||
|
||||
mi.cell->connections["\\A"] = new_sig_a;
|
||||
mi.cell->connections["\\B"] = new_sig_b;
|
||||
mi.cell->connections["\\S"] = new_sig_s;
|
||||
if (new_sig_s.width == 1) {
|
||||
mi.cell->type = "$mux";
|
||||
mi.cell->attributes.erase("\\S_WIDTH");
|
||||
} else {
|
||||
mi.cell->attributes["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool list_is_subset(const std::vector<int> &sub, const std::vector<int> &super)
|
||||
{
|
||||
for (int v : sub)
|
||||
if (!is_in_list(super, v))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_in_list(const std::vector<int> &list, int value)
|
||||
{
|
||||
for (int v : list)
|
||||
if (v == value)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_to_list(std::vector<int> &list, int value)
|
||||
{
|
||||
if (!is_in_list(list, value))
|
||||
list.push_back(value);
|
||||
}
|
||||
|
||||
std::vector<int> sig2bits(RTLIL::SigSpec sig)
|
||||
{
|
||||
std::vector<int> results;
|
||||
assign_map.apply(sig);
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks)
|
||||
if (c.wire != NULL) {
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (bit2num.count(bit) == 0) {
|
||||
bitinfo_t info;
|
||||
info.num = bit2info.size();
|
||||
info.bit = bit;
|
||||
info.seen_non_mux = false;
|
||||
bit2info.push_back(info);
|
||||
bit2num[info.bit] = info.num;
|
||||
}
|
||||
results.push_back(bit2num[bit]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
struct knowledge_t
|
||||
{
|
||||
// database of known inactive signals
|
||||
// the 2nd integer is a reference counter used to manage the
|
||||
// list. when it is non-zero the signal in known to be inactive
|
||||
std::map<int, int> known_inactive;
|
||||
|
||||
// database of known active signals
|
||||
// the 2nd dimension is the list of or-ed signals. so we know that
|
||||
// for each i there is a j so that known_active[i][j] points to an
|
||||
// inactive control signal.
|
||||
std::vector<std::vector<int>> known_active;
|
||||
|
||||
// this is just used to keep track of visited muxes in order to prohibit
|
||||
// endless recursion in mux loops
|
||||
std::set<int> visited_muxes;
|
||||
};
|
||||
|
||||
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx)
|
||||
{
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
muxinfo.ports[port_idx].enabled = true;
|
||||
|
||||
for (size_t i = 0; i < muxinfo.ports.size(); i++) {
|
||||
if (int(i) == port_idx)
|
||||
continue;
|
||||
for (int b : muxinfo.ports[i].ctrl_sigs)
|
||||
knowledge.known_inactive[b]++;
|
||||
}
|
||||
|
||||
if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated)
|
||||
knowledge.known_active.push_back(muxinfo.ports[port_idx].ctrl_sigs);
|
||||
|
||||
for (int m : muxinfo.ports[port_idx].input_muxes) {
|
||||
if (knowledge.visited_muxes.count(m) > 0)
|
||||
continue;
|
||||
knowledge.visited_muxes.insert(m);
|
||||
eval_mux(knowledge, m);
|
||||
knowledge.visited_muxes.erase(m);
|
||||
}
|
||||
|
||||
if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated)
|
||||
knowledge.known_active.pop_back();
|
||||
|
||||
for (size_t i = 0; i < muxinfo.ports.size(); i++) {
|
||||
if (int(i) == port_idx)
|
||||
continue;
|
||||
for (int b : muxinfo.ports[i].ctrl_sigs)
|
||||
knowledge.known_inactive[b]--;
|
||||
}
|
||||
}
|
||||
|
||||
void eval_mux(knowledge_t &knowledge, int mux_idx)
|
||||
{
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
|
||||
// if there is a constant activated port we just use it
|
||||
for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++)
|
||||
{
|
||||
portinfo_t &portinfo = muxinfo.ports[port_idx];
|
||||
if (portinfo.const_activated) {
|
||||
eval_mux_port(knowledge, mux_idx, port_idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// compare ports with known_active signals. if we find a match, only this
|
||||
// port can be active. do not include the last port (its the default port
|
||||
// that has no control signals).
|
||||
for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++)
|
||||
{
|
||||
portinfo_t &portinfo = muxinfo.ports[port_idx];
|
||||
for (size_t i = 0; i < knowledge.known_active.size(); i++) {
|
||||
if (list_is_subset(knowledge.known_active[i], portinfo.ctrl_sigs)) {
|
||||
eval_mux_port(knowledge, mux_idx, port_idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compare ports with known_inactive and known_active signals. If all control
|
||||
// signals of the port are know_inactive or if the control signals of all other
|
||||
// ports are known_active this port can't be activated. this loop includes the
|
||||
// default port but no known_inactive match is performed on the default port.
|
||||
for (size_t port_idx = 0; port_idx < muxinfo.ports.size(); port_idx++)
|
||||
{
|
||||
portinfo_t &portinfo = muxinfo.ports[port_idx];
|
||||
|
||||
if (port_idx < muxinfo.ports.size()-1) {
|
||||
bool found_non_known_inactive = false;
|
||||
for (int i : portinfo.ctrl_sigs)
|
||||
if (knowledge.known_inactive[i] == 0)
|
||||
found_non_known_inactive = true;
|
||||
if (!found_non_known_inactive)
|
||||
continue;
|
||||
}
|
||||
|
||||
bool port_active = true;
|
||||
std::vector<int> other_ctrl_sig;
|
||||
for (size_t i = 0; i < muxinfo.ports.size()-1; i++) {
|
||||
if (i == port_idx)
|
||||
continue;
|
||||
other_ctrl_sig.insert(other_ctrl_sig.end(),
|
||||
muxinfo.ports[i].ctrl_sigs.begin(), muxinfo.ports[i].ctrl_sigs.end());
|
||||
}
|
||||
for (size_t i = 0; i < knowledge.known_active.size(); i++) {
|
||||
if (list_is_subset(knowledge.known_active[i], other_ctrl_sig))
|
||||
port_active = false;
|
||||
}
|
||||
if (port_active)
|
||||
eval_mux_port(knowledge, mux_idx, port_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void eval_root_mux(int mux_idx)
|
||||
{
|
||||
knowledge_t knowledge;
|
||||
eval_mux(knowledge, mux_idx);
|
||||
}
|
||||
};
|
||||
|
||||
struct OptMuxtreePass : public Pass {
|
||||
OptMuxtreePass() : Pass("opt_muxtree") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
int total_count = 0;
|
||||
for (auto &mod_it : design->modules) {
|
||||
if (mod_it.second->processes.size() > 0) {
|
||||
log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str());
|
||||
} else {
|
||||
OptMuxtreeWorker worker(design, mod_it.second);
|
||||
total_count += worker.removed_count;
|
||||
}
|
||||
}
|
||||
log("Removed %d multiplexer ports.\n", total_count);
|
||||
}
|
||||
} OptMuxtreePass;
|
||||
|
236
passes/opt/opt_reduce.cc
Normal file
236
passes/opt/opt_reduce.cc
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/sha1.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
struct OptReduceWorker
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
|
||||
int total_count;
|
||||
bool did_something;
|
||||
|
||||
void opt_reduce(std::set<RTLIL::Cell*> &cells, SigSet<RTLIL::Cell*> &drivers, RTLIL::Cell *cell)
|
||||
{
|
||||
if (cells.count(cell) == 0)
|
||||
return;
|
||||
cells.erase(cell);
|
||||
|
||||
RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]);
|
||||
sig_a.sort_and_unify();
|
||||
sig_a.expand();
|
||||
|
||||
RTLIL::SigSpec new_sig_a;
|
||||
for (auto &chunk : sig_a.chunks)
|
||||
{
|
||||
if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S0) {
|
||||
if (cell->type == "$reduce_and") {
|
||||
new_sig_a = RTLIL::SigSpec(RTLIL::State::S0);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S1) {
|
||||
if (cell->type == "$reduce_or") {
|
||||
new_sig_a = RTLIL::SigSpec(RTLIL::State::S1);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (chunk.wire == NULL) {
|
||||
new_sig_a = RTLIL::SigSpec(RTLIL::State::Sx);
|
||||
break;
|
||||
}
|
||||
|
||||
bool imported_children = false;
|
||||
for (auto child_cell : drivers.find(chunk)) {
|
||||
if (child_cell->type == cell->type) {
|
||||
opt_reduce(cells, drivers, child_cell);
|
||||
new_sig_a.append(child_cell->connections["\\A"]);
|
||||
imported_children = true;
|
||||
}
|
||||
}
|
||||
if (!imported_children)
|
||||
new_sig_a.append(chunk);
|
||||
}
|
||||
new_sig_a.sort_and_unify();
|
||||
|
||||
if (new_sig_a != sig_a || sig_a.width != cell->connections["\\A"].width) {
|
||||
log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
|
||||
did_something = true;
|
||||
OPT_DID_SOMETHING = true;
|
||||
total_count++;
|
||||
}
|
||||
|
||||
cell->connections["\\A"] = new_sig_a;
|
||||
cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.width);
|
||||
return;
|
||||
}
|
||||
|
||||
void opt_mux(RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]);
|
||||
RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]);
|
||||
RTLIL::SigSpec sig_s = assign_map(cell->connections["\\S"]);
|
||||
|
||||
RTLIL::SigSpec new_sig_b, new_sig_s;
|
||||
std::set<RTLIL::SigSpec> handled_sig;
|
||||
|
||||
handled_sig.insert(sig_a);
|
||||
for (int i = 0; i < sig_s.width; i++)
|
||||
{
|
||||
RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.width, sig_a.width);
|
||||
if (handled_sig.count(this_b) > 0)
|
||||
continue;
|
||||
|
||||
RTLIL::SigSpec this_s = sig_s.extract(i, 1);
|
||||
for (int j = i+1; j < sig_s.width; j++) {
|
||||
RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.width, sig_a.width);
|
||||
if (this_b == that_b)
|
||||
this_s.append(sig_s.extract(j, 1));
|
||||
}
|
||||
|
||||
if (this_s.width > 1)
|
||||
{
|
||||
RTLIL::Wire *reduce_or_wire = new RTLIL::Wire;
|
||||
reduce_or_wire->name = NEW_ID;
|
||||
module->wires[reduce_or_wire->name] = reduce_or_wire;
|
||||
|
||||
RTLIL::Cell *reduce_or_cell = new RTLIL::Cell;
|
||||
reduce_or_cell->name = NEW_ID;
|
||||
reduce_or_cell->type = "$reduce_or";
|
||||
reduce_or_cell->connections["\\A"] = this_s;
|
||||
reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.width);
|
||||
reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
module->cells[reduce_or_cell->name] = reduce_or_cell;
|
||||
|
||||
this_s = RTLIL::SigSpec(reduce_or_wire);
|
||||
reduce_or_cell->connections["\\Y"] = this_s;
|
||||
}
|
||||
|
||||
new_sig_b.append(this_b);
|
||||
new_sig_s.append(this_s);
|
||||
handled_sig.insert(this_b);
|
||||
}
|
||||
|
||||
if (new_sig_s.width != sig_s.width) {
|
||||
log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
|
||||
did_something = true;
|
||||
OPT_DID_SOMETHING = true;
|
||||
total_count++;
|
||||
}
|
||||
|
||||
if (new_sig_s.width == 0)
|
||||
{
|
||||
module->connections.push_back(RTLIL::SigSig(cell->connections["\\Y"], cell->connections["\\A"]));
|
||||
assign_map.add(cell->connections["\\Y"], cell->connections["\\A"]);
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell->connections["\\B"] = new_sig_b;
|
||||
cell->connections["\\S"] = new_sig_s;
|
||||
if (new_sig_s.width > 1) {
|
||||
cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width);
|
||||
} else {
|
||||
cell->type = "$mux";
|
||||
cell->parameters.erase("\\S_WIDTH");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module) :
|
||||
design(design), module(module), assign_map(module)
|
||||
{
|
||||
log(" Optimizing cells in module %s.\n", module->name.c_str());
|
||||
|
||||
total_count = 0;
|
||||
did_something = true;
|
||||
|
||||
while (did_something)
|
||||
{
|
||||
did_something = false;
|
||||
|
||||
// merge trees of reduce_* cells to one single cell and unify input vectors
|
||||
// (only handle recduce_and and reduce_or for various reasons)
|
||||
|
||||
const char *type_list[] = { "$reduce_or", "$reduce_and" };
|
||||
for (auto type : type_list)
|
||||
{
|
||||
SigSet<RTLIL::Cell*> drivers;
|
||||
std::set<RTLIL::Cell*> cells;
|
||||
|
||||
for (auto &cell_it : module->cells) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (cell->type != type || !design->selected(module, cell))
|
||||
continue;
|
||||
drivers.insert(assign_map(cell->connections["\\Y"]), cell);
|
||||
cells.insert(cell);
|
||||
}
|
||||
|
||||
while (cells.size() > 0) {
|
||||
RTLIL::Cell *cell = *cells.begin();
|
||||
opt_reduce(cells, drivers, cell);
|
||||
}
|
||||
}
|
||||
|
||||
// merge identical inputs on $mux and $pmux cells
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if ((cell->type != "$mux" && cell->type != "$pmux" && cell->type != "$safe_pmux") || !design->selected(module, cell))
|
||||
continue;
|
||||
opt_mux(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct OptReducePass : public Pass {
|
||||
OptReducePass() : Pass("opt_reduce") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n");
|
||||
extra_args(args, 1, design);
|
||||
|
||||
int total_count = 0;
|
||||
for (auto &mod_it : design->modules) {
|
||||
if (!design->selected(mod_it.second))
|
||||
continue;
|
||||
OptReduceWorker worker(design, mod_it.second);
|
||||
total_count += worker.total_count;
|
||||
}
|
||||
|
||||
log("Performed a total of %d changes.\n", total_count);
|
||||
}
|
||||
} OptReducePass;
|
||||
|
135
passes/opt/opt_rmdff.cc
Normal file
135
passes/opt/opt_rmdff.cc
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static SigMap assign_map;
|
||||
|
||||
static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
|
||||
{
|
||||
RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r;
|
||||
RTLIL::Const val_cp, val_rp, val_rv;
|
||||
|
||||
if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") {
|
||||
sig_d = dff->connections["\\D"];
|
||||
sig_q = dff->connections["\\Q"];
|
||||
sig_c = dff->connections["\\C"];
|
||||
val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1);
|
||||
}
|
||||
else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" &&
|
||||
(dff->type[6] == 'N' || dff->type[6] == 'P') &&
|
||||
(dff->type[7] == 'N' || dff->type[7] == 'P') &&
|
||||
(dff->type[8] == '0' || dff->type[8] == '1')) {
|
||||
sig_d = dff->connections["\\D"];
|
||||
sig_q = dff->connections["\\Q"];
|
||||
sig_c = dff->connections["\\C"];
|
||||
sig_r = dff->connections["\\R"];
|
||||
val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
|
||||
val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
|
||||
val_rv = RTLIL::Const(dff->type[8] == '1', 1);
|
||||
}
|
||||
else if (dff->type == "$dff") {
|
||||
sig_d = dff->connections["\\D"];
|
||||
sig_q = dff->connections["\\Q"];
|
||||
sig_c = dff->connections["\\CLK"];
|
||||
val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
|
||||
}
|
||||
else if (dff->type == "$adff") {
|
||||
sig_d = dff->connections["\\D"];
|
||||
sig_q = dff->connections["\\Q"];
|
||||
sig_c = dff->connections["\\CLK"];
|
||||
sig_r = dff->connections["\\ARST"];
|
||||
val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
|
||||
val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1);
|
||||
val_rv = dff->parameters["\\ARST_VALUE"];
|
||||
}
|
||||
else
|
||||
log_error("abort.");
|
||||
|
||||
assign_map.apply(sig_d);
|
||||
assign_map.apply(sig_q);
|
||||
assign_map.apply(sig_c);
|
||||
assign_map.apply(sig_r);
|
||||
|
||||
if (sig_d.is_fully_const() && sig_r.width == 0) {
|
||||
RTLIL::SigSig conn(sig_q, sig_d);
|
||||
mod->connections.push_back(conn);
|
||||
goto delete_dff;
|
||||
}
|
||||
|
||||
if (sig_d == sig_q && sig_r.width == 0) {
|
||||
goto delete_dff;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
delete_dff:
|
||||
log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str());
|
||||
OPT_DID_SOMETHING = true;
|
||||
mod->cells.erase(dff->name);
|
||||
delete dff;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct OptRmdffPass : public Pass {
|
||||
OptRmdffPass() : Pass("opt_rmdff") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
int total_count = 0;
|
||||
log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
assign_map.set(mod_it.second);
|
||||
|
||||
std::vector<std::string> dff_list;
|
||||
for (auto &it : mod_it.second->cells) {
|
||||
if (it.second->type == "$_DFF_N_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_P_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_NN0_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_NN1_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_NP0_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_NP1_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_PN0_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_PN1_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_PP0_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$dff") dff_list.push_back(it.first);
|
||||
if (it.second->type == "$adff") dff_list.push_back(it.first);
|
||||
}
|
||||
|
||||
for (auto &id : dff_list) {
|
||||
if (mod_it.second->cells.count(id) > 0 &&
|
||||
handle_dff(mod_it.second, mod_it.second->cells[id]))
|
||||
total_count++;
|
||||
}
|
||||
}
|
||||
|
||||
assign_map.clear();
|
||||
log("Replaced %d DFF cells.\n", total_count);
|
||||
}
|
||||
} OptRmdffPass;
|
||||
|
239
passes/opt/opt_rmunused.cc
Normal file
239
passes/opt/opt_rmunused.cc
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
static CellTypes ct;
|
||||
|
||||
static void rmunused_module_cells(RTLIL::Module *module)
|
||||
{
|
||||
SigMap assign_map(module);
|
||||
std::set<RTLIL::Cell*> queue, unused;
|
||||
|
||||
SigSet<RTLIL::Cell*> wire2driver;
|
||||
for (auto &it : module->cells) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections) {
|
||||
if (!ct.cell_input(cell->type, it2.first)) {
|
||||
RTLIL::SigSpec sig = it2.second;
|
||||
assign_map.apply(sig);
|
||||
wire2driver.insert(sig, cell);
|
||||
}
|
||||
}
|
||||
if (cell->type == "$memwr")
|
||||
queue.insert(cell);
|
||||
unused.insert(cell);
|
||||
}
|
||||
|
||||
for (auto &it : module->wires) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
if (wire->port_output) {
|
||||
std::set<RTLIL::Cell*> cell_list;
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
assign_map.apply(sig);
|
||||
wire2driver.find(sig, cell_list);
|
||||
for (auto cell : cell_list)
|
||||
queue.insert(cell);
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.size() > 0)
|
||||
{
|
||||
std::set<RTLIL::Cell*> new_queue;
|
||||
for (auto cell : queue)
|
||||
unused.erase(cell);
|
||||
for (auto cell : queue) {
|
||||
for (auto &it : cell->connections) {
|
||||
if (!ct.cell_output(cell->type, it.first)) {
|
||||
std::set<RTLIL::Cell*> cell_list;
|
||||
RTLIL::SigSpec sig = it.second;
|
||||
assign_map.apply(sig);
|
||||
wire2driver.find(sig, cell_list);
|
||||
for (auto cell : cell_list) {
|
||||
if (unused.count(cell) > 0)
|
||||
new_queue.insert(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
queue.swap(new_queue);
|
||||
}
|
||||
|
||||
for (auto cell : unused) {
|
||||
log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
|
||||
OPT_DID_SOMETHING = true;
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
}
|
||||
}
|
||||
|
||||
static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2)
|
||||
{
|
||||
assert(s1.width == 1);
|
||||
assert(s2.width == 1);
|
||||
assert(s1.chunks.size() == 1);
|
||||
assert(s2.chunks.size() == 1);
|
||||
|
||||
RTLIL::Wire *w1 = s1.chunks[0].wire;
|
||||
RTLIL::Wire *w2 = s2.chunks[0].wire;
|
||||
|
||||
if (w1 == NULL || w2 == NULL)
|
||||
return w2 == NULL;
|
||||
|
||||
if (w1->port_input != w2->port_input)
|
||||
return w2->port_input;
|
||||
|
||||
if (w1->name[0] != w2->name[0])
|
||||
return w2->name[0] == '\\';
|
||||
|
||||
if (w1->attributes.size() != w2->attributes.size())
|
||||
return w2->attributes.size() > w1->attributes.size();
|
||||
|
||||
return w2->name < w1->name;
|
||||
}
|
||||
|
||||
static void rmunused_module_signals(RTLIL::Module *module)
|
||||
{
|
||||
SigMap assign_map(module);
|
||||
for (auto &it : module->wires) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigSpec s1 = RTLIL::SigSpec(wire, 1, i), s2 = assign_map(s1);
|
||||
if (!compare_signals(s1, s2))
|
||||
assign_map.add(s1);
|
||||
}
|
||||
}
|
||||
|
||||
module->connections.clear();
|
||||
|
||||
SigPool used_signals;
|
||||
SigPool used_signals_nodrivers;
|
||||
for (auto &it : module->cells) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections) {
|
||||
assign_map.apply(it2.second);
|
||||
used_signals.add(it2.second);
|
||||
if (!ct.cell_output(cell->type, it2.first))
|
||||
used_signals_nodrivers.add(it2.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RTLIL::Wire*> del_wires;
|
||||
for (auto &it : module->wires) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
if (wire->name[0] == '\\') {
|
||||
RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
|
||||
assign_map.apply(s2);
|
||||
if (!used_signals.check_any(s2) && wire->port_id == 0) {
|
||||
log(" removing unused non-port wire %s.\n", wire->name.c_str());
|
||||
del_wires.push_back(wire);
|
||||
} else {
|
||||
s1.expand();
|
||||
s2.expand();
|
||||
assert(s1.chunks.size() == s2.chunks.size());
|
||||
RTLIL::SigSig new_conn;
|
||||
for (size_t i = 0; i < s1.chunks.size(); i++)
|
||||
if (s1.chunks[i] != s2.chunks[i]) {
|
||||
new_conn.first.append(s1.chunks[i]);
|
||||
new_conn.second.append(s2.chunks[i]);
|
||||
}
|
||||
if (new_conn.first.width > 0) {
|
||||
new_conn.first.optimize();
|
||||
new_conn.second.optimize();
|
||||
module->connections.push_back(new_conn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!used_signals.check_any(RTLIL::SigSpec(wire)))
|
||||
del_wires.push_back(wire);
|
||||
}
|
||||
RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
|
||||
if (!used_signals_nodrivers.check_any(sig)) {
|
||||
std::string unused_bits;
|
||||
sig.expand();
|
||||
for (size_t i = 0; i < sig.chunks.size(); i++) {
|
||||
if (sig.chunks[i].wire == NULL)
|
||||
continue;
|
||||
if (!used_signals_nodrivers.check_any(sig)) {
|
||||
if (!unused_bits.empty())
|
||||
unused_bits += " ";
|
||||
unused_bits += stringf("%zd", i);
|
||||
}
|
||||
}
|
||||
if (unused_bits.empty() || wire->port_id != 0)
|
||||
wire->attributes.erase("\\unused_bits");
|
||||
else
|
||||
wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
|
||||
} else {
|
||||
wire->attributes.erase("\\unused_bits");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : del_wires) {
|
||||
module->wires.erase(wire->name);
|
||||
delete wire;
|
||||
}
|
||||
|
||||
if (del_wires.size() > 0)
|
||||
log(" removed %zd unused temporary wires.\n", del_wires.size());
|
||||
}
|
||||
|
||||
static void rmunused_module(RTLIL::Module *module)
|
||||
{
|
||||
log("Finding unused cells or wires in module %s..\n", module->name.c_str());
|
||||
|
||||
rmunused_module_cells(module);
|
||||
rmunused_module_signals(module);
|
||||
}
|
||||
|
||||
struct OptRmUnusedPass : public Pass {
|
||||
OptRmUnusedPass() : Pass("opt_rmunused") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT_RMUNUSED pass (remove unused cells and wires).\n");
|
||||
log_push();
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
if (mod_it.second->processes.size() > 0) {
|
||||
log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str());
|
||||
} else {
|
||||
rmunused_module(mod_it.second);
|
||||
}
|
||||
}
|
||||
|
||||
ct.clear();
|
||||
log_pop();
|
||||
}
|
||||
} OptRmUnusedPass;
|
||||
|
250
passes/opt/opt_share.cc
Normal file
250
passes/opt/opt_share.cc
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* 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 "opt_status.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/sha1.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
#define USE_CELL_HASH_CACHE
|
||||
|
||||
struct OptShareWorker
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
|
||||
CellTypes ct;
|
||||
int total_count;
|
||||
#ifdef USE_CELL_HASH_CACHE
|
||||
std::map<const RTLIL::Cell*, std::string> cell_hash_cache;
|
||||
#endif
|
||||
|
||||
#ifdef USE_CELL_HASH_CACHE
|
||||
std::string int_to_hash_string(unsigned int v)
|
||||
{
|
||||
if (v == 0)
|
||||
return "0";
|
||||
std::string str = "";
|
||||
while (v > 0) {
|
||||
str += 'a' + (v & 15);
|
||||
v = v >> 4;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell_hash_cache.count(cell) > 0)
|
||||
return cell_hash_cache[cell];
|
||||
|
||||
std::string hash_string = cell->type + "\n";
|
||||
|
||||
for (auto &it : cell->parameters)
|
||||
hash_string += "P " + it.first + "=" + it.second.as_string() + "\n";
|
||||
|
||||
for (auto &it : cell->connections) {
|
||||
if (ct.cell_output(cell->type, it.first))
|
||||
continue;
|
||||
RTLIL::SigSpec sig = it.second;
|
||||
assign_map.apply(sig);
|
||||
hash_string += "C " + it.first + "=";
|
||||
for (auto &chunk : sig.chunks) {
|
||||
if (chunk.wire)
|
||||
hash_string += "{" + chunk.wire->name + " " +
|
||||
int_to_hash_string(chunk.offset) + " " +
|
||||
int_to_hash_string(chunk.width) + "}";
|
||||
else
|
||||
hash_string += chunk.data.as_string();
|
||||
}
|
||||
hash_string += "\n";
|
||||
}
|
||||
|
||||
unsigned char hash[20];
|
||||
char hash_hex_string[41];
|
||||
sha1::calc(hash_string.c_str(), hash_string.size(), hash);
|
||||
sha1::toHexString(hash, hash_hex_string);
|
||||
cell_hash_cache[cell] = hash_hex_string;
|
||||
|
||||
return cell_hash_cache[cell];
|
||||
}
|
||||
#endif
|
||||
|
||||
bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool <)
|
||||
{
|
||||
#ifdef USE_CELL_HASH_CACHE
|
||||
std::string hash1 = hash_cell_parameters_and_connections(cell1);
|
||||
std::string hash2 = hash_cell_parameters_and_connections(cell2);
|
||||
|
||||
if (hash1 != hash2) {
|
||||
lt = hash1 < hash2;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cell1->parameters != cell2->parameters) {
|
||||
lt = cell1->parameters < cell2->parameters;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections;
|
||||
std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections;
|
||||
|
||||
for (auto &it : conn1) {
|
||||
if (ct.cell_output(cell1->type, it.first))
|
||||
it.second = RTLIL::SigSpec();
|
||||
else
|
||||
assign_map.apply(it.second);
|
||||
}
|
||||
|
||||
for (auto &it : conn2) {
|
||||
if (ct.cell_output(cell2->type, it.first))
|
||||
it.second = RTLIL::SigSpec();
|
||||
else
|
||||
assign_map.apply(it.second);
|
||||
}
|
||||
|
||||
if (conn1 != conn2) {
|
||||
lt = conn1 < conn2;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
|
||||
{
|
||||
if (cell1->type != cell2->type)
|
||||
return cell1->type < cell2->type;
|
||||
|
||||
if (!ct.cell_known(cell1->type))
|
||||
return cell1 < cell2;
|
||||
|
||||
bool lt;
|
||||
if (compare_cell_parameters_and_connections(cell1, cell2, lt))
|
||||
return lt;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct CompareCells {
|
||||
OptShareWorker *that;
|
||||
CompareCells(OptShareWorker *that) : that(that) {}
|
||||
bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const {
|
||||
return that->compare_cells(cell1, cell2);
|
||||
}
|
||||
};
|
||||
|
||||
OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) :
|
||||
design(design), module(module), assign_map(module)
|
||||
{
|
||||
total_count = 0;
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
if (mode_nomux) {
|
||||
ct.cell_types.erase("$mux");
|
||||
ct.cell_types.erase("$pmux");
|
||||
ct.cell_types.erase("$safe_pmux");
|
||||
}
|
||||
|
||||
log("Finding identical cells in module `%s'.\n", module->name.c_str());
|
||||
assign_map.set(module);
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something)
|
||||
{
|
||||
#ifdef USE_CELL_HASH_CACHE
|
||||
cell_hash_cache.clear();
|
||||
#endif
|
||||
std::vector<RTLIL::Cell*> cells;
|
||||
cells.reserve(module->cells.size());
|
||||
for (auto &it : module->cells) {
|
||||
if (ct.cell_known(it.second->type) && design->selected(module, it.second))
|
||||
cells.push_back(it.second);
|
||||
}
|
||||
|
||||
did_something = false;
|
||||
std::map<RTLIL::Cell*, RTLIL::Cell*, CompareCells> sharemap(CompareCells(this));
|
||||
for (auto cell : cells)
|
||||
{
|
||||
if (sharemap.count(cell) > 0) {
|
||||
did_something = true;
|
||||
log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
|
||||
for (auto &it : cell->connections) {
|
||||
if (ct.cell_output(cell->type, it.first)) {
|
||||
RTLIL::SigSpec other_sig = sharemap[cell]->connections[it.first];
|
||||
log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
|
||||
log_signal(it.second), log_signal(other_sig));
|
||||
module->connections.push_back(RTLIL::SigSig(it.second, other_sig));
|
||||
assign_map.add(it.second, other_sig);
|
||||
}
|
||||
}
|
||||
log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
|
||||
module->cells.erase(cell->name);
|
||||
OPT_DID_SOMETHING = true;
|
||||
total_count++;
|
||||
delete cell;
|
||||
} else {
|
||||
sharemap[cell] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct OptSharePass : public Pass {
|
||||
OptSharePass() : Pass("opt_share") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing OPT_SHARE pass (detect identical cells).\n");
|
||||
|
||||
bool mode_nomux = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-nomux") {
|
||||
mode_nomux = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_count = 0;
|
||||
for (auto &mod_it : design->modules) {
|
||||
if (!design->selected(mod_it.second))
|
||||
continue;
|
||||
OptShareWorker worker(design, mod_it.second, mode_nomux);
|
||||
total_count += worker.total_count;
|
||||
}
|
||||
|
||||
log("Removed a total of %d cells.\n", total_count);
|
||||
}
|
||||
} OptSharePass;
|
||||
|
26
passes/opt/opt_status.h
Normal file
26
passes/opt/opt_status.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPT_STATUS_H
|
||||
#define OPT_STATUS_H
|
||||
|
||||
extern bool OPT_DID_SOMETHING;
|
||||
|
||||
#endif
|
||||
|
8
passes/proc/Makefile.inc
Normal file
8
passes/proc/Makefile.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
OBJS += passes/proc/proc.o
|
||||
OBJS += passes/proc/proc_clean.o
|
||||
OBJS += passes/proc/proc_rmdead.o
|
||||
OBJS += passes/proc/proc_arst.o
|
||||
OBJS += passes/proc/proc_mux.o
|
||||
OBJS += passes/proc/proc_dff.o
|
||||
|
44
passes/proc/proc.cc
Normal file
44
passes/proc/proc.cc
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct ProcPass : public Pass {
|
||||
ProcPass() : Pass("proc") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC pass (convert processes to netlists).\n");
|
||||
log_push();
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
Pass::call(design, "proc_clean");
|
||||
Pass::call(design, "proc_rmdead");
|
||||
Pass::call(design, "proc_arst");
|
||||
Pass::call(design, "proc_mux");
|
||||
Pass::call(design, "proc_dff");
|
||||
Pass::call(design, "proc_clean");
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} ProcPass;
|
||||
|
191
passes/proc/proc_arst.cc
Normal file
191
passes/proc/proc_arst.cc
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity)
|
||||
{
|
||||
if (signal.width != 1)
|
||||
return false;
|
||||
if (signal == ref)
|
||||
return true;
|
||||
|
||||
for (auto &cell_it : mod->cells) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (cell->type == "$reduce_or" && cell->connections["\\Y"] == signal)
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
if (cell->type == "$reduce_bool" && cell->connections["\\Y"] == signal)
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
if (cell->type == "$logic_not" && cell->connections["\\Y"] == signal) {
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
}
|
||||
if (cell->type == "$not" && cell->connections["\\Y"] == signal) {
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
}
|
||||
if (cell->type == "$eq" && cell->connections["\\Y"] == signal) {
|
||||
if (cell->connections["\\A"].is_fully_const()) {
|
||||
if (!cell->connections["\\A"].as_bool())
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\B"], ref, polarity);
|
||||
}
|
||||
if (cell->connections["\\B"].is_fully_const()) {
|
||||
if (!cell->connections["\\B"].as_bool())
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
}
|
||||
}
|
||||
if (cell->type == "$ne" && cell->connections["\\Y"] == signal) {
|
||||
if (cell->connections["\\A"].is_fully_const()) {
|
||||
if (cell->connections["\\A"].as_bool())
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\B"], ref, polarity);
|
||||
}
|
||||
if (cell->connections["\\B"].is_fully_const()) {
|
||||
if (cell->connections["\\B"].as_bool())
|
||||
polarity = !polarity;
|
||||
return check_signal(mod, cell->connections["\\A"], ref, polarity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown)
|
||||
{
|
||||
for (auto &action : cs->actions) {
|
||||
if (unknown)
|
||||
rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.width), &rval);
|
||||
else
|
||||
rspec.replace(action.first, action.second, &rval);
|
||||
}
|
||||
|
||||
for (auto sw : cs->switches) {
|
||||
if (sw->signal.width == 0) {
|
||||
for (auto cs2 : sw->cases)
|
||||
apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown);
|
||||
}
|
||||
bool this_polarity = polarity;
|
||||
if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
|
||||
for (auto cs2 : sw->cases) {
|
||||
for (auto comp : cs2->compare)
|
||||
if (comp == RTLIL::SigSpec(this_polarity, 1))
|
||||
goto matched_case;
|
||||
if (cs2->compare.size() == 0) {
|
||||
matched_case:
|
||||
apply_const(mod, rspec, rval, cs2, const_sig, polarity, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto cs2 : sw->cases)
|
||||
apply_const(mod, rspec, rval, cs2, const_sig, polarity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity)
|
||||
{
|
||||
for (auto sw : cs->switches) {
|
||||
bool this_polarity = polarity;
|
||||
if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
|
||||
bool found_rem_path = false;
|
||||
for (size_t i = 0; i < sw->cases.size(); i++) {
|
||||
RTLIL::CaseRule *cs2 = sw->cases[i];
|
||||
for (auto comp : cs2->compare)
|
||||
if (comp == RTLIL::SigSpec(this_polarity, 1))
|
||||
goto matched_case;
|
||||
if (found_rem_path) {
|
||||
matched_case:
|
||||
sw->cases.erase(sw->cases.begin() + (i--));
|
||||
delete cs2;
|
||||
continue;
|
||||
}
|
||||
found_rem_path = true;
|
||||
cs2->compare.clear();
|
||||
}
|
||||
sw->signal = RTLIL::SigSpec();
|
||||
} else {
|
||||
for (auto cs2 : sw->cases)
|
||||
eliminate_const(mod, cs2, const_sig, polarity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map)
|
||||
{
|
||||
if (proc->root_case.switches.size() != 1)
|
||||
return;
|
||||
|
||||
RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal;
|
||||
|
||||
for (auto &sync : proc->syncs) {
|
||||
if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
|
||||
bool polarity = sync->type == RTLIL::SyncType::STp;
|
||||
if (check_signal(mod, root_sig, sync->signal, polarity)) {
|
||||
log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str());
|
||||
sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
|
||||
for (auto &action : sync->actions) {
|
||||
RTLIL::SigSpec rspec = action.second;
|
||||
RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.width);
|
||||
RTLIL::SigSpec last_rval;
|
||||
for (int count = 0; rval != last_rval; count++) {
|
||||
last_rval = rval;
|
||||
apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false);
|
||||
assign_map.apply(rval);
|
||||
if (rval.is_fully_const())
|
||||
break;
|
||||
if (count > 100)
|
||||
log_error("Async reset %s yields endless loop at value %s for signal %s.\n",
|
||||
log_signal(sync->signal), log_signal(rval), log_signal(action.first));
|
||||
rspec = rval;
|
||||
}
|
||||
if (rval.has_marked_bits())
|
||||
log_error("Async reset %s yields non-constant value %s for signal %s.\n",
|
||||
log_signal(sync->signal), log_signal(rval), log_signal(action.first));
|
||||
action.second = rval;
|
||||
}
|
||||
eliminate_const(mod, &proc->root_case, root_sig, polarity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcArstPass : public Pass {
|
||||
ProcArstPass() : Pass("proc_arst") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC_ARST pass (detect async resets in processes).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
SigMap assign_map(mod_it.second);
|
||||
for (auto &proc_it : mod_it.second->processes)
|
||||
proc_arst(mod_it.second, proc_it.second, assign_map);
|
||||
}
|
||||
}
|
||||
} ProcArstPass;
|
||||
|
160
passes/proc/proc_clean.cc
Normal file
160
passes/proc/proc_clean.cc
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void switch_clean(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count);
|
||||
static void case_clean(RTLIL::CaseRule *cs, bool &did_something, int &count);
|
||||
|
||||
static void switch_clean(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count)
|
||||
{
|
||||
if (sw->signal.width > 0 && sw->signal.is_fully_const())
|
||||
{
|
||||
int found_matching_case_idx = -1;
|
||||
for (int i = 0; i < int(sw->cases.size()) && found_matching_case_idx < 0; i++)
|
||||
{
|
||||
RTLIL::CaseRule *cs = sw->cases[i];
|
||||
if (cs->compare.size() == 0)
|
||||
break;
|
||||
for (int j = 0; j < int(cs->compare.size()); j++) {
|
||||
RTLIL::SigSpec &val = cs->compare[j];
|
||||
if (!val.is_fully_const())
|
||||
continue;
|
||||
if (val == sw->signal) {
|
||||
cs->compare.clear();
|
||||
found_matching_case_idx = i;
|
||||
break;
|
||||
} else
|
||||
cs->compare.erase(cs->compare.begin()+(j--));
|
||||
}
|
||||
if (cs->compare.size() == 0 && found_matching_case_idx < 0) {
|
||||
sw->cases.erase(sw->cases.begin()+(i--));
|
||||
delete cs;
|
||||
}
|
||||
}
|
||||
while (found_matching_case_idx >= 0 && int(sw->cases.size()) > found_matching_case_idx+1) {
|
||||
delete sw->cases.back();
|
||||
sw->cases.pop_back();
|
||||
}
|
||||
if (found_matching_case_idx == 0)
|
||||
sw->signal = RTLIL::SigSpec();
|
||||
}
|
||||
|
||||
if (sw->cases.size() == 1 && (sw->signal.width == 0 || sw->cases[0]->compare.empty()))
|
||||
{
|
||||
did_something = true;
|
||||
for (auto &action : sw->cases[0]->actions)
|
||||
parent->actions.push_back(action);
|
||||
for (auto sw2 : sw->cases[0]->switches)
|
||||
parent->switches.push_back(sw2);
|
||||
sw->cases[0]->switches.clear();
|
||||
delete sw->cases[0];
|
||||
sw->cases.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool all_cases_are_empty = true;
|
||||
for (auto cs : sw->cases) {
|
||||
if (cs->actions.size() != 0 || cs->switches.size() != 0)
|
||||
all_cases_are_empty = false;
|
||||
case_clean(cs, did_something, count);
|
||||
}
|
||||
if (all_cases_are_empty) {
|
||||
did_something = true;
|
||||
for (auto cs : sw->cases)
|
||||
delete cs;
|
||||
sw->cases.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void case_clean(RTLIL::CaseRule *cs, bool &did_something, int &count)
|
||||
{
|
||||
for (size_t i = 0; i < cs->actions.size(); i++) {
|
||||
if (cs->actions[i].first.width == 0) {
|
||||
did_something = true;
|
||||
cs->actions.erase(cs->actions.begin() + (i--));
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < cs->switches.size(); i++) {
|
||||
RTLIL::SwitchRule *sw = cs->switches[i];
|
||||
if (sw->cases.size() == 0) {
|
||||
cs->switches.erase(cs->switches.begin() + (i--));
|
||||
did_something = true;
|
||||
delete sw;
|
||||
count++;
|
||||
} else
|
||||
switch_clean(sw, cs, did_something, count);
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
|
||||
{
|
||||
int count = 0;
|
||||
bool did_something = true;
|
||||
for (size_t i = 0; i < proc->syncs.size(); i++) {
|
||||
for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++)
|
||||
if (proc->syncs[i]->actions[j].first.width == 0)
|
||||
proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--));
|
||||
if (proc->syncs[i]->actions.size() == 0) {
|
||||
delete proc->syncs[i];
|
||||
proc->syncs.erase(proc->syncs.begin() + (i--));
|
||||
}
|
||||
}
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
case_clean(&proc->root_case, did_something, count);
|
||||
}
|
||||
if (count > 0)
|
||||
log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str());
|
||||
total_count += count;
|
||||
}
|
||||
|
||||
struct ProcCleanPass : public Pass {
|
||||
ProcCleanPass() : Pass("proc_clean") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
int total_count = 0;
|
||||
log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
std::vector<std::string> delme;
|
||||
for (auto &proc_it : mod_it.second->processes) {
|
||||
proc_clean(mod_it.second, proc_it.second, total_count);
|
||||
if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 &&
|
||||
proc_it.second->root_case.actions.size() == 0) {
|
||||
log("Removing empty process `%s.%s'.\n", mod_it.first.c_str(), proc_it.second->name.c_str());
|
||||
delme.push_back(proc_it.first);
|
||||
}
|
||||
}
|
||||
for (auto &id : delme) {
|
||||
delete mod_it.second->processes[id];
|
||||
mod_it.second->processes.erase(id);
|
||||
}
|
||||
}
|
||||
|
||||
log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
|
||||
}
|
||||
} ProcCleanPass;
|
||||
|
178
passes/proc/proc_dff.cc
Normal file
178
passes/proc/proc_dff.cc
Normal file
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include "kernel/log.h"
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc)
|
||||
{
|
||||
RTLIL::SigSpec lvalue;
|
||||
|
||||
for (auto sync : proc->syncs)
|
||||
for (auto &action : sync->actions)
|
||||
if (action.first.width > 0) {
|
||||
lvalue = action.first;
|
||||
lvalue.sort_and_unify();
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto sync : proc->syncs) {
|
||||
RTLIL::SigSpec this_lvalue;
|
||||
for (auto &action : sync->actions)
|
||||
this_lvalue.append(action.first);
|
||||
this_lvalue.sort_and_unify();
|
||||
RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue);
|
||||
if (common_sig.width > 0)
|
||||
lvalue = common_sig;
|
||||
}
|
||||
|
||||
return lvalue;
|
||||
}
|
||||
|
||||
static void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_rst, RTLIL::SigSpec sig_out,
|
||||
bool clk_polarity, bool arst_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec *arst, RTLIL::Process *proc)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$procdff$" << (RTLIL::autoidx++);
|
||||
|
||||
RTLIL::Cell *cell = new RTLIL::Cell;
|
||||
cell->name = sstr.str();
|
||||
cell->type = arst ? "$adff" : "$dff";
|
||||
cell->attributes = proc->attributes;
|
||||
mod->cells[cell->name] = cell;
|
||||
|
||||
cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width);
|
||||
if (arst) {
|
||||
cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity, 1);
|
||||
cell->parameters["\\ARST_VALUE"] = val_rst;
|
||||
}
|
||||
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1);
|
||||
|
||||
cell->connections["\\D"] = sig_in;
|
||||
cell->connections["\\Q"] = sig_out;
|
||||
if (arst)
|
||||
cell->connections["\\ARST"] = *arst;
|
||||
cell->connections["\\CLK"] = clk;
|
||||
|
||||
log(" created %s cell `%s' with %s edge clock", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative");
|
||||
if (arst)
|
||||
log(" and %s level reset", arst_polarity ? "positive" : "negative");
|
||||
log(".\n");
|
||||
}
|
||||
|
||||
static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
RTLIL::SigSpec sig = find_any_lvalue(proc);
|
||||
|
||||
if (sig.width == 0)
|
||||
break;
|
||||
|
||||
log("Creating register for signal `%s.%s' using process `%s.%s'.\n",
|
||||
mod->name.c_str(), log_signal(sig), mod->name.c_str(), proc->name.c_str());
|
||||
|
||||
RTLIL::SigSpec insig = RTLIL::SigSpec(RTLIL::State::Sz, sig.width);
|
||||
RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width);
|
||||
RTLIL::SyncRule *sync_level = NULL;
|
||||
RTLIL::SyncRule *sync_edge = NULL;
|
||||
RTLIL::SyncRule *sync_always = NULL;
|
||||
|
||||
for (auto sync : proc->syncs)
|
||||
for (auto &action : sync->actions)
|
||||
{
|
||||
if (action.first.extract(sig).width == 0)
|
||||
continue;
|
||||
|
||||
if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) {
|
||||
if (sync_level != NULL && sync_level != sync)
|
||||
log_error("Multiple level sensitive events found for this signal!\n");
|
||||
sig.replace(action.first, action.second, &rstval);
|
||||
sync_level = sync;
|
||||
}
|
||||
else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
|
||||
if (sync_edge != NULL && sync_edge != sync)
|
||||
log_error("Multiple edge sensitive events found for this signal!\n");
|
||||
sig.replace(action.first, action.second, &insig);
|
||||
sync_edge = sync;
|
||||
}
|
||||
else if (sync->type == RTLIL::SyncType::STa) {
|
||||
if (sync_always != NULL && sync_always != sync)
|
||||
log_error("Multiple always events found for this signal!\n");
|
||||
sig.replace(action.first, action.second, &insig);
|
||||
sync_always = sync;
|
||||
}
|
||||
else {
|
||||
log_error("Event with any-edge sensitivity found for this signal!\n");
|
||||
}
|
||||
|
||||
action.first.remove2(sig, &action.second);
|
||||
}
|
||||
|
||||
ce.assign_map.apply(insig);
|
||||
ce.assign_map.apply(rstval);
|
||||
ce.assign_map.apply(sig);
|
||||
|
||||
insig.optimize();
|
||||
rstval.optimize();
|
||||
sig.optimize();
|
||||
|
||||
if (sync_always) {
|
||||
if (sync_edge || sync_level)
|
||||
log_error("Mixed always event with edge and/or level sensitive events!\n");
|
||||
log(" created direct connection (no actual register cell created).\n");
|
||||
mod->connections.push_back(RTLIL::SigSig(sig, insig));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sync_edge)
|
||||
log_error("Missing edge-sensitive event for this signal!\n");
|
||||
|
||||
if (!rstval.is_fully_const() && !ce.eval(rstval))
|
||||
log_error("Async reset value `%s' is not constant!\n", log_signal(rstval));
|
||||
|
||||
gen_dff(mod, insig, rstval.chunks[0].data, sig,
|
||||
sync_edge->type == RTLIL::SyncType::STp,
|
||||
sync_level && sync_level->type == RTLIL::SyncType::ST1,
|
||||
sync_edge->signal, sync_level ? &sync_level->signal : NULL, proc);
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcDffPass : public Pass {
|
||||
ProcDffPass() : Pass("proc_dff") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules) {
|
||||
ConstEval ce(mod_it.second);
|
||||
for (auto &proc_it : mod_it.second->processes)
|
||||
proc_dff(mod_it.second, proc_it.second, ce);
|
||||
}
|
||||
}
|
||||
} ProcDffPass;
|
||||
|
294
passes/proc/proc_mux.cc
Normal file
294
passes/proc/proc_mux.cc
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/bitpattern.h"
|
||||
#include "kernel/log.h"
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs)
|
||||
{
|
||||
for (auto &action : cs->actions) {
|
||||
if (action.first.width)
|
||||
return action.first;
|
||||
}
|
||||
|
||||
for (auto sw : cs->switches)
|
||||
for (auto cs2 : sw->cases) {
|
||||
RTLIL::SigSpec sig = find_any_lvalue(cs2);
|
||||
if (sig.width)
|
||||
return sig;
|
||||
}
|
||||
|
||||
return RTLIL::SigSpec();
|
||||
}
|
||||
|
||||
static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig)
|
||||
{
|
||||
for (auto &action : cs->actions) {
|
||||
RTLIL::SigSpec lvalue = action.first.extract(sig);
|
||||
if (lvalue.width)
|
||||
sig = lvalue;
|
||||
}
|
||||
|
||||
for (auto sw : cs->switches)
|
||||
for (auto cs2 : sw->cases)
|
||||
extract_core_signal(cs2, sig);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$procmux$" << (RTLIL::autoidx++);
|
||||
|
||||
RTLIL::Wire *cmp_wire = new RTLIL::Wire;
|
||||
cmp_wire->name = sstr.str() + "_CMP";
|
||||
cmp_wire->width = 0;
|
||||
mod->wires[cmp_wire->name] = cmp_wire;
|
||||
|
||||
for (auto comp : compare)
|
||||
{
|
||||
RTLIL::SigSpec sig = signal;
|
||||
sig.expand();
|
||||
comp.expand();
|
||||
|
||||
// get rid of don't-care bits
|
||||
assert(sig.width == comp.width);
|
||||
for (int i = 0; i < comp.width; i++)
|
||||
if (comp.chunks[i].wire == NULL && comp.chunks[i].data.bits[0] == RTLIL::State::Sa) {
|
||||
sig.remove(i, 1);
|
||||
comp.remove(i--, 1);
|
||||
}
|
||||
if (comp.width == 0)
|
||||
return RTLIL::SigSpec();
|
||||
sig.optimize();
|
||||
comp.optimize();
|
||||
|
||||
if (sig.width == 1 && comp == RTLIL::SigSpec(1,1))
|
||||
{
|
||||
mod->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++), sig));
|
||||
}
|
||||
else
|
||||
{
|
||||
// create compare cell
|
||||
RTLIL::Cell *eq_cell = new RTLIL::Cell;
|
||||
std::stringstream sstr2;
|
||||
sstr2 << sstr.str() << "_CMP" << cmp_wire->width;
|
||||
eq_cell->name = sstr2.str();
|
||||
eq_cell->type = "$eq";
|
||||
eq_cell->attributes = sw->attributes;
|
||||
mod->cells[eq_cell->name] = eq_cell;
|
||||
|
||||
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
|
||||
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0);
|
||||
|
||||
eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.width);
|
||||
eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(comp.width);
|
||||
eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
|
||||
eq_cell->connections["\\A"] = sig;
|
||||
eq_cell->connections["\\B"] = comp;
|
||||
eq_cell->connections["\\Y"] = RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++);
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Wire *ctrl_wire;
|
||||
if (cmp_wire->width == 1)
|
||||
{
|
||||
ctrl_wire = cmp_wire;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl_wire = new RTLIL::Wire;
|
||||
ctrl_wire->name = sstr.str() + "_CTRL";
|
||||
ctrl_wire->width = 1;
|
||||
mod->wires[ctrl_wire->name] = ctrl_wire;
|
||||
|
||||
// reduce cmp vector to one logic signal
|
||||
RTLIL::Cell *any_cell = new RTLIL::Cell;
|
||||
any_cell->name = sstr.str() + "_ANY";
|
||||
any_cell->type = "$reduce_or";
|
||||
any_cell->attributes = sw->attributes;
|
||||
mod->cells[any_cell->name] = any_cell;
|
||||
|
||||
any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
|
||||
any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width);
|
||||
any_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
|
||||
|
||||
any_cell->connections["\\A"] = cmp_wire;
|
||||
any_cell->connections["\\Y"] = RTLIL::SigSpec(ctrl_wire);
|
||||
}
|
||||
|
||||
return RTLIL::SigSpec(ctrl_wire);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw)
|
||||
{
|
||||
assert(when_signal.width == else_signal.width);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$procmux$" << (RTLIL::autoidx++);
|
||||
|
||||
// the trivial cases
|
||||
if (compare.size() == 0 || when_signal == else_signal)
|
||||
return when_signal;
|
||||
|
||||
// compare results
|
||||
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw);
|
||||
if (ctrl_sig.width == 0)
|
||||
return when_signal;
|
||||
assert(ctrl_sig.width == 1);
|
||||
|
||||
// prepare multiplexer output signal
|
||||
RTLIL::Wire *result_wire = new RTLIL::Wire;
|
||||
result_wire->name = sstr.str() + "_Y";
|
||||
result_wire->width = when_signal.width;
|
||||
mod->wires[result_wire->name] = result_wire;
|
||||
|
||||
// create the multiplexer itself
|
||||
RTLIL::Cell *mux_cell = new RTLIL::Cell;
|
||||
mux_cell->name = sstr.str();
|
||||
mux_cell->type = "$mux";
|
||||
mux_cell->attributes = sw->attributes;
|
||||
mod->cells[mux_cell->name] = mux_cell;
|
||||
|
||||
mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.width);
|
||||
mux_cell->connections["\\A"] = else_signal;
|
||||
mux_cell->connections["\\B"] = when_signal;
|
||||
mux_cell->connections["\\S"] = ctrl_sig;
|
||||
mux_cell->connections["\\Y"] = RTLIL::SigSpec(result_wire);
|
||||
|
||||
last_mux_cell = mux_cell;
|
||||
return RTLIL::SigSpec(result_wire);
|
||||
}
|
||||
|
||||
static void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw)
|
||||
{
|
||||
assert(last_mux_cell != NULL);
|
||||
assert(when_signal.width == last_mux_cell->connections["\\A"].width);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$procmux$" << (RTLIL::autoidx++);
|
||||
|
||||
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw);
|
||||
assert(ctrl_sig.width == 1);
|
||||
last_mux_cell->type = "$pmux";
|
||||
last_mux_cell->connections["\\S"].append(ctrl_sig);
|
||||
last_mux_cell->connections["\\B"].append(when_signal);
|
||||
last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->connections["\\S"].width;
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval)
|
||||
{
|
||||
RTLIL::SigSpec result = defval;
|
||||
|
||||
for (auto &action : cs->actions) {
|
||||
sig.replace(action.first, action.second, &result);
|
||||
action.first.remove2(sig, &action.second);
|
||||
}
|
||||
|
||||
for (auto sw : cs->switches)
|
||||
{
|
||||
// detect groups of parallel cases
|
||||
std::vector<int> pgroups(sw->cases.size());
|
||||
if (sw->attributes.count("\\parallel_case") == 0) {
|
||||
BitPatternPool pool(sw->signal.width);
|
||||
bool extra_group_for_next_case = false;
|
||||
for (size_t i = 0; i < sw->cases.size(); i++) {
|
||||
RTLIL::CaseRule *cs2 = sw->cases[i];
|
||||
if (i != 0) {
|
||||
pgroups[i] = pgroups[i-1];
|
||||
if (extra_group_for_next_case) {
|
||||
pgroups[i] = pgroups[i-1]+1;
|
||||
extra_group_for_next_case = false;
|
||||
}
|
||||
for (auto pat : cs2->compare)
|
||||
if (!pat.is_fully_const() || !pool.has_all(pat))
|
||||
pgroups[i] = pgroups[i-1]+1;
|
||||
if (cs2->compare.empty())
|
||||
pgroups[i] = pgroups[i-1]+1;
|
||||
if (pgroups[i] != pgroups[i-1])
|
||||
pool = BitPatternPool(sw->signal.width);
|
||||
}
|
||||
for (auto pat : cs2->compare)
|
||||
if (!pat.is_fully_const())
|
||||
extra_group_for_next_case = true;
|
||||
else
|
||||
pool.take(pat);
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate in reverse order to give the first entry the top priority
|
||||
RTLIL::SigSpec initial_val = result;
|
||||
RTLIL::Cell *last_mux_cell = NULL;
|
||||
for (size_t i = 0; i < sw->cases.size(); i++) {
|
||||
int case_idx = sw->cases.size() - i - 1;
|
||||
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
|
||||
RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val);
|
||||
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
|
||||
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw);
|
||||
else
|
||||
result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc)
|
||||
{
|
||||
bool first = true;
|
||||
while (1)
|
||||
{
|
||||
RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case);
|
||||
|
||||
if (sig.width == 0)
|
||||
break;
|
||||
|
||||
if (first) {
|
||||
log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
|
||||
first = false;
|
||||
}
|
||||
|
||||
extract_core_signal(&proc->root_case, sig);
|
||||
|
||||
log(" creating decoder for signal `%s'.\n", log_signal(sig));
|
||||
|
||||
RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.width));
|
||||
mod->connections.push_back(RTLIL::SigSig(sig, value));
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcMuxPass : public Pass {
|
||||
ProcMuxPass() : Pass("proc_mux") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &proc_it : mod_it.second->processes)
|
||||
proc_mux(mod_it.second, proc_it.second);
|
||||
}
|
||||
} ProcMuxPass;
|
||||
|
87
passes/proc/proc_rmdead.cc
Normal file
87
passes/proc/proc_rmdead.cc
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/bitpattern.h"
|
||||
#include "kernel/log.h"
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <set>
|
||||
|
||||
static void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
|
||||
{
|
||||
BitPatternPool pool(sw->signal);
|
||||
|
||||
for (size_t i = 0; i < sw->cases.size(); i++)
|
||||
{
|
||||
bool is_default = sw->cases[i]->compare.size() == 0 && !pool.empty();
|
||||
|
||||
for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) {
|
||||
RTLIL::SigSpec sig = sw->cases[i]->compare[j];
|
||||
if (!sig.is_fully_const())
|
||||
continue;
|
||||
if (!pool.take(sig))
|
||||
sw->cases[i]->compare.erase(sw->cases[i]->compare.begin() + (j--));
|
||||
}
|
||||
|
||||
if (!is_default) {
|
||||
if (sw->cases[i]->compare.size() == 0) {
|
||||
delete sw->cases[i];
|
||||
sw->cases.erase(sw->cases.begin() + (i--));
|
||||
counter++;
|
||||
continue;
|
||||
}
|
||||
if (pool.empty())
|
||||
sw->cases[i]->compare.clear();
|
||||
}
|
||||
|
||||
for (auto switch_it : sw->cases[i]->switches)
|
||||
proc_rmdead(switch_it, counter);
|
||||
|
||||
if (is_default)
|
||||
pool.take_all();
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcRmdeadPass : public Pass {
|
||||
ProcRmdeadPass() : Pass("proc_rmdead") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
int total_counter = 0;
|
||||
for (auto &mod_it : design->modules)
|
||||
for (auto &proc_it : mod_it.second->processes) {
|
||||
int counter = 0;
|
||||
for (auto switch_it : proc_it.second->root_case.switches)
|
||||
proc_rmdead(switch_it, counter);
|
||||
if (counter > 0)
|
||||
log("Removed %d dead cases from process %s in module %s.\n", counter,
|
||||
proc_it.first.c_str(), mod_it.first.c_str());
|
||||
total_counter += counter;
|
||||
}
|
||||
|
||||
log("Removed a total of %d dead cases.\n", total_counter);
|
||||
}
|
||||
} ProcRmdeadPass;
|
||||
|
3
passes/submod/Makefile.inc
Normal file
3
passes/submod/Makefile.inc
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += passes/submod/submod.o
|
||||
|
273
passes/submod/submod.cc
Normal file
273
passes/submod/submod.cc
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
|
||||
struct SubmodWorker
|
||||
{
|
||||
CellTypes ct;
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
|
||||
struct SubModule
|
||||
{
|
||||
std::string name, full_name;
|
||||
std::set<RTLIL::Cell*> cells;
|
||||
};
|
||||
|
||||
std::map<std::string, SubModule> submodules;
|
||||
|
||||
struct wire_flags_t {
|
||||
RTLIL::Wire *new_wire;
|
||||
bool is_int_driven, is_int_used, is_ext_driven, is_ext_used;
|
||||
wire_flags_t() : new_wire(NULL), is_int_driven(false), is_int_used(false), is_ext_driven(false), is_ext_used(false) { }
|
||||
};
|
||||
std::map<RTLIL::Wire*, wire_flags_t> wire_flags;
|
||||
bool flag_found_something;
|
||||
|
||||
void flag_wire(RTLIL::Wire *wire, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used)
|
||||
{
|
||||
if (wire_flags.count(wire) == 0) {
|
||||
if (!create)
|
||||
return;
|
||||
wire_flags[wire] = wire_flags_t();
|
||||
}
|
||||
if (set_int_driven)
|
||||
wire_flags[wire].is_int_driven = true;
|
||||
if (set_int_used)
|
||||
wire_flags[wire].is_int_used = true;
|
||||
if (set_ext_driven)
|
||||
wire_flags[wire].is_ext_driven = true;
|
||||
if (set_ext_used)
|
||||
wire_flags[wire].is_ext_used = true;
|
||||
flag_found_something = true;
|
||||
}
|
||||
|
||||
void flag_signal(RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used)
|
||||
{
|
||||
for (auto &c : sig.chunks)
|
||||
if (c.wire != NULL)
|
||||
flag_wire(c.wire, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used);
|
||||
}
|
||||
|
||||
void handle_submodule(SubModule &submod)
|
||||
{
|
||||
log("Creating submodule %s (%s) of module %s.\n", submod.name.c_str(), submod.full_name.c_str(), module->name.c_str());
|
||||
|
||||
wire_flags.clear();
|
||||
for (RTLIL::Cell *cell : submod.cells) {
|
||||
if (ct.cell_known(cell->type)) {
|
||||
for (auto &conn : cell->connections)
|
||||
flag_signal(conn.second, true, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first), false, false);
|
||||
} else {
|
||||
log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str());
|
||||
for (auto &conn : cell->connections)
|
||||
flag_signal(conn.second, true, true, true, false, false);
|
||||
}
|
||||
}
|
||||
for (auto &it : module->cells) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (submod.cells.count(cell) > 0)
|
||||
continue;
|
||||
if (ct.cell_known(cell->type)) {
|
||||
for (auto &conn : cell->connections)
|
||||
flag_signal(conn.second, false, false, false, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first));
|
||||
} else {
|
||||
flag_found_something = false;
|
||||
for (auto &conn : cell->connections)
|
||||
flag_signal(conn.second, false, false, false, true, true);
|
||||
if (flag_found_something)
|
||||
log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Module *new_mod = new RTLIL::Module;
|
||||
new_mod->name = submod.full_name;
|
||||
design->modules[new_mod->name] = new_mod;
|
||||
int port_counter = 1, auto_name_counter = 1;
|
||||
|
||||
std::set<std::string> all_wire_names;
|
||||
for (auto &it : wire_flags) {
|
||||
all_wire_names.insert(it.first->name);
|
||||
}
|
||||
|
||||
for (auto &it : wire_flags)
|
||||
{
|
||||
RTLIL::Wire *wire = it.first;
|
||||
wire_flags_t &flags = it.second;
|
||||
|
||||
if (wire->port_input)
|
||||
flags.is_ext_driven = true;
|
||||
if (wire->port_output)
|
||||
flags.is_ext_used = true;
|
||||
|
||||
RTLIL::Wire *new_wire = new RTLIL::Wire;
|
||||
new_wire->name = wire->name;
|
||||
new_wire->width = wire->width;
|
||||
new_wire->start_offset = wire->start_offset;
|
||||
new_wire->attributes = wire->attributes;
|
||||
|
||||
if (flags.is_int_driven && flags.is_ext_used)
|
||||
new_wire->port_output = true;
|
||||
if (flags.is_ext_driven && flags.is_int_used)
|
||||
new_wire->port_input = true;
|
||||
|
||||
if (flags.is_int_driven && flags.is_ext_driven)
|
||||
new_wire->port_input = true, new_wire->port_output = true;
|
||||
|
||||
if (new_wire->port_input || new_wire->port_output) {
|
||||
new_wire->port_id = port_counter++;
|
||||
while (new_wire->name[0] == '$') {
|
||||
std::string new_wire_name = stringf("\\n%d", auto_name_counter++);
|
||||
if (all_wire_names.count(new_wire_name) == 0) {
|
||||
all_wire_names.insert(new_wire_name);
|
||||
new_wire->name = new_wire_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_wire->port_input && new_wire->port_output)
|
||||
log(" signal %s: inout %s\n", wire->name.c_str(), new_wire->name.c_str());
|
||||
else if (new_wire->port_input)
|
||||
log(" signal %s: input %s\n", wire->name.c_str(), new_wire->name.c_str());
|
||||
else if (new_wire->port_output)
|
||||
log(" signal %s: output %s\n", wire->name.c_str(), new_wire->name.c_str());
|
||||
else
|
||||
log(" signal %s: internal\n", wire->name.c_str());
|
||||
|
||||
new_mod->wires[new_wire->name] = new_wire;
|
||||
flags.new_wire = new_wire;
|
||||
}
|
||||
|
||||
for (RTLIL::Cell *cell : submod.cells) {
|
||||
RTLIL::Cell *new_cell = new RTLIL::Cell(*cell);
|
||||
for (auto &conn : new_cell->connections)
|
||||
for (auto &c : conn.second.chunks)
|
||||
if (c.wire != NULL) {
|
||||
assert(wire_flags.count(c.wire) > 0);
|
||||
c.wire = wire_flags[c.wire].new_wire;
|
||||
}
|
||||
log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str());
|
||||
new_mod->cells[new_cell->name] = new_cell;
|
||||
module->cells.erase(cell->name);
|
||||
delete cell;
|
||||
}
|
||||
submod.cells.clear();
|
||||
|
||||
RTLIL::Cell *new_cell = new RTLIL::Cell;
|
||||
new_cell->name = submod.full_name;
|
||||
new_cell->type = submod.full_name;
|
||||
for (auto &it : wire_flags)
|
||||
{
|
||||
RTLIL::Wire *old_wire = it.first;
|
||||
RTLIL::Wire *new_wire = it.second.new_wire;
|
||||
if (new_wire->port_id > 0)
|
||||
new_cell->connections[new_wire->name] = RTLIL::SigSpec(old_wire);
|
||||
}
|
||||
module->cells[new_cell->name] = new_cell;
|
||||
}
|
||||
|
||||
SubmodWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module)
|
||||
{
|
||||
if (!design->selected_whole_module(module->name))
|
||||
return;
|
||||
|
||||
if (module->processes.size() > 0) {
|
||||
log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (module->memories.size() > 0) {
|
||||
log("Skipping module %s as it contains memories (run 'memory' pass first).\n", module->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
for (auto &it : module->wires)
|
||||
it.second->attributes.erase("\\submod");
|
||||
|
||||
for (auto &it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].str.size() == 0) {
|
||||
cell->attributes.erase("\\submod");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string submod_str = cell->attributes["\\submod"].str;
|
||||
cell->attributes.erase("\\submod");
|
||||
|
||||
if (submodules.count(submod_str) == 0) {
|
||||
submodules[submod_str].name = submod_str;
|
||||
submodules[submod_str].full_name = module->name + "_" + submod_str;
|
||||
while (design->modules.count(submodules[submod_str].full_name) != 0 ||
|
||||
module->count_id(submodules[submod_str].full_name) != 0)
|
||||
submodules[submod_str].full_name += "_";
|
||||
}
|
||||
|
||||
submodules[submod_str].cells.insert(cell);
|
||||
}
|
||||
|
||||
for (auto &it : submodules)
|
||||
handle_submodule(it.second);
|
||||
}
|
||||
};
|
||||
|
||||
struct SubmodPass : public Pass {
|
||||
SubmodPass() : Pass("submod") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing SUBMOD pass (moving cells to submodes as requested).\n");
|
||||
log_push();
|
||||
|
||||
Pass::call(design, "opt_rmunused");
|
||||
log_header("Continuing SUBMOD pass.\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
std::set<std::string> handled_modules;
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
std::vector<std::string> queued_modules;
|
||||
for (auto &mod_it : design->modules)
|
||||
if (handled_modules.count(mod_it.first) == 0)
|
||||
queued_modules.push_back(mod_it.first);
|
||||
for (auto &modname : queued_modules)
|
||||
if (design->modules.count(modname) != 0) {
|
||||
SubmodWorker worker(design, design->modules[modname]);
|
||||
handled_modules.insert(modname);
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
|
||||
Pass::call(design, "opt_rmunused");
|
||||
}
|
||||
} SubmodPass;
|
||||
|
11
passes/techmap/Makefile.inc
Normal file
11
passes/techmap/Makefile.inc
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
GENFILES += passes/techmap/stdcells.inc
|
||||
OBJS += passes/techmap/techmap.o
|
||||
|
||||
passes/techmap/stdcells.inc: techlibs/stdcells.v
|
||||
od -v -td1 -w1 $< | awk 'BEGIN { print "static char stdcells_code[] = {"; } $$2 != "" { print $$2 ","; } \
|
||||
END { print 0 "};"; }' | fmt > $@.new
|
||||
mv $@.new $@
|
||||
|
||||
passes/techmap/techmap.o: passes/techmap/stdcells.inc
|
||||
|
207
passes/techmap/techmap.cc
Normal file
207
passes/techmap/techmap.cc
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "passes/techmap/stdcells.inc"
|
||||
|
||||
static void apply_prefix(std::string prefix, std::string &id)
|
||||
{
|
||||
if (id[0] == '\\')
|
||||
id = prefix + "." + id.substr(1);
|
||||
else
|
||||
id = prefix + "." + id;
|
||||
}
|
||||
|
||||
static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
|
||||
{
|
||||
for (size_t i = 0; i < sig.chunks.size(); i++) {
|
||||
if (sig.chunks[i].wire == NULL)
|
||||
continue;
|
||||
std::string wire_name = sig.chunks[i].wire->name;
|
||||
apply_prefix(prefix, wire_name);
|
||||
assert(module->wires.count(wire_name) > 0);
|
||||
sig.chunks[i].wire = module->wires[wire_name];
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
|
||||
|
||||
static bool techmap_module(RTLIL::Module *module, RTLIL::Design *map)
|
||||
{
|
||||
bool did_something = false;
|
||||
|
||||
std::vector<std::string> cell_names;
|
||||
|
||||
for (auto &cell_it : module->cells)
|
||||
cell_names.push_back(cell_it.first);
|
||||
|
||||
for (auto &cell_name : cell_names)
|
||||
{
|
||||
if (module->cells.count(cell_name) == 0)
|
||||
continue;
|
||||
|
||||
RTLIL::Cell *cell = module->cells[cell_name];
|
||||
|
||||
if (map->modules.count(cell->type) == 0)
|
||||
continue;
|
||||
|
||||
RTLIL::Module *tpl = map->modules[cell->type];
|
||||
std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(cell->type, cell->parameters);
|
||||
|
||||
if (techmap_cache.count(key) > 0) {
|
||||
tpl = techmap_cache[key];
|
||||
} else {
|
||||
std::string derived_name = cell->type;
|
||||
if (cell->parameters.size() != 0) {
|
||||
derived_name = tpl->derive(map, cell->parameters);
|
||||
tpl = map->modules[derived_name];
|
||||
log_header("Continuing TECHMAP pass.\n");
|
||||
}
|
||||
for (auto &cit : tpl->cells)
|
||||
if (cit.second->type == "\\TECHMAP_FAILED") {
|
||||
log("Not using module `%s' from techmap as it contains a TECHMAP_FAILED marker cell.\n", derived_name.c_str());
|
||||
tpl = NULL;
|
||||
break;
|
||||
}
|
||||
techmap_cache[key] = tpl;
|
||||
}
|
||||
|
||||
if (tpl == NULL)
|
||||
goto next_cell;
|
||||
|
||||
log("Mapping `%s.%s' using `%s'.\n", module->name.c_str(), cell_name.c_str(), tpl->name.c_str());
|
||||
|
||||
if (tpl->memories.size() != 0)
|
||||
log_error("Technology map yielded memories -> this is not supported.");
|
||||
|
||||
if (tpl->processes.size() != 0)
|
||||
log_error("Technology map yielded processes -> this is not supported.");
|
||||
|
||||
for (auto &it : tpl->wires) {
|
||||
RTLIL::Wire *w = new RTLIL::Wire(*it.second);
|
||||
apply_prefix(cell_name, w->name);
|
||||
w->port_input = false;
|
||||
w->port_output = false;
|
||||
w->port_id = 0;
|
||||
module->wires[w->name] = w;
|
||||
}
|
||||
|
||||
for (auto &it : tpl->cells) {
|
||||
RTLIL::Cell *c = new RTLIL::Cell(*it.second);
|
||||
if (c->type.substr(0, 2) == "\\$")
|
||||
c->type = c->type.substr(1);
|
||||
apply_prefix(cell_name, c->name);
|
||||
for (auto &it2 : c->connections)
|
||||
apply_prefix(cell_name, it2.second, module);
|
||||
module->cells[c->name] = c;
|
||||
}
|
||||
|
||||
for (auto &it : tpl->connections) {
|
||||
RTLIL::SigSig c = it;
|
||||
apply_prefix(cell_name, c.first, module);
|
||||
apply_prefix(cell_name, c.second, module);
|
||||
module->connections.push_back(c);
|
||||
}
|
||||
|
||||
for (auto &it : cell->connections) {
|
||||
assert(tpl->wires.count(it.first));
|
||||
assert(tpl->wires[it.first]->port_id > 0);
|
||||
RTLIL::Wire *w = tpl->wires[it.first];
|
||||
RTLIL::SigSig c;
|
||||
if (w->port_output) {
|
||||
c.first = it.second;
|
||||
c.second = RTLIL::SigSpec(w);
|
||||
apply_prefix(cell_name, c.second, module);
|
||||
} else {
|
||||
c.first = RTLIL::SigSpec(w);
|
||||
c.second = it.second;
|
||||
apply_prefix(cell_name, c.first, module);
|
||||
}
|
||||
if (c.second.width > c.first.width)
|
||||
c.second.remove(c.first.width, c.second.width - c.first.width);
|
||||
if (c.second.width < c.first.width)
|
||||
c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width));
|
||||
assert(c.first.width == c.second.width);
|
||||
module->connections.push_back(c);
|
||||
}
|
||||
|
||||
delete cell;
|
||||
module->cells.erase(cell_name);
|
||||
did_something = true;
|
||||
next_cell:;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
struct TechmapPass : public Pass {
|
||||
TechmapPass() : Pass("techmap") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing TECHMAP pass (map to technology primitives).\n");
|
||||
log_push();
|
||||
|
||||
std::string filename;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-map" && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
RTLIL::Design *map = new RTLIL::Design;
|
||||
FILE *f = filename.empty() ? fmemopen(stdcells_code, strlen(stdcells_code), "rt") : fopen(filename.c_str(), "rt");
|
||||
if (f == NULL)
|
||||
log_error("Can't open map file `%s'\n", filename.c_str());
|
||||
Frontend::frontend_call(map, f, filename.empty() ? "<stdcells.v>" : filename, "verilog");
|
||||
fclose(f);
|
||||
|
||||
std::map<RTLIL::IdString, RTLIL::Module*> modules_new;
|
||||
for (auto &it : map->modules) {
|
||||
if (it.first.substr(0, 2) == "\\$")
|
||||
it.second->name = it.first.substr(1);
|
||||
modules_new[it.second->name] = it.second;
|
||||
}
|
||||
map->modules.swap(modules_new);
|
||||
|
||||
bool did_something = true;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
for (auto &mod_it : design->modules)
|
||||
if (techmap_module(mod_it.second, map))
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
log("No more expansions possible.\n");
|
||||
techmap_cache.clear();
|
||||
delete map;
|
||||
log_pop();
|
||||
}
|
||||
} TechmapPass;
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue