mirror of https://github.com/YosysHQ/yosys.git
initial import
This commit is contained in:
commit
7764d0ba1d
|
@ -0,0 +1,76 @@
|
|||
|
||||
CONFIG := clang-debug
|
||||
# CONFIG := gcc-debug
|
||||
# CONFIG := release
|
||||
|
||||
OBJS = kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/sha1.o kernel/calc.o kernel/select.o kernel/show.o
|
||||
OBJS += bigint/BigIntegerAlgorithms.o bigint/BigInteger.o bigint/BigIntegerUtils.o bigint/BigUnsigned.o bigint/BigUnsignedInABase.o
|
||||
|
||||
GENFILES =
|
||||
TARGETS = yosys
|
||||
|
||||
all: top-all
|
||||
|
||||
CXXFLAGS = -Wall -Wextra -ggdb -I$(shell pwd) -MD
|
||||
LDFLAGS =
|
||||
LDLIBS = -lstdc++ -lreadline -lm
|
||||
|
||||
-include Makefile.conf
|
||||
|
||||
ifeq ($(CONFIG),clang-debug)
|
||||
CXX = clang
|
||||
CXXFLAGS += -std=c++11 -O0
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG),gcc-debug)
|
||||
CXX = gcc
|
||||
CXXFLAGS += -std=gnu++0x -O0
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG),release)
|
||||
CXX = gcc
|
||||
CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
|
||||
endif
|
||||
|
||||
include frontends/*/Makefile.inc
|
||||
include passes/*/Makefile.inc
|
||||
include backends/*/Makefile.inc
|
||||
include techlibs/Makefile.inc
|
||||
|
||||
top-all: $(TARGETS)
|
||||
|
||||
yosys: $(OBJS)
|
||||
$(CXX) -o yosys $(LDFLAGS) $(OBJS) $(LDLIBS)
|
||||
|
||||
test: yosys
|
||||
cd tests/simple && bash run-test.sh
|
||||
cd tests/hana && bash run-test.sh
|
||||
cd tests/asicworld && bash run-test.sh
|
||||
|
||||
help:
|
||||
@find -name '*.cc' | xargs egrep -h '(Pass|Frontend|Backend)\(".*"\)' | \
|
||||
sed 's,.*: ,,; s, .*,,;' | sort | tr '\n' '\t' | expand -t25 | fmt
|
||||
|
||||
install: yosys
|
||||
install yosys /usr/local/bin/yosys
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS)
|
||||
rm -f bigint/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d
|
||||
|
||||
mrproper: clean
|
||||
svn st --no-ignore | grep '^[?I]' | cut -c8- | sed 's,^ *,,; /^Makefile.conf$$/ d;' | xargs -r -d '\n' rm -vrf
|
||||
|
||||
qtcreator:
|
||||
{ for file in $(basename $(OBJS)); do \
|
||||
for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \
|
||||
done; find backends bigint frontends kernel passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files
|
||||
{ echo .; find backends bigint frontends kernel passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes
|
||||
touch qtcreator.config qtcreator.creator
|
||||
|
||||
-include bigint/*.d
|
||||
-include frontends/*/*.d
|
||||
-include passes/*/*.d
|
||||
-include backends/*/*.d
|
||||
-include kernel/*.d
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
yosys -- Yosys Open SYnthesis Suite
|
||||
===================================
|
||||
|
||||
This is a framework for RTL synthesis tools. It is highly
|
||||
experimental and under construction. The goal for now is
|
||||
to implement an extensible Verilog-2005 synthesis tool.
|
||||
|
||||
The aim of this tool is to generate valid logic netlists
|
||||
from HDL designs in a manner that allows for easy addition
|
||||
of extra synthesis passes. This tool does not aim at generating
|
||||
efficient logic netlists. This can be done by passing the
|
||||
output of Yosys to a low-level synthesis tool such as ABC.
|
||||
|
||||
Yosys is free software licensed under the ISC license (a GPL
|
||||
compatible licence that is similar in terms to the MIT license
|
||||
or the 2-clause BSD license).
|
||||
|
||||
|
||||
Unsupported Verilog-2005 Features
|
||||
=================================
|
||||
|
||||
The following Verilog-2005 features are not supported by
|
||||
yosys and there are currently no plans to add support
|
||||
for them:
|
||||
|
||||
- Non-sythesizable language features as defined in
|
||||
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
|
||||
|
||||
- The "tri", "triand", "trior", "wand" and "wor" net types
|
||||
|
||||
- The "library" and "configuration" source file formats
|
||||
|
||||
- The "disable" and "primitive" statements
|
||||
|
||||
- Latched logic (is synthesized as logic with feedback loops)
|
||||
|
||||
|
||||
Verilog Attributes and non-standard features
|
||||
============================================
|
||||
|
||||
- The 'full_case' attribute on case statements is supported
|
||||
(also the non-standard "// synopsys full_case" directive)
|
||||
|
||||
- The "// synopsys translate_off" and "// synopsys translate_on"
|
||||
directives are also supported (but the use of `ifdef .. `endif
|
||||
is strongly recommended instead).
|
||||
|
||||
- The "nomem2reg" attribute on modules or arrays prohibits the
|
||||
automatic early conversion of arrays to seperate registers.
|
||||
|
||||
- The "nolatches" attribute on modules or always-blocks
|
||||
prohibits the generation of logic-loops for latches. Instead
|
||||
all not explicitly assigned values default to x-bits.
|
||||
|
||||
- In addition to the (* ... *) attribute syntax, yosys supports
|
||||
the non-standard {* ... *} attribute syntax to set default attributes
|
||||
for everything that comes after the {* ... *} statement. (Reset
|
||||
by adding an empty {* *} statement.) The preprocessor define
|
||||
__YOSYS_ENABLE_DEFATTR__ must be set in order for this featre to be active.
|
||||
|
||||
|
||||
TODOs / Open Bugs
|
||||
=================
|
||||
|
||||
- Write "design and implementation of.." document
|
||||
|
||||
- Add brief sourcecode documentation to:
|
||||
|
||||
- Most passes and kernel functionalities
|
||||
|
||||
- Implement missing Verilog 2005 features:
|
||||
|
||||
- Signed constants
|
||||
- ROM modelling using "initial" blocks
|
||||
- Builtin primitive gates (and, nand, cmos, nmos, pmos, etc..)
|
||||
- Ignore what needs to be ignored (e.g. drive and charge strenghts)
|
||||
- Check standard vs. implementation to identify missing features
|
||||
|
||||
- Actually use range information on parameters
|
||||
|
||||
- Implement mux-to-tribuf pass and rebalance mixed mux/tribuf trees
|
||||
|
||||
- TCL and Python interfaces to frontends, passes, backends and RTLIL
|
||||
|
||||
- Additional internal cell types: $bitcount, $pla, $lut and $pmux
|
||||
|
||||
- Subsystem for selecting stuff (and limiting scope of passes)
|
||||
|
||||
- Support for registering designs (as collection of modules) to CellTypes
|
||||
|
||||
- Kernel support for collections of cells (from input/output cones, etc)
|
||||
|
||||
- Smarter resource sharing pass (add MUXes and get rid of duplicated cells)
|
||||
|
||||
- FSM state encoding and technology mapping
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/autotest/autotest.o
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#define NUM_ITER 1000
|
||||
|
||||
static std::string id(std::string internal_id)
|
||||
{
|
||||
const char *str = internal_id.c_str();
|
||||
bool do_escape = false;
|
||||
|
||||
if (*str == '\\')
|
||||
str++;
|
||||
|
||||
if ('0' <= *str && *str <= '9')
|
||||
do_escape = true;
|
||||
|
||||
for (int i = 0; str[i]; i++) {
|
||||
if ('0' <= str[i] && str[i] <= '9')
|
||||
continue;
|
||||
if ('a' <= str[i] && str[i] <= 'z')
|
||||
continue;
|
||||
if ('A' <= str[i] && str[i] <= 'Z')
|
||||
continue;
|
||||
if (str[i] == '_')
|
||||
continue;
|
||||
do_escape = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_escape)
|
||||
return "\\" + std::string(str) + " ";
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
static std::string idx(std::string str)
|
||||
{
|
||||
if (str[0] == '\\')
|
||||
return str.substr(1);
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string())
|
||||
{
|
||||
str1 = idx(str1);
|
||||
if (!str2.empty())
|
||||
str1 += "_" + idx(str2);
|
||||
if (!str3.empty())
|
||||
str1 += "_" + idx(str3);
|
||||
return id(str1);
|
||||
}
|
||||
|
||||
static void autotest(FILE *f, RTLIL::Design *design)
|
||||
{
|
||||
fprintf(f, "module testbench;\n\n");
|
||||
|
||||
fprintf(f, "integer i;\n\n");
|
||||
|
||||
fprintf(f, "reg [31:0] xorshift128_x = 123456789;\n");
|
||||
fprintf(f, "reg [31:0] xorshift128_y = 362436069;\n");
|
||||
fprintf(f, "reg [31:0] xorshift128_z = 521288629;\n");
|
||||
fprintf(f, "reg [31:0] xorshift128_w = 88675123;\n");
|
||||
fprintf(f, "reg [31:0] xorshift128_t;\n\n");
|
||||
fprintf(f, "task xorshift128;\n");
|
||||
fprintf(f, "begin\n");
|
||||
fprintf(f, "\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
|
||||
fprintf(f, "\txorshift128_x = xorshift128_y;\n");
|
||||
fprintf(f, "\txorshift128_y = xorshift128_z;\n");
|
||||
fprintf(f, "\txorshift128_z = xorshift128_w;\n");
|
||||
fprintf(f, "\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
for (auto it = design->modules.begin(); it != design->modules.end(); it++)
|
||||
{
|
||||
std::map<std::string, int> signal_in;
|
||||
std::map<std::string, std::string> signal_const;
|
||||
std::map<std::string, int> signal_clk;
|
||||
std::map<std::string, int> signal_out;
|
||||
|
||||
RTLIL::Module *mod = it->second;
|
||||
int count_ports = 0;
|
||||
log("Generating test bench for module `%s'.\n", it->first.c_str());
|
||||
for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) {
|
||||
RTLIL::Wire *wire = it2->second;
|
||||
if (wire->port_output) {
|
||||
count_ports++;
|
||||
signal_out[idy("sig", mod->name, wire->name)] = wire->width;
|
||||
fprintf(f, "wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str());
|
||||
} else if (wire->port_input) {
|
||||
count_ports++;
|
||||
bool is_clksignal = wire->attributes.count("\\gentb_clock") > 0;
|
||||
for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); it3++)
|
||||
for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); it4++) {
|
||||
if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
|
||||
continue;
|
||||
RTLIL::SigSpec &signal = (*it4)->signal;
|
||||
for (size_t i = 0; i < signal.chunks.size(); i++) {
|
||||
if (signal.chunks[i].wire == wire)
|
||||
is_clksignal = true;
|
||||
}
|
||||
}
|
||||
if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) {
|
||||
signal_clk[idy("sig", mod->name, wire->name)] = wire->width;
|
||||
} else {
|
||||
signal_in[idy("sig", mod->name, wire->name)] = wire->width;
|
||||
if (wire->attributes.count("\\gentb_constant") > 0)
|
||||
signal_const[idy("sig", mod->name, wire->name)] = wire->attributes["\\gentb_constant"].as_string();
|
||||
}
|
||||
fprintf(f, "reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str());
|
||||
}
|
||||
}
|
||||
fprintf(f, "%s %s(\n", id(mod->name).c_str(), idy("uut", mod->name).c_str());
|
||||
for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) {
|
||||
RTLIL::Wire *wire = it2->second;
|
||||
if (wire->port_output || wire->port_input)
|
||||
fprintf(f, "\t.%s(%s)%s\n", id(wire->name).c_str(),
|
||||
idy("sig", mod->name, wire->name).c_str(), --count_ports ? "," : "");
|
||||
}
|
||||
fprintf(f, ");\n\n");
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "reset").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
int delay_counter = 0;
|
||||
for (auto it = signal_in.begin(); it != signal_in.end(); it++)
|
||||
fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
|
||||
fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
|
||||
fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str());
|
||||
fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str());
|
||||
}
|
||||
delay_counter = 0;
|
||||
for (auto it = signal_in.begin(); it != signal_in.end(); it++)
|
||||
fprintf(f, "\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
|
||||
fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str());
|
||||
fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str());
|
||||
}
|
||||
delay_counter = 0;
|
||||
for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
|
||||
if (signal_const.count(it->first) == 0)
|
||||
continue;
|
||||
fprintf(f, "\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
|
||||
}
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "update_data").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
delay_counter = 0;
|
||||
for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
|
||||
if (signal_const.count(it->first) > 0)
|
||||
continue;
|
||||
fprintf(f, "\txorshift128;\n");
|
||||
fprintf(f, "\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
|
||||
}
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "update_clock").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
if (signal_clk.size()) {
|
||||
fprintf(f, "\txorshift128;\n");
|
||||
fprintf(f, "\t{");
|
||||
int total_clock_bits = 0;
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
|
||||
fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
|
||||
total_clock_bits += it->second;
|
||||
}
|
||||
fprintf(f, " } = {");
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
|
||||
fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
|
||||
fprintf(f, " } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits);
|
||||
}
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
char shorthand = 'A';
|
||||
std::vector<std::string> header1;
|
||||
std::string header2 = "";
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "print_status").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
fprintf(f, "\t$display(\"%%b %%b %%b %%t %%d\", {");
|
||||
if (signal_in.size())
|
||||
for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
|
||||
fprintf(f, "%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
|
||||
int len = it->second;
|
||||
if (len > 1)
|
||||
header2 += "/", len--;
|
||||
while (len > 1)
|
||||
header2 += "-", len--;
|
||||
if (len > 0)
|
||||
header2 += shorthand, len--;
|
||||
header1.push_back(" " + it->first);
|
||||
header1.back()[0] = shorthand++;
|
||||
}
|
||||
else {
|
||||
fprintf(f, " 1'bx");
|
||||
header2 += "#";
|
||||
}
|
||||
fprintf(f, " }, {");
|
||||
header2 += " ";
|
||||
if (signal_clk.size()) {
|
||||
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
|
||||
fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
|
||||
int len = it->second;
|
||||
if (len > 1)
|
||||
header2 += "/", len--;
|
||||
while (len > 1)
|
||||
header2 += "-", len--;
|
||||
if (len > 0)
|
||||
header2 += shorthand, len--;
|
||||
header1.push_back(" " + it->first);
|
||||
header1.back()[0] = shorthand++;
|
||||
}
|
||||
} else {
|
||||
fprintf(f, " 1'bx");
|
||||
header2 += "#";
|
||||
}
|
||||
fprintf(f, " }, {");
|
||||
header2 += " ";
|
||||
if (signal_out.size()) {
|
||||
for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
|
||||
fprintf(f, "%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
|
||||
int len = it->second;
|
||||
if (len > 1)
|
||||
header2 += "/", len--;
|
||||
while (len > 1)
|
||||
header2 += "-", len--;
|
||||
if (len > 0)
|
||||
header2 += shorthand, len--;
|
||||
header1.push_back(" " + it->first);
|
||||
header1.back()[0] = shorthand++;
|
||||
}
|
||||
} else {
|
||||
fprintf(f, " 1'bx");
|
||||
header2 += "#";
|
||||
}
|
||||
fprintf(f, " }, $time, i);\n");
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "print_header").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
fprintf(f, "\t$display();\n");
|
||||
for (auto &hdr : header1)
|
||||
fprintf(f, "\t$display(\" %s\");\n", hdr.c_str());
|
||||
fprintf(f, "\t$display();\n");
|
||||
fprintf(f, "\t$display(\"%s\");\n", header2.c_str());
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
|
||||
fprintf(f, "task %s;\n", idy(mod->name, "test").c_str());
|
||||
fprintf(f, "begin\n");
|
||||
fprintf(f, "\t$display(\"\\n==== %s ====\");\n", idy(mod->name).c_str());
|
||||
fprintf(f, "\t%s;\n", idy(mod->name, "reset").c_str());
|
||||
fprintf(f, "\tfor (i=0; i<%d; i=i+1) begin\n", NUM_ITER);
|
||||
fprintf(f, "\t\tif (i %% 20 == 0) %s;\n", idy(mod->name, "print_header").c_str());
|
||||
fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_data").c_str());
|
||||
fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_clock").c_str());
|
||||
fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "print_status").c_str());
|
||||
fprintf(f, "\tend\n");
|
||||
fprintf(f, "end\n");
|
||||
fprintf(f, "endtask\n\n");
|
||||
}
|
||||
|
||||
fprintf(f, "initial begin\n");
|
||||
fprintf(f, "\t// $dumpfile(\"testbench.vcd\");\n");
|
||||
fprintf(f, "\t// $dumpvars(0, testbench);\n");
|
||||
for (auto it = design->modules.begin(); it != design->modules.end(); it++)
|
||||
fprintf(f, "\t%s;\n", idy(it->first, "test").c_str());
|
||||
fprintf(f, "\t$finish;\n");
|
||||
fprintf(f, "end\n\n");
|
||||
|
||||
fprintf(f, "endmodule\n");
|
||||
}
|
||||
|
||||
struct AutotestBackend : public Backend {
|
||||
AutotestBackend() : Backend("autotest") { }
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing AUTOTEST backend (auto-generate pseudo-random test benches).\n");
|
||||
extra_args(f, filename, args, 1);
|
||||
autotest(f, design);
|
||||
}
|
||||
} AutotestBackend;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/ilang/ilang_backend.o
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward backend for the RTLIL text
|
||||
* representation (as understood by the 'ilang' frontend).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ilang_backend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace ILANG_BACKEND;
|
||||
|
||||
void ILANG_BACKEND::dump_const(FILE *f, const RTLIL::Const &data, int width, int offset, bool autoint)
|
||||
{
|
||||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
if (data.str.empty() || width != (int)data.bits.size()) {
|
||||
if (width == 32 && autoint) {
|
||||
int32_t val = 0;
|
||||
for (int i = 0; i < width; i++) {
|
||||
assert(offset+i < (int)data.bits.size());
|
||||
switch (data.bits[offset+i]) {
|
||||
case RTLIL::S0: break;
|
||||
case RTLIL::S1: val |= 1 << i; break;
|
||||
default: val = -1; break;
|
||||
}
|
||||
}
|
||||
if (val >= 0) {
|
||||
fprintf(f, "%d", val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(f, "%d'", width);
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
assert(i < (int)data.bits.size());
|
||||
switch (data.bits[i]) {
|
||||
case RTLIL::S0: fprintf(f, "0"); break;
|
||||
case RTLIL::S1: fprintf(f, "1"); break;
|
||||
case RTLIL::Sx: fprintf(f, "x"); break;
|
||||
case RTLIL::Sz: fprintf(f, "z"); break;
|
||||
case RTLIL::Sa: fprintf(f, "-"); break;
|
||||
case RTLIL::Sm: fprintf(f, "m"); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(f, "\"");
|
||||
for (size_t i = 0; i < data.str.size(); i++) {
|
||||
if (data.str[i] == '\n')
|
||||
fprintf(f, "\\n");
|
||||
else if (data.str[i] == '\t')
|
||||
fprintf(f, "\\t");
|
||||
else if (data.str[i] < 32)
|
||||
fprintf(f, "\\%03o", data.str[i]);
|
||||
else if (data.str[i] == '"')
|
||||
fprintf(f, "\\\"");
|
||||
else
|
||||
fputc(data.str[i], f);
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint)
|
||||
{
|
||||
if (chunk.wire == NULL) {
|
||||
dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
|
||||
} else {
|
||||
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
||||
fprintf(f, "%s", chunk.wire->name.c_str());
|
||||
else if (chunk.width == 1)
|
||||
fprintf(f, "%s [%d]", chunk.wire->name.c_str(), chunk.offset);
|
||||
else
|
||||
fprintf(f, "%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint)
|
||||
{
|
||||
if (sig.chunks.size() == 1) {
|
||||
dump_sigchunk(f, sig.chunks[0], autoint);
|
||||
} else {
|
||||
fprintf(f, "{ ");
|
||||
for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) {
|
||||
dump_sigchunk(f, *it, false);
|
||||
fprintf(f, " ");
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire)
|
||||
{
|
||||
for (auto it = wire->attributes.begin(); it != wire->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "%s" "wire ", indent.c_str());
|
||||
if (wire->auto_width)
|
||||
fprintf(f, "auto ");
|
||||
if (wire->width != 1)
|
||||
fprintf(f, "width %d ", wire->width);
|
||||
if (wire->start_offset != 0)
|
||||
fprintf(f, "offset %d ", wire->start_offset);
|
||||
if (wire->port_input && !wire->port_output)
|
||||
fprintf(f, "input %d ", wire->port_id);
|
||||
if (!wire->port_input && wire->port_output)
|
||||
fprintf(f, "output %d ", wire->port_id);
|
||||
if (wire->port_input && wire->port_output)
|
||||
fprintf(f, "inout %d ", wire->port_id);
|
||||
fprintf(f, "%s\n", wire->name.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory)
|
||||
{
|
||||
for (auto it = memory->attributes.begin(); it != memory->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "%s" "memory ", indent.c_str());
|
||||
if (memory->width != 1)
|
||||
fprintf(f, "width %d ", memory->width);
|
||||
if (memory->size != 0)
|
||||
fprintf(f, "size %d ", memory->size);
|
||||
fprintf(f, "%s\n", memory->name.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell)
|
||||
{
|
||||
for (auto it = cell->attributes.begin(); it != cell->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
|
||||
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) {
|
||||
fprintf(f, "%s parameter %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
|
||||
fprintf(f, "%s connect %s ", indent.c_str(), it->first.c_str());
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs)
|
||||
{
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
|
||||
{
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
fprintf(f, " ");
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
|
||||
dump_proc_switch(f, indent, *it);
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw)
|
||||
{
|
||||
for (auto it = sw->attributes.begin(); it != sw->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "switch ", indent.c_str());
|
||||
dump_sigspec(f, sw->signal);
|
||||
fprintf(f, "\n");
|
||||
|
||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); it++)
|
||||
{
|
||||
fprintf(f, "%s case ", indent.c_str());
|
||||
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
||||
if (i > 0)
|
||||
fprintf(f, ", ");
|
||||
dump_sigspec(f, (*it)->compare[i]);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
dump_proc_case_body(f, indent + " ", *it);
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy)
|
||||
{
|
||||
fprintf(f, "%s" "sync ", indent.c_str());
|
||||
switch (sy->type) {
|
||||
if (0) case RTLIL::ST0: fprintf(f, "low ");
|
||||
if (0) case RTLIL::ST1: fprintf(f, "high ");
|
||||
if (0) case RTLIL::STp: fprintf(f, "posedge ");
|
||||
if (0) case RTLIL::STn: fprintf(f, "negedge ");
|
||||
if (0) case RTLIL::STe: fprintf(f, "edge ");
|
||||
dump_sigspec(f, sy->signal);
|
||||
fprintf(f, "\n");
|
||||
break;
|
||||
case RTLIL::STa: fprintf(f, "always\n"); break;
|
||||
}
|
||||
|
||||
for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) {
|
||||
fprintf(f, "%s update ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
fprintf(f, " ");
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc)
|
||||
{
|
||||
for (auto it = proc->attributes.begin(); it != proc->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "%s" "process %s\n", indent.c_str(), proc->name.c_str());
|
||||
dump_proc_case_body(f, indent + " ", &proc->root_case);
|
||||
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++)
|
||||
dump_proc_sync(f, indent + " ", *it);
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
||||
{
|
||||
fprintf(f, "%s" "connect ", indent.c_str());
|
||||
dump_sigspec(f, left);
|
||||
fprintf(f, " ");
|
||||
dump_sigspec(f, right);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_module(FILE *f, std::string indent, const RTLIL::Module *module)
|
||||
{
|
||||
for (auto it = module->attributes.begin(); it != module->attributes.end(); it++) {
|
||||
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "module %s\n", indent.c_str(), module->name.c_str());
|
||||
|
||||
for (auto it = module->wires.begin(); it != module->wires.end(); it++)
|
||||
dump_wire(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->memories.begin(); it != module->memories.end(); it++)
|
||||
dump_memory(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->cells.begin(); it != module->cells.end(); it++)
|
||||
dump_cell(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->processes.begin(); it != module->processes.end(); it++)
|
||||
dump_proc(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->connections.begin(); it != module->connections.end(); it++)
|
||||
dump_conn(f, indent + " ", it->first, it->second);
|
||||
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_design(FILE *f, const RTLIL::Design *design)
|
||||
{
|
||||
for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
|
||||
if (it != design->modules.begin())
|
||||
fprintf(f, "\n");
|
||||
dump_module(f, "", it->second);
|
||||
}
|
||||
}
|
||||
|
||||
struct IlangBackend : public Backend {
|
||||
IlangBackend() : Backend("ilang") { }
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) {
|
||||
log_header("Executing ILANG backend.\n");
|
||||
extra_args(f, filename, args, 1);
|
||||
log("Output filename: %s\n", filename.c_str());
|
||||
ILANG_BACKEND::dump_design(f, design);
|
||||
}
|
||||
} IlangBackend;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward backend for the RTLIL text
|
||||
* representation (as understood by the 'ilang' frontend).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ILANG_BACKEND_H
|
||||
#define ILANG_BACKEND_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ILANG_BACKEND {
|
||||
void dump_const(FILE *f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
|
||||
void dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint = true);
|
||||
void dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint = true);
|
||||
void dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire);
|
||||
void dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory);
|
||||
void dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell);
|
||||
void dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs);
|
||||
void dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw);
|
||||
void dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy);
|
||||
void dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc);
|
||||
void dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right);
|
||||
void dump_module(FILE *f, std::string indent, const RTLIL::Module *module);
|
||||
void dump_design(FILE *f, const RTLIL::Design *design);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/verilog/verilog_backend.o
|
||||
|
|
@ -0,0 +1,905 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A simple and straightforward verilog backend.
|
||||
*
|
||||
* Note that RTLIL processes can't always be mapped easily to a Verilog
|
||||
* process. Therefore this frontend should only be used to export a
|
||||
* Verilog netlist (i.e. after the "proc" pass has converted all processes
|
||||
* to logic networks and registers).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "verilog_backend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace {
|
||||
|
||||
bool norename, noattr, attr2comment, noexpr;
|
||||
int auto_name_counter, auto_name_offset, auto_name_digits;
|
||||
std::map<std::string, int> auto_name_map;
|
||||
|
||||
std::set<std::string> reg_wires;
|
||||
|
||||
CellTypes reg_ct;
|
||||
RTLIL::Module *active_module;
|
||||
|
||||
void reset_auto_counter_id(const std::string &id, bool may_rename)
|
||||
{
|
||||
const char *str = id.c_str();
|
||||
|
||||
if (*str == '$' && may_rename && !norename)
|
||||
auto_name_map[id] = auto_name_counter++;
|
||||
|
||||
if (str[0] != '_' && str[1] != 0)
|
||||
return;
|
||||
for (int i = 0; str[i] != 0; i++) {
|
||||
if (str[i] == '_')
|
||||
continue;
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return;
|
||||
}
|
||||
|
||||
int num = atoi(str+1);
|
||||
if (num >= auto_name_offset)
|
||||
auto_name_offset = num + 1;
|
||||
}
|
||||
|
||||
void reset_auto_counter(RTLIL::Module *module)
|
||||
{
|
||||
auto_name_map.clear();
|
||||
auto_name_counter = 0;
|
||||
auto_name_offset = 0;
|
||||
|
||||
reset_auto_counter_id(module->name, false);
|
||||
|
||||
for (auto it = module->wires.begin(); it != module->wires.end(); it++)
|
||||
reset_auto_counter_id(it->second->name, true);
|
||||
|
||||
for (auto it = module->cells.begin(); it != module->cells.end(); it++) {
|
||||
reset_auto_counter_id(it->second->name, true);
|
||||
reset_auto_counter_id(it->second->type, false);
|
||||
}
|
||||
|
||||
for (auto it = module->processes.begin(); it != module->processes.end(); it++)
|
||||
reset_auto_counter_id(it->second->name, false);
|
||||
|
||||
auto_name_digits = 1;
|
||||
for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
|
||||
auto_name_digits++;
|
||||
|
||||
for (auto it = auto_name_map.begin(); it != auto_name_map.end(); it++)
|
||||
log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second);
|
||||
}
|
||||
|
||||
std::string id(std::string internal_id, bool may_rename = true)
|
||||
{
|
||||
const char *str = internal_id.c_str();
|
||||
bool do_escape = false;
|
||||
|
||||
if (may_rename && auto_name_map.count(internal_id) != 0) {
|
||||
char buffer[100];
|
||||
snprintf(buffer, 100, "_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
if (*str == '\\')
|
||||
str++;
|
||||
|
||||
if ('0' <= *str && *str <= '9')
|
||||
do_escape = true;
|
||||
|
||||
for (int i = 0; str[i]; i++)
|
||||
{
|
||||
if ('0' <= str[i] && str[i] <= '9')
|
||||
continue;
|
||||
if ('a' <= str[i] && str[i] <= 'z')
|
||||
continue;
|
||||
if ('A' <= str[i] && str[i] <= 'Z')
|
||||
continue;
|
||||
if (str[i] == '_')
|
||||
continue;
|
||||
do_escape = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_escape)
|
||||
return "\\" + std::string(str) + " ";
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name)
|
||||
{
|
||||
sig.optimize();
|
||||
if (sig.chunks.size() != 1 || sig.chunks[0].wire == NULL)
|
||||
return false;
|
||||
if (reg_wires.count(sig.chunks[0].wire->name) == 0)
|
||||
return false;
|
||||
reg_name = id(sig.chunks[0].wire->name);
|
||||
if (sig.width != sig.chunks[0].wire->width)
|
||||
if (sig.width == 1)
|
||||
reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset);
|
||||
else
|
||||
reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset + sig.chunks[0].width - 1,
|
||||
sig.chunks[0].wire->start_offset + sig.chunks[0].offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dump_const(FILE *f, RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false)
|
||||
{
|
||||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
if (data.str.empty() || width != (int)data.bits.size()) {
|
||||
if (width == 32 && !no_decimal) {
|
||||
uint32_t val = 0;
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
assert(i < (int)data.bits.size());
|
||||
if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
|
||||
goto dump_bits;
|
||||
if (data.bits[i] == RTLIL::S1)
|
||||
val |= 1 << (i - offset);
|
||||
}
|
||||
fprintf(f, "%d", (int)val);
|
||||
} else {
|
||||
dump_bits:
|
||||
fprintf(f, "%d'b", width);
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
assert(i < (int)data.bits.size());
|
||||
switch (data.bits[i]) {
|
||||
case RTLIL::S0: fprintf(f, "0"); break;
|
||||
case RTLIL::S1: fprintf(f, "1"); break;
|
||||
case RTLIL::Sx: fprintf(f, "x"); break;
|
||||
case RTLIL::Sz: fprintf(f, "z"); break;
|
||||
case RTLIL::Sa: fprintf(f, "z"); break;
|
||||
case RTLIL::Sm: log_error("Found marker state in final netlist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(f, "\"");
|
||||
for (size_t i = 0; i < data.str.size(); i++) {
|
||||
if (data.str[i] == '\n')
|
||||
fprintf(f, "\\n");
|
||||
else if (data.str[i] == '\t')
|
||||
fprintf(f, "\\t");
|
||||
else if (data.str[i] < 32)
|
||||
fprintf(f, "\\%03o", data.str[i]);
|
||||
else if (data.str[i] == '"')
|
||||
fprintf(f, "\\\"");
|
||||
else
|
||||
fputc(data.str[i], f);
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
}
|
||||
}
|
||||
|
||||
void dump_sigchunk(FILE *f, RTLIL::SigChunk &chunk, bool no_decimal = false)
|
||||
{
|
||||
if (chunk.wire == NULL) {
|
||||
dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
|
||||
} else {
|
||||
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
||||
fprintf(f, "%s", id(chunk.wire->name).c_str());
|
||||
else if (chunk.width == 1)
|
||||
fprintf(f, "%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
|
||||
else
|
||||
fprintf(f, "%s[%d:%d]", id(chunk.wire->name).c_str(),
|
||||
chunk.offset + chunk.wire->start_offset + chunk.width - 1,
|
||||
chunk.offset + chunk.wire->start_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_sigspec(FILE *f, RTLIL::SigSpec &sig)
|
||||
{
|
||||
if (sig.chunks.size() == 1) {
|
||||
dump_sigchunk(f, sig.chunks[0]);
|
||||
} else {
|
||||
fprintf(f, "{ ");
|
||||
for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) {
|
||||
if (it != sig.chunks.rbegin())
|
||||
fprintf(f, ", ");
|
||||
dump_sigchunk(f, *it, true);
|
||||
}
|
||||
fprintf(f, " }");
|
||||
}
|
||||
}
|
||||
|
||||
void dump_attributes(FILE *f, std::string indent, std::map<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n')
|
||||
{
|
||||
if (noattr)
|
||||
return;
|
||||
for (auto it = attributes.begin(); it != attributes.end(); it++) {
|
||||
fprintf(f, "%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
|
||||
if (it->second.bits.size() > 0) {
|
||||
fprintf(f, " = ");
|
||||
dump_const(f, it->second);
|
||||
}
|
||||
fprintf(f, " %s%c", attr2comment ? "*/" : "*)", term);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_wire(FILE *f, std::string indent, RTLIL::Wire *wire)
|
||||
{
|
||||
dump_attributes(f, indent, wire->attributes);
|
||||
if (wire->port_input && !wire->port_output)
|
||||
fprintf(f, "%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
||||
else if (!wire->port_input && wire->port_output)
|
||||
fprintf(f, "%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
||||
else if (wire->port_input && wire->port_output)
|
||||
fprintf(f, "%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
||||
else
|
||||
fprintf(f, "%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
|
||||
if (wire->width != 1)
|
||||
fprintf(f, "[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
|
||||
fprintf(f, "%s;\n", id(wire->name).c_str());
|
||||
}
|
||||
|
||||
void dump_memory(FILE *f, std::string indent, RTLIL::Memory *memory)
|
||||
{
|
||||
dump_attributes(f, indent, memory->attributes);
|
||||
fprintf(f, "%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
|
||||
}
|
||||
|
||||
void dump_cell_expr_port(FILE *f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
|
||||
{
|
||||
if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
|
||||
fprintf(f, "$signed(");
|
||||
dump_sigspec(f, cell->connections["\\" + port]);
|
||||
fprintf(f, ")");
|
||||
} else
|
||||
dump_sigspec(f, cell->connections["\\" + port]);
|
||||
}
|
||||
|
||||
std::string cellname(RTLIL::Cell *cell)
|
||||
{
|
||||
if (!norename && cell->name[0] == '$' && reg_ct.cell_known(cell->type) && cell->connections.count("\\Q") > 0)
|
||||
{
|
||||
RTLIL::SigSpec sig = cell->connections["\\Q"];
|
||||
if (sig.width != 1 || sig.is_fully_const())
|
||||
goto no_special_reg_name;
|
||||
|
||||
sig.optimize();
|
||||
RTLIL::Wire *wire = sig.chunks[0].wire;
|
||||
|
||||
if (wire->name[0] != '\\')
|
||||
goto no_special_reg_name;
|
||||
|
||||
std::string cell_name = wire->name;
|
||||
|
||||
size_t pos = cell_name.find('[');
|
||||
if (pos != std::string::npos)
|
||||
cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
|
||||
else
|
||||
cell_name = cell_name + "_reg";
|
||||
|
||||
if (wire->width != 1)
|
||||
cell_name += stringf("[%d]", wire->start_offset + sig.chunks[0].offset);
|
||||
|
||||
if (active_module && active_module->count_id(cell_name) > 0)
|
||||
goto no_special_reg_name;
|
||||
|
||||
return id(cell_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
no_special_reg_name:
|
||||
return id(cell->name).c_str();
|
||||
}
|
||||
}
|
||||
|
||||
void dump_cell_expr_uniop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op)
|
||||
{
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = %s ", op.c_str());
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
dump_cell_expr_port(f, cell, "A", true);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
void dump_cell_expr_binop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op)
|
||||
{
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = ");
|
||||
dump_cell_expr_port(f, cell, "A", true);
|
||||
fprintf(f, " %s ", op.c_str());
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
dump_cell_expr_port(f, cell, "B", true);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
bool dump_cell_expr(FILE *f, std::string indent, RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell->type == "$_INV_") {
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = ");
|
||||
fprintf(f, "~");
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
dump_cell_expr_port(f, cell, "A", false);
|
||||
fprintf(f, ";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") {
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = ");
|
||||
dump_cell_expr_port(f, cell, "A", false);
|
||||
fprintf(f, " ");
|
||||
if (cell->type == "$_AND_")
|
||||
fprintf(f, "&");
|
||||
if (cell->type == "$_OR_")
|
||||
fprintf(f, "|");
|
||||
if (cell->type == "$_XOR_")
|
||||
fprintf(f, "^");
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
fprintf(f, " ");
|
||||
dump_cell_expr_port(f, cell, "B", false);
|
||||
fprintf(f, ";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_") {
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = ");
|
||||
dump_cell_expr_port(f, cell, "S", false);
|
||||
fprintf(f, " ? ");
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
dump_cell_expr_port(f, cell, "B", false);
|
||||
fprintf(f, " : ");
|
||||
dump_cell_expr_port(f, cell, "A", false);
|
||||
fprintf(f, ";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type.substr(0, 6) == "$_DFF_")
|
||||
{
|
||||
std::string reg_name = cellname(cell);
|
||||
bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name);
|
||||
|
||||
if (!out_is_reg_wire)
|
||||
fprintf(f, "%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
||||
|
||||
dump_attributes(f, indent, cell->attributes);
|
||||
fprintf(f, "%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
|
||||
dump_sigspec(f, cell->connections["\\C"]);
|
||||
if (cell->type[7] != '_') {
|
||||
fprintf(f, " or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
|
||||
dump_sigspec(f, cell->connections["\\R"]);
|
||||
}
|
||||
fprintf(f, ")\n");
|
||||
|
||||
if (cell->type[7] != '_') {
|
||||
fprintf(f, "%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
|
||||
dump_sigspec(f, cell->connections["\\R"]);
|
||||
fprintf(f, ")\n");
|
||||
fprintf(f, "%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
|
||||
fprintf(f, "%s" " else\n", indent.c_str());
|
||||
}
|
||||
|
||||
fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
||||
dump_cell_expr_port(f, cell, "D", false);
|
||||
fprintf(f, ";\n");
|
||||
|
||||
if (!out_is_reg_wire) {
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Q"]);
|
||||
fprintf(f, " = %s;\n", reg_name.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define HANDLE_UNIOP(_type, _operator) \
|
||||
if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
|
||||
#define HANDLE_BINOP(_type, _operator) \
|
||||
if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
|
||||
|
||||
HANDLE_UNIOP("$not", "~")
|
||||
HANDLE_UNIOP("$pos", "+")
|
||||
HANDLE_UNIOP("$neg", "-")
|
||||
|
||||
HANDLE_BINOP("$and", "&")
|
||||
HANDLE_BINOP("$or", "|")
|
||||
HANDLE_BINOP("$xor", "^")
|
||||
HANDLE_BINOP("$xnor", "~^")
|
||||
|
||||
HANDLE_UNIOP("$reduce_and", "&")
|
||||
HANDLE_UNIOP("$reduce_or", "|")
|
||||
HANDLE_UNIOP("$reduce_xor", "^")
|
||||
HANDLE_UNIOP("$reduce_xnor", "~^")
|
||||
HANDLE_UNIOP("$reduce_bool", "|")
|
||||
|
||||
HANDLE_BINOP("$shl", "<<")
|
||||
HANDLE_BINOP("$shr", ">>")
|
||||
HANDLE_BINOP("$sshl", "<<<")
|
||||
HANDLE_BINOP("$sshr", ">>>")
|
||||
|
||||
HANDLE_BINOP("$lt", "<")
|
||||
HANDLE_BINOP("$le", "<=")
|
||||
HANDLE_BINOP("$eq", "==")
|
||||
HANDLE_BINOP("$ne", "!=")
|
||||
HANDLE_BINOP("$ge", ">=")
|
||||
HANDLE_BINOP("$gt", ">")
|
||||
|
||||
HANDLE_BINOP("$add", "+")
|
||||
HANDLE_BINOP("$sub", "-")
|
||||
HANDLE_BINOP("$mul", "*")
|
||||
HANDLE_BINOP("$div", "/")
|
||||
HANDLE_BINOP("$mod", "%")
|
||||
HANDLE_BINOP("$pow", "**")
|
||||
|
||||
HANDLE_UNIOP("$logic_not", "!")
|
||||
HANDLE_BINOP("$logic_and", "&&")
|
||||
HANDLE_BINOP("$logic_or", "||")
|
||||
|
||||
#undef HANDLE_UNIOP
|
||||
#undef HANDLE_BINOP
|
||||
|
||||
if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$pmux_safe")
|
||||
{
|
||||
int width = cell->parameters["\\WIDTH"].as_int();
|
||||
int s_width = cell->connections["\\S"].width;
|
||||
std::string reg_name = cellname(cell);
|
||||
fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
|
||||
|
||||
dump_attributes(f, indent, cell->attributes);
|
||||
if (!noattr)
|
||||
fprintf(f, "%s" "(* parallel_case *)\n", indent.c_str());
|
||||
fprintf(f, "%s" "always @*\n", indent.c_str());
|
||||
fprintf(f, "%s" " casez (", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\S"]);
|
||||
fprintf(f, noattr ? ") // synopsys parallel_case\n" : ")\n");
|
||||
|
||||
for (int i = 0; i < s_width; i++)
|
||||
{
|
||||
fprintf(f, "%s" " %d'b", indent.c_str(), s_width);
|
||||
|
||||
for (int j = s_width-1; j >= 0; j--)
|
||||
fprintf(f, "%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
|
||||
|
||||
fprintf(f, ":\n");
|
||||
fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str());
|
||||
|
||||
RTLIL::SigSpec s = cell->connections["\\B"].extract(i * width, width);
|
||||
dump_sigspec(f, s);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
fprintf(f, "%s" " default:\n", indent.c_str());
|
||||
fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str());
|
||||
dump_sigspec(f, cell->connections["\\A"]);
|
||||
fprintf(f, ";\n");
|
||||
|
||||
fprintf(f, "%s" " endcase\n", indent.c_str());
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Y"]);
|
||||
fprintf(f, " = %s;\n", reg_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$dff" || cell->type == "$adff")
|
||||
{
|
||||
RTLIL::SigSpec sig_clk, sig_arst, val_arst;
|
||||
bool pol_clk, pol_arst = false;
|
||||
|
||||
sig_clk = cell->connections["\\CLK"];
|
||||
pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
|
||||
|
||||
if (cell->type == "$adff") {
|
||||
sig_arst = cell->connections["\\ARST"];
|
||||
pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
|
||||
val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
|
||||
}
|
||||
|
||||
std::string reg_name = cellname(cell);
|
||||
bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name);
|
||||
|
||||
if (!out_is_reg_wire)
|
||||
fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
|
||||
|
||||
fprintf(f, "%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
||||
dump_sigspec(f, sig_clk);
|
||||
if (cell->type == "$adff") {
|
||||
fprintf(f, " or %sedge ", pol_arst ? "pos" : "neg");
|
||||
dump_sigspec(f, sig_arst);
|
||||
}
|
||||
fprintf(f, ")\n");
|
||||
|
||||
if (cell->type == "$adff") {
|
||||
fprintf(f, "%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
|
||||
dump_sigspec(f, sig_arst);
|
||||
fprintf(f, ")\n");
|
||||
fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
||||
dump_sigspec(f, val_arst);
|
||||
fprintf(f, ";\n");
|
||||
fprintf(f, "%s" " else\n", indent.c_str());
|
||||
}
|
||||
|
||||
fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
||||
dump_cell_expr_port(f, cell, "D", false);
|
||||
fprintf(f, ";\n");
|
||||
|
||||
if (!out_is_reg_wire) {
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->connections["\\Q"]);
|
||||
fprintf(f, " = %s;\n", reg_name.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: $memrd, $memwr, $mem, $fsm
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dump_cell(FILE *f, std::string indent, RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell->type[0] == '$' && !noexpr) {
|
||||
if (dump_cell_expr(f, indent, cell))
|
||||
return;
|
||||
}
|
||||
|
||||
dump_attributes(f, indent, cell->attributes);
|
||||
fprintf(f, "%s" "%s", indent.c_str(), id(cell->type, false).c_str());
|
||||
|
||||
if (cell->parameters.size() > 0) {
|
||||
fprintf(f, " #(");
|
||||
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) {
|
||||
if (it != cell->parameters.begin())
|
||||
fprintf(f, ",");
|
||||
fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
||||
dump_const(f, it->second);
|
||||
fprintf(f, ")");
|
||||
}
|
||||
fprintf(f, "\n%s" ")", indent.c_str());
|
||||
}
|
||||
|
||||
std::string cell_name = cellname(cell);
|
||||
if (cell_name != id(cell->name))
|
||||
fprintf(f, " %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
|
||||
else
|
||||
fprintf(f, " %s (", cell_name.c_str());
|
||||
|
||||
bool first_arg = true;
|
||||
std::set<std::string> numbered_ports;
|
||||
for (int i = 1; true; i++) {
|
||||
char str[16];
|
||||
snprintf(str, 16, "$%d", i);
|
||||
for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
|
||||
if (it->first != str)
|
||||
continue;
|
||||
if (!first_arg)
|
||||
fprintf(f, ",");
|
||||
first_arg = false;
|
||||
fprintf(f, "\n%s ", indent.c_str());
|
||||
dump_sigspec(f, it->second);
|
||||
numbered_ports.insert(it->first);
|
||||
goto found_numbered_port;
|
||||
}
|
||||
break;
|
||||
found_numbered_port:;
|
||||
}
|
||||
for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
|
||||
if (numbered_ports.count(it->first))
|
||||
continue;
|
||||
if (!first_arg)
|
||||
fprintf(f, ",");
|
||||
first_arg = false;
|
||||
fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
||||
if (it->second.width > 0)
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, ")");
|
||||
}
|
||||
fprintf(f, "\n%s" ");\n", indent.c_str());
|
||||
}
|
||||
|
||||
void dump_conn(FILE *f, std::string indent, RTLIL::SigSpec &left, RTLIL::SigSpec &right)
|
||||
{
|
||||
fprintf(f, "%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, left);
|
||||
fprintf(f, " = ");
|
||||
dump_sigspec(f, right);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw);
|
||||
|
||||
void dump_case_body(FILE *f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
||||
{
|
||||
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts >= 2)
|
||||
fprintf(f, "%s" "begin\n", indent.c_str());
|
||||
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) {
|
||||
if (it->first.width == 0)
|
||||
continue;
|
||||
fprintf(f, "%s ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
fprintf(f, " = ");
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
|
||||
dump_proc_switch(f, indent + " ", *it);
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts == 0)
|
||||
fprintf(f, "%s /* empty */;\n", indent.c_str());
|
||||
|
||||
if (omit_trailing_begin || number_of_stmts >= 2)
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw)
|
||||
{
|
||||
if (sw->signal.width == 0) {
|
||||
fprintf(f, "%s" "begin\n", indent.c_str());
|
||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) {
|
||||
if ((*it)->compare.size() == 0)
|
||||
dump_case_body(f, indent + " ", *it);
|
||||
}
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "casez (", indent.c_str());
|
||||
dump_sigspec(f, sw->signal);
|
||||
fprintf(f, ")\n");
|
||||
|
||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) {
|
||||
fprintf(f, "%s ", indent.c_str());
|
||||
if ((*it)->compare.size() == 0)
|
||||
fprintf(f, "default");
|
||||
else {
|
||||
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
||||
if (i > 0)
|
||||
fprintf(f, ", ");
|
||||
dump_sigspec(f, (*it)->compare[i]);
|
||||
}
|
||||
}
|
||||
fprintf(f, ":\n");
|
||||
dump_case_body(f, indent + " ", *it);
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "endcase\n", indent.c_str());
|
||||
}
|
||||
|
||||
void case_body_find_regs(RTLIL::CaseRule *cs)
|
||||
{
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
|
||||
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
|
||||
case_body_find_regs(*it2);
|
||||
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) {
|
||||
for (size_t i = 0; i < it->first.chunks.size(); i++)
|
||||
if (it->first.chunks[i].wire)
|
||||
reg_wires.insert(it->first.chunks[i].wire->name);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_process(FILE *f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
|
||||
{
|
||||
if (find_regs) {
|
||||
case_body_find_regs(&proc->root_case);
|
||||
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++)
|
||||
for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
|
||||
for (size_t i = 0; i < it2->first.chunks.size(); i++)
|
||||
if (it2->first.chunks[i].wire)
|
||||
reg_wires.insert(it2->first.chunks[i].wire->name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "%s" "always @* begin\n", indent.c_str());
|
||||
dump_case_body(f, indent, &proc->root_case, true);
|
||||
|
||||
std::string backup_indent = indent;
|
||||
|
||||
for (size_t i = 0; i < proc->syncs.size(); i++)
|
||||
{
|
||||
RTLIL::SyncRule *sync = proc->syncs[i];
|
||||
indent = backup_indent;
|
||||
|
||||
if (sync->type == RTLIL::STa) {
|
||||
fprintf(f, "%s" "always @* begin\n", indent.c_str());
|
||||
} else {
|
||||
fprintf(f, "%s" "always @(", indent.c_str());
|
||||
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
|
||||
fprintf(f, "posedge ");
|
||||
if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
|
||||
fprintf(f, "negedge ");
|
||||
dump_sigspec(f, sync->signal);
|
||||
fprintf(f, ") begin\n");
|
||||
}
|
||||
std::string ends = indent + "end\n";
|
||||
indent += " ";
|
||||
|
||||
if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
|
||||
fprintf(f, "%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
|
||||
dump_sigspec(f, sync->signal);
|
||||
fprintf(f, ") begin\n");
|
||||
ends = indent + "end\n" + ends;
|
||||
indent += " ";
|
||||
}
|
||||
|
||||
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
|
||||
for (size_t j = 0; j < proc->syncs.size(); j++) {
|
||||
RTLIL::SyncRule *sync2 = proc->syncs[j];
|
||||
if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
|
||||
fprintf(f, "%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
|
||||
dump_sigspec(f, sync2->signal);
|
||||
fprintf(f, ") begin\n");
|
||||
ends = indent + "end\n" + ends;
|
||||
indent += " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = sync->actions.begin(); it != sync->actions.end(); it++) {
|
||||
if (it->first.width == 0)
|
||||
continue;
|
||||
fprintf(f, "%s ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
fprintf(f, " <= ");
|
||||
dump_sigspec(f, it->second);
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
fprintf(f, "%s", ends.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void dump_module(FILE *f, std::string indent, RTLIL::Module *module)
|
||||
{
|
||||
reg_wires.clear();
|
||||
reset_auto_counter(module);
|
||||
active_module = module;
|
||||
|
||||
for (auto it = module->processes.begin(); it != module->processes.end(); it++)
|
||||
dump_process(f, indent + " ", it->second, true);
|
||||
|
||||
if (!noexpr)
|
||||
{
|
||||
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
|
||||
for (auto &it : module->cells)
|
||||
{
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (!reg_ct.cell_known(cell->type) || cell->connections.count("\\Q") == 0)
|
||||
continue;
|
||||
|
||||
RTLIL::SigSpec sig = cell->connections["\\Q"];
|
||||
sig.optimize();
|
||||
|
||||
if (sig.chunks.size() == 1 && sig.chunks[0].wire)
|
||||
for (int i = 0; i < sig.chunks[0].width; i++)
|
||||
reg_bits.insert(std::pair<RTLIL::Wire*,int>(sig.chunks[0].wire, sig.chunks[0].offset+i));
|
||||
}
|
||||
for (auto &it : module->wires)
|
||||
{
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
|
||||
goto this_wire_aint_reg;
|
||||
reg_wires.insert(wire->name);
|
||||
this_wire_aint_reg:;
|
||||
}
|
||||
}
|
||||
|
||||
dump_attributes(f, indent, module->attributes);
|
||||
fprintf(f, "%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
|
||||
bool keep_running = true;
|
||||
for (int port_id = 1; keep_running; port_id++) {
|
||||
keep_running = false;
|
||||
for (auto it = module->wires.begin(); it != module->wires.end(); it++) {
|
||||
RTLIL::Wire *wire = it->second;
|
||||
if (wire->port_id == port_id) {
|
||||
if (port_id != 1)
|
||||
fprintf(f, ", ");
|
||||
fprintf(f, "%s", id(wire->name).c_str());
|
||||
keep_running = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(f, ");\n");
|
||||
|
||||
for (auto it = module->wires.begin(); it != module->wires.end(); it++)
|
||||
dump_wire(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->memories.begin(); it != module->memories.end(); it++)
|
||||
dump_memory(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->cells.begin(); it != module->cells.end(); it++)
|
||||
dump_cell(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->processes.begin(); it != module->processes.end(); it++)
|
||||
dump_process(f, indent + " ", it->second);
|
||||
|
||||
for (auto it = module->connections.begin(); it != module->connections.end(); it++)
|
||||
dump_conn(f, indent + " ", it->first, it->second);
|
||||
|
||||
fprintf(f, "%s" "endmodule\n", indent.c_str());
|
||||
active_module = NULL;
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
struct VerilogBackend : public Backend {
|
||||
VerilogBackend() : Backend("verilog") { }
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing Verilog backend.\n");
|
||||
|
||||
norename = false;
|
||||
noattr = false;
|
||||
attr2comment = false;
|
||||
noexpr = false;
|
||||
|
||||
reg_ct.clear();
|
||||
reg_ct.setup_stdcells_mem();
|
||||
reg_ct.cell_types.insert("$dff");
|
||||
reg_ct.cell_types.insert("$adff");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-norename") {
|
||||
norename = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noattr") {
|
||||
noattr = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-attr2comment") {
|
||||
attr2comment = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noexpr") {
|
||||
noexpr = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
|
||||
log("Dumping module `%s'.\n", it->first.c_str());
|
||||
if (it != design->modules.begin())
|
||||
fprintf(f, "\n");
|
||||
dump_module(f, "", it->second);
|
||||
}
|
||||
|
||||
reg_ct.clear();
|
||||
}
|
||||
} VerilogBackend;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A simple and straightforward verilog backend.
|
||||
*
|
||||
* Note that RTLIL processes can't always be mapped easily to a Verilog
|
||||
* process. Therefore this frontend should only be used to export a
|
||||
* Verilog netlist (i.e. after the "proc" pass has converted all processes
|
||||
* to logic networks and registers).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VERILOG_BACKEND_H
|
||||
#define VERILOG_BACKEND_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace VERILOG_BACKEND {
|
||||
void verilog_backend(FILE *f, std::vector<std::string> args, RTLIL::Design *design);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
*.o
|
||||
sample
|
||||
testsuite
|
||||
testsuite.expected
|
||||
testsuite.out
|
||||
testsuite.err
|
|
@ -0,0 +1,405 @@
|
|||
#include "BigInteger.hh"
|
||||
|
||||
void BigInteger::operator =(const BigInteger &x) {
|
||||
// Calls like a = a have no effect
|
||||
if (this == &x)
|
||||
return;
|
||||
// Copy sign
|
||||
sign = x.sign;
|
||||
// Copy the rest
|
||||
mag = x.mag;
|
||||
}
|
||||
|
||||
BigInteger::BigInteger(const Blk *b, Index blen, Sign s) : mag(b, blen) {
|
||||
switch (s) {
|
||||
case zero:
|
||||
if (!mag.isZero())
|
||||
throw "BigInteger::BigInteger(const Blk *, Index, Sign): Cannot use a sign of zero with a nonzero magnitude";
|
||||
sign = zero;
|
||||
break;
|
||||
case positive:
|
||||
case negative:
|
||||
// If the magnitude is zero, force the sign to zero.
|
||||
sign = mag.isZero() ? zero : s;
|
||||
break;
|
||||
default:
|
||||
/* g++ seems to be optimizing out this case on the assumption
|
||||
* that the sign is a valid member of the enumeration. Oh well. */
|
||||
throw "BigInteger::BigInteger(const Blk *, Index, Sign): Invalid sign";
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger::BigInteger(const BigUnsigned &x, Sign s) : mag(x) {
|
||||
switch (s) {
|
||||
case zero:
|
||||
if (!mag.isZero())
|
||||
throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Cannot use a sign of zero with a nonzero magnitude";
|
||||
sign = zero;
|
||||
break;
|
||||
case positive:
|
||||
case negative:
|
||||
// If the magnitude is zero, force the sign to zero.
|
||||
sign = mag.isZero() ? zero : s;
|
||||
break;
|
||||
default:
|
||||
/* g++ seems to be optimizing out this case on the assumption
|
||||
* that the sign is a valid member of the enumeration. Oh well. */
|
||||
throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Invalid sign";
|
||||
}
|
||||
}
|
||||
|
||||
/* CONSTRUCTION FROM PRIMITIVE INTEGERS
|
||||
* Same idea as in BigUnsigned.cc, except that negative input results in a
|
||||
* negative BigInteger instead of an exception. */
|
||||
|
||||
// Done longhand to let us use initialization.
|
||||
BigInteger::BigInteger(unsigned long x) : mag(x) { sign = mag.isZero() ? zero : positive; }
|
||||
BigInteger::BigInteger(unsigned int x) : mag(x) { sign = mag.isZero() ? zero : positive; }
|
||||
BigInteger::BigInteger(unsigned short x) : mag(x) { sign = mag.isZero() ? zero : positive; }
|
||||
|
||||
// For signed input, determine the desired magnitude and sign separately.
|
||||
|
||||
namespace {
|
||||
template <class X, class UX>
|
||||
BigInteger::Blk magOf(X x) {
|
||||
/* UX(...) cast needed to stop short(-2^15), which negates to
|
||||
* itself, from sign-extending in the conversion to Blk. */
|
||||
return BigInteger::Blk(x < 0 ? UX(-x) : x);
|
||||
}
|
||||
template <class X>
|
||||
BigInteger::Sign signOf(X x) {
|
||||
return (x == 0) ? BigInteger::zero
|
||||
: (x > 0) ? BigInteger::positive
|
||||
: BigInteger::negative;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger::BigInteger(long x) : sign(signOf(x)), mag(magOf<long , unsigned long >(x)) {}
|
||||
BigInteger::BigInteger(int x) : sign(signOf(x)), mag(magOf<int , unsigned int >(x)) {}
|
||||
BigInteger::BigInteger(short x) : sign(signOf(x)), mag(magOf<short, unsigned short>(x)) {}
|
||||
|
||||
// CONVERSION TO PRIMITIVE INTEGERS
|
||||
|
||||
/* Reuse BigUnsigned's conversion to an unsigned primitive integer.
|
||||
* The friend is a separate function rather than
|
||||
* BigInteger::convertToUnsignedPrimitive to avoid requiring BigUnsigned to
|
||||
* declare BigInteger. */
|
||||
template <class X>
|
||||
inline X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a) {
|
||||
return a.convertToPrimitive<X>();
|
||||
}
|
||||
|
||||
template <class X>
|
||||
X BigInteger::convertToUnsignedPrimitive() const {
|
||||
if (sign == negative)
|
||||
throw "BigInteger::to<Primitive>: "
|
||||
"Cannot convert a negative integer to an unsigned type";
|
||||
else
|
||||
return convertBigUnsignedToPrimitiveAccess<X>(mag);
|
||||
}
|
||||
|
||||
/* Similar to BigUnsigned::convertToPrimitive, but split into two cases for
|
||||
* nonnegative and negative numbers. */
|
||||
template <class X, class UX>
|
||||
X BigInteger::convertToSignedPrimitive() const {
|
||||
if (sign == zero)
|
||||
return 0;
|
||||
else if (mag.getLength() == 1) {
|
||||
// The single block might fit in an X. Try the conversion.
|
||||
Blk b = mag.getBlock(0);
|
||||
if (sign == positive) {
|
||||
X x = X(b);
|
||||
if (x >= 0 && Blk(x) == b)
|
||||
return x;
|
||||
} else {
|
||||
X x = -X(b);
|
||||
/* UX(...) needed to avoid rejecting conversion of
|
||||
* -2^15 to a short. */
|
||||
if (x < 0 && Blk(UX(-x)) == b)
|
||||
return x;
|
||||
}
|
||||
// Otherwise fall through.
|
||||
}
|
||||
throw "BigInteger::to<Primitive>: "
|
||||
"Value is too big to fit in the requested type";
|
||||
}
|
||||
|
||||
unsigned long BigInteger::toUnsignedLong () const { return convertToUnsignedPrimitive<unsigned long > (); }
|
||||
unsigned int BigInteger::toUnsignedInt () const { return convertToUnsignedPrimitive<unsigned int > (); }
|
||||
unsigned short BigInteger::toUnsignedShort() const { return convertToUnsignedPrimitive<unsigned short> (); }
|
||||
long BigInteger::toLong () const { return convertToSignedPrimitive <long , unsigned long> (); }
|
||||
int BigInteger::toInt () const { return convertToSignedPrimitive <int , unsigned int> (); }
|
||||
short BigInteger::toShort () const { return convertToSignedPrimitive <short, unsigned short>(); }
|
||||
|
||||
// COMPARISON
|
||||
BigInteger::CmpRes BigInteger::compareTo(const BigInteger &x) const {
|
||||
// A greater sign implies a greater number
|
||||
if (sign < x.sign)
|
||||
return less;
|
||||
else if (sign > x.sign)
|
||||
return greater;
|
||||
else switch (sign) {
|
||||
// If the signs are the same...
|
||||
case zero:
|
||||
return equal; // Two zeros are equal
|
||||
case positive:
|
||||
// Compare the magnitudes
|
||||
return mag.compareTo(x.mag);
|
||||
case negative:
|
||||
// Compare the magnitudes, but return the opposite result
|
||||
return CmpRes(-mag.compareTo(x.mag));
|
||||
default:
|
||||
throw "BigInteger internal error";
|
||||
}
|
||||
}
|
||||
|
||||
/* COPY-LESS OPERATIONS
|
||||
* These do some messing around to determine the sign of the result,
|
||||
* then call one of BigUnsigned's copy-less operations. */
|
||||
|
||||
// See remarks about aliased calls in BigUnsigned.cc .
|
||||
#define DTRT_ALIASED(cond, op) \
|
||||
if (cond) { \
|
||||
BigInteger tmpThis; \
|
||||
tmpThis.op; \
|
||||
*this = tmpThis; \
|
||||
return; \
|
||||
}
|
||||
|
||||
void BigInteger::add(const BigInteger &a, const BigInteger &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, add(a, b));
|
||||
// If one argument is zero, copy the other.
|
||||
if (a.sign == zero)
|
||||
operator =(b);
|
||||
else if (b.sign == zero)
|
||||
operator =(a);
|
||||
// If the arguments have the same sign, take the
|
||||
// common sign and add their magnitudes.
|
||||
else if (a.sign == b.sign) {
|
||||
sign = a.sign;
|
||||
mag.add(a.mag, b.mag);
|
||||
} else {
|
||||
// Otherwise, their magnitudes must be compared.
|
||||
switch (a.mag.compareTo(b.mag)) {
|
||||
case equal:
|
||||
// If their magnitudes are the same, copy zero.
|
||||
mag = 0;
|
||||
sign = zero;
|
||||
break;
|
||||
// Otherwise, take the sign of the greater, and subtract
|
||||
// the lesser magnitude from the greater magnitude.
|
||||
case greater:
|
||||
sign = a.sign;
|
||||
mag.subtract(a.mag, b.mag);
|
||||
break;
|
||||
case less:
|
||||
sign = b.sign;
|
||||
mag.subtract(b.mag, a.mag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BigInteger::subtract(const BigInteger &a, const BigInteger &b) {
|
||||
// Notice that this routine is identical to BigInteger::add,
|
||||
// if one replaces b.sign by its opposite.
|
||||
DTRT_ALIASED(this == &a || this == &b, subtract(a, b));
|
||||
// If a is zero, copy b and flip its sign. If b is zero, copy a.
|
||||
if (a.sign == zero) {
|
||||
mag = b.mag;
|
||||
// Take the negative of _b_'s, sign, not ours.
|
||||
// Bug pointed out by Sam Larkin on 2005.03.30.
|
||||
sign = Sign(-b.sign);
|
||||
} else if (b.sign == zero)
|
||||
operator =(a);
|
||||
// If their signs differ, take a.sign and add the magnitudes.
|
||||
else if (a.sign != b.sign) {
|
||||
sign = a.sign;
|
||||
mag.add(a.mag, b.mag);
|
||||
} else {
|
||||
// Otherwise, their magnitudes must be compared.
|
||||
switch (a.mag.compareTo(b.mag)) {
|
||||
// If their magnitudes are the same, copy zero.
|
||||
case equal:
|
||||
mag = 0;
|
||||
sign = zero;
|
||||
break;
|
||||
// If a's magnitude is greater, take a.sign and
|
||||
// subtract a from b.
|
||||
case greater:
|
||||
sign = a.sign;
|
||||
mag.subtract(a.mag, b.mag);
|
||||
break;
|
||||
// If b's magnitude is greater, take the opposite
|
||||
// of b.sign and subtract b from a.
|
||||
case less:
|
||||
sign = Sign(-b.sign);
|
||||
mag.subtract(b.mag, a.mag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BigInteger::multiply(const BigInteger &a, const BigInteger &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, multiply(a, b));
|
||||
// If one object is zero, copy zero and return.
|
||||
if (a.sign == zero || b.sign == zero) {
|
||||
sign = zero;
|
||||
mag = 0;
|
||||
return;
|
||||
}
|
||||
// If the signs of the arguments are the same, the result
|
||||
// is positive, otherwise it is negative.
|
||||
sign = (a.sign == b.sign) ? positive : negative;
|
||||
// Multiply the magnitudes.
|
||||
mag.multiply(a.mag, b.mag);
|
||||
}
|
||||
|
||||
/*
|
||||
* DIVISION WITH REMAINDER
|
||||
* Please read the comments before the definition of
|
||||
* `BigUnsigned::divideWithRemainder' in `BigUnsigned.cc' for lots of
|
||||
* information you should know before reading this function.
|
||||
*
|
||||
* Following Knuth, I decree that x / y is to be
|
||||
* 0 if y==0 and floor(real-number x / y) if y!=0.
|
||||
* Then x % y shall be x - y*(integer x / y).
|
||||
*
|
||||
* Note that x = y * (x / y) + (x % y) always holds.
|
||||
* In addition, (x % y) is from 0 to y - 1 if y > 0,
|
||||
* and from -(|y| - 1) to 0 if y < 0. (x % y) = x if y = 0.
|
||||
*
|
||||
* Examples: (q = a / b, r = a % b)
|
||||
* a b q r
|
||||
* === === === ===
|
||||
* 4 3 1 1
|
||||
* -4 3 -2 2
|
||||
* 4 -3 -2 -2
|
||||
* -4 -3 1 -1
|
||||
*/
|
||||
void BigInteger::divideWithRemainder(const BigInteger &b, BigInteger &q) {
|
||||
// Defend against aliased calls;
|
||||
// same idea as in BigUnsigned::divideWithRemainder .
|
||||
if (this == &q)
|
||||
throw "BigInteger::divideWithRemainder: Cannot write quotient and remainder into the same variable";
|
||||
if (this == &b || &q == &b) {
|
||||
BigInteger tmpB(b);
|
||||
divideWithRemainder(tmpB, q);
|
||||
return;
|
||||
}
|
||||
|
||||
// Division by zero gives quotient 0 and remainder *this
|
||||
if (b.sign == zero) {
|
||||
q.mag = 0;
|
||||
q.sign = zero;
|
||||
return;
|
||||
}
|
||||
// 0 / b gives quotient 0 and remainder 0
|
||||
if (sign == zero) {
|
||||
q.mag = 0;
|
||||
q.sign = zero;
|
||||
return;
|
||||
}
|
||||
|
||||
// Here *this != 0, b != 0.
|
||||
|
||||
// Do the operands have the same sign?
|
||||
if (sign == b.sign) {
|
||||
// Yes: easy case. Quotient is zero or positive.
|
||||
q.sign = positive;
|
||||
} else {
|
||||
// No: harder case. Quotient is negative.
|
||||
q.sign = negative;
|
||||
// Decrease the magnitude of the dividend by one.
|
||||
mag--;
|
||||
/*
|
||||
* We tinker with the dividend before and with the
|
||||
* quotient and remainder after so that the result
|
||||
* comes out right. To see why it works, consider the following
|
||||
* list of examples, where A is the magnitude-decreased
|
||||
* a, Q and R are the results of BigUnsigned division
|
||||
* with remainder on A and |b|, and q and r are the
|
||||
* final results we want:
|
||||
*
|
||||
* a A b Q R q r
|
||||
* -3 -2 3 0 2 -1 0
|
||||
* -4 -3 3 1 0 -2 2
|
||||
* -5 -4 3 1 1 -2 1
|
||||
* -6 -5 3 1 2 -2 0
|
||||
*
|
||||
* It appears that we need a total of 3 corrections:
|
||||
* Decrease the magnitude of a to get A. Increase the
|
||||
* magnitude of Q to get q (and make it negative).
|
||||
* Find r = (b - 1) - R and give it the desired sign.
|
||||
*/
|
||||
}
|
||||
|
||||
// Divide the magnitudes.
|
||||
mag.divideWithRemainder(b.mag, q.mag);
|
||||
|
||||
if (sign != b.sign) {
|
||||
// More for the harder case (as described):
|
||||
// Increase the magnitude of the quotient by one.
|
||||
q.mag++;
|
||||
// Modify the remainder.
|
||||
mag.subtract(b.mag, mag);
|
||||
mag--;
|
||||
}
|
||||
|
||||
// Sign of the remainder is always the sign of the divisor b.
|
||||
sign = b.sign;
|
||||
|
||||
// Set signs to zero as necessary. (Thanks David Allen!)
|
||||
if (mag.isZero())
|
||||
sign = zero;
|
||||
if (q.mag.isZero())
|
||||
q.sign = zero;
|
||||
|
||||
// WHEW!!!
|
||||
}
|
||||
|
||||
// Negation
|
||||
void BigInteger::negate(const BigInteger &a) {
|
||||
DTRT_ALIASED(this == &a, negate(a));
|
||||
// Copy a's magnitude
|
||||
mag = a.mag;
|
||||
// Copy the opposite of a.sign
|
||||
sign = Sign(-a.sign);
|
||||
}
|
||||
|
||||
// INCREMENT/DECREMENT OPERATORS
|
||||
|
||||
// Prefix increment
|
||||
void BigInteger::operator ++() {
|
||||
if (sign == negative) {
|
||||
mag--;
|
||||
if (mag == 0)
|
||||
sign = zero;
|
||||
} else {
|
||||
mag++;
|
||||
sign = positive; // if not already
|
||||
}
|
||||
}
|
||||
|
||||
// Postfix increment: same as prefix
|
||||
void BigInteger::operator ++(int) {
|
||||
operator ++();
|
||||
}
|
||||
|
||||
// Prefix decrement
|
||||
void BigInteger::operator --() {
|
||||
if (sign == positive) {
|
||||
mag--;
|
||||
if (mag == 0)
|
||||
sign = zero;
|
||||
} else {
|
||||
mag++;
|
||||
sign = negative;
|
||||
}
|
||||
}
|
||||
|
||||
// Postfix decrement: same as prefix
|
||||
void BigInteger::operator --(int) {
|
||||
operator --();
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
#ifndef BIGINTEGER_H
|
||||
#define BIGINTEGER_H
|
||||
|
||||
#include "BigUnsigned.hh"
|
||||
|
||||
/* A BigInteger object represents a signed integer of size limited only by
|
||||
* available memory. BigUnsigneds support most mathematical operators and can
|
||||
* be converted to and from most primitive integer types.
|
||||
*
|
||||
* A BigInteger is just an aggregate of a BigUnsigned and a sign. (It is no
|
||||
* longer derived from BigUnsigned because that led to harmful implicit
|
||||
* conversions.) */
|
||||
class BigInteger {
|
||||
|
||||
public:
|
||||
typedef BigUnsigned::Blk Blk;
|
||||
typedef BigUnsigned::Index Index;
|
||||
typedef BigUnsigned::CmpRes CmpRes;
|
||||
static const CmpRes
|
||||
less = BigUnsigned::less ,
|
||||
equal = BigUnsigned::equal ,
|
||||
greater = BigUnsigned::greater;
|
||||
// Enumeration for the sign of a BigInteger.
|
||||
enum Sign { negative = -1, zero = 0, positive = 1 };
|
||||
|
||||
protected:
|
||||
Sign sign;
|
||||
BigUnsigned mag;
|
||||
|
||||
public:
|
||||
// Constructs zero.
|
||||
BigInteger() : sign(zero), mag() {}
|
||||
|
||||
// Copy constructor
|
||||
BigInteger(const BigInteger &x) : sign(x.sign), mag(x.mag) {};
|
||||
|
||||
// Assignment operator
|
||||
void operator=(const BigInteger &x);
|
||||
|
||||
// Constructor that copies from a given array of blocks with a sign.
|
||||
BigInteger(const Blk *b, Index blen, Sign s);
|
||||
|
||||
// Nonnegative constructor that copies from a given array of blocks.
|
||||
BigInteger(const Blk *b, Index blen) : mag(b, blen) {
|
||||
sign = mag.isZero() ? zero : positive;
|
||||
}
|
||||
|
||||
// Constructor from a BigUnsigned and a sign
|
||||
BigInteger(const BigUnsigned &x, Sign s);
|
||||
|
||||
// Nonnegative constructor from a BigUnsigned
|
||||
BigInteger(const BigUnsigned &x) : mag(x) {
|
||||
sign = mag.isZero() ? zero : positive;
|
||||
}
|
||||
|
||||
// Constructors from primitive integer types
|
||||
BigInteger(unsigned long x);
|
||||
BigInteger( long x);
|
||||
BigInteger(unsigned int x);
|
||||
BigInteger( int x);
|
||||
BigInteger(unsigned short x);
|
||||
BigInteger( short x);
|
||||
|
||||
/* Converters to primitive integer types
|
||||
* The implicit conversion operators caused trouble, so these are now
|
||||
* named. */
|
||||
unsigned long toUnsignedLong () const;
|
||||
long toLong () const;
|
||||
unsigned int toUnsignedInt () const;
|
||||
int toInt () const;
|
||||
unsigned short toUnsignedShort() const;
|
||||
short toShort () const;
|
||||
protected:
|
||||
// Helper
|
||||
template <class X> X convertToUnsignedPrimitive() const;
|
||||
template <class X, class UX> X convertToSignedPrimitive() const;
|
||||
public:
|
||||
|
||||
// ACCESSORS
|
||||
Sign getSign() const { return sign; }
|
||||
/* The client can't do any harm by holding a read-only reference to the
|
||||
* magnitude. */
|
||||
const BigUnsigned &getMagnitude() const { return mag; }
|
||||
|
||||
// Some accessors that go through to the magnitude
|
||||
Index getLength() const { return mag.getLength(); }
|
||||
Index getCapacity() const { return mag.getCapacity(); }
|
||||
Blk getBlock(Index i) const { return mag.getBlock(i); }
|
||||
bool isZero() const { return sign == zero; } // A bit special
|
||||
|
||||
// COMPARISONS
|
||||
|
||||
// Compares this to x like Perl's <=>
|
||||
CmpRes compareTo(const BigInteger &x) const;
|
||||
|
||||
// Ordinary comparison operators
|
||||
bool operator ==(const BigInteger &x) const {
|
||||
return sign == x.sign && mag == x.mag;
|
||||
}
|
||||
bool operator !=(const BigInteger &x) const { return !operator ==(x); };
|
||||
bool operator < (const BigInteger &x) const { return compareTo(x) == less ; }
|
||||
bool operator <=(const BigInteger &x) const { return compareTo(x) != greater; }
|
||||
bool operator >=(const BigInteger &x) const { return compareTo(x) != less ; }
|
||||
bool operator > (const BigInteger &x) const { return compareTo(x) == greater; }
|
||||
|
||||
// OPERATORS -- See the discussion in BigUnsigned.hh.
|
||||
void add (const BigInteger &a, const BigInteger &b);
|
||||
void subtract(const BigInteger &a, const BigInteger &b);
|
||||
void multiply(const BigInteger &a, const BigInteger &b);
|
||||
/* See the comment on BigUnsigned::divideWithRemainder. Semantics
|
||||
* differ from those of primitive integers when negatives and/or zeros
|
||||
* are involved. */
|
||||
void divideWithRemainder(const BigInteger &b, BigInteger &q);
|
||||
void negate(const BigInteger &a);
|
||||
|
||||
/* Bitwise operators are not provided for BigIntegers. Use
|
||||
* getMagnitude to get the magnitude and operate on that instead. */
|
||||
|
||||
BigInteger operator +(const BigInteger &x) const;
|
||||
BigInteger operator -(const BigInteger &x) const;
|
||||
BigInteger operator *(const BigInteger &x) const;
|
||||
BigInteger operator /(const BigInteger &x) const;
|
||||
BigInteger operator %(const BigInteger &x) const;
|
||||
BigInteger operator -() const;
|
||||
|
||||
void operator +=(const BigInteger &x);
|
||||
void operator -=(const BigInteger &x);
|
||||
void operator *=(const BigInteger &x);
|
||||
void operator /=(const BigInteger &x);
|
||||
void operator %=(const BigInteger &x);
|
||||
void flipSign();
|
||||
|
||||
// INCREMENT/DECREMENT OPERATORS
|
||||
void operator ++( );
|
||||
void operator ++(int);
|
||||
void operator --( );
|
||||
void operator --(int);
|
||||
};
|
||||
|
||||
// NORMAL OPERATORS
|
||||
/* These create an object to hold the result and invoke
|
||||
* the appropriate put-here operation on it, passing
|
||||
* this and x. The new object is then returned. */
|
||||
inline BigInteger BigInteger::operator +(const BigInteger &x) const {
|
||||
BigInteger ans;
|
||||
ans.add(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigInteger BigInteger::operator -(const BigInteger &x) const {
|
||||
BigInteger ans;
|
||||
ans.subtract(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigInteger BigInteger::operator *(const BigInteger &x) const {
|
||||
BigInteger ans;
|
||||
ans.multiply(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigInteger BigInteger::operator /(const BigInteger &x) const {
|
||||
if (x.isZero()) throw "BigInteger::operator /: division by zero";
|
||||
BigInteger q, r;
|
||||
r = *this;
|
||||
r.divideWithRemainder(x, q);
|
||||
return q;
|
||||
}
|
||||
inline BigInteger BigInteger::operator %(const BigInteger &x) const {
|
||||
if (x.isZero()) throw "BigInteger::operator %: division by zero";
|
||||
BigInteger q, r;
|
||||
r = *this;
|
||||
r.divideWithRemainder(x, q);
|
||||
return r;
|
||||
}
|
||||
inline BigInteger BigInteger::operator -() const {
|
||||
BigInteger ans;
|
||||
ans.negate(*this);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/*
|
||||
* ASSIGNMENT OPERATORS
|
||||
*
|
||||
* Now the responsibility for making a temporary copy if necessary
|
||||
* belongs to the put-here operations. See Assignment Operators in
|
||||
* BigUnsigned.hh.
|
||||
*/
|
||||
inline void BigInteger::operator +=(const BigInteger &x) {
|
||||
add(*this, x);
|
||||
}
|
||||
inline void BigInteger::operator -=(const BigInteger &x) {
|
||||
subtract(*this, x);
|
||||
}
|
||||
inline void BigInteger::operator *=(const BigInteger &x) {
|
||||
multiply(*this, x);
|
||||
}
|
||||
inline void BigInteger::operator /=(const BigInteger &x) {
|
||||
if (x.isZero()) throw "BigInteger::operator /=: division by zero";
|
||||
/* The following technique is slightly faster than copying *this first
|
||||
* when x is large. */
|
||||
BigInteger q;
|
||||
divideWithRemainder(x, q);
|
||||
// *this contains the remainder, but we overwrite it with the quotient.
|
||||
*this = q;
|
||||
}
|
||||
inline void BigInteger::operator %=(const BigInteger &x) {
|
||||
if (x.isZero()) throw "BigInteger::operator %=: division by zero";
|
||||
BigInteger q;
|
||||
// Mods *this by x. Don't care about quotient left in q.
|
||||
divideWithRemainder(x, q);
|
||||
}
|
||||
// This one is trivial
|
||||
inline void BigInteger::flipSign() {
|
||||
sign = Sign(-sign);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
#include "BigIntegerAlgorithms.hh"
|
||||
|
||||
BigUnsigned gcd(BigUnsigned a, BigUnsigned b) {
|
||||
BigUnsigned trash;
|
||||
// Neat in-place alternating technique.
|
||||
for (;;) {
|
||||
if (b.isZero())
|
||||
return a;
|
||||
a.divideWithRemainder(b, trash);
|
||||
if (a.isZero())
|
||||
return b;
|
||||
b.divideWithRemainder(a, trash);
|
||||
}
|
||||
}
|
||||
|
||||
void extendedEuclidean(BigInteger m, BigInteger n,
|
||||
BigInteger &g, BigInteger &r, BigInteger &s) {
|
||||
if (&g == &r || &g == &s || &r == &s)
|
||||
throw "BigInteger extendedEuclidean: Outputs are aliased";
|
||||
BigInteger r1(1), s1(0), r2(0), s2(1), q;
|
||||
/* Invariants:
|
||||
* r1*m(orig) + s1*n(orig) == m(current)
|
||||
* r2*m(orig) + s2*n(orig) == n(current) */
|
||||
for (;;) {
|
||||
if (n.isZero()) {
|
||||
r = r1; s = s1; g = m;
|
||||
return;
|
||||
}
|
||||
// Subtract q times the second invariant from the first invariant.
|
||||
m.divideWithRemainder(n, q);
|
||||
r1 -= q*r2; s1 -= q*s2;
|
||||
|
||||
if (m.isZero()) {
|
||||
r = r2; s = s2; g = n;
|
||||
return;
|
||||
}
|
||||
// Subtract q times the first invariant from the second invariant.
|
||||
n.divideWithRemainder(m, q);
|
||||
r2 -= q*r1; s2 -= q*s1;
|
||||
}
|
||||
}
|
||||
|
||||
BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n) {
|
||||
BigInteger g, r, s;
|
||||
extendedEuclidean(x, n, g, r, s);
|
||||
if (g == 1)
|
||||
// r*x + s*n == 1, so r*x === 1 (mod n), so r is the answer.
|
||||
return (r % n).getMagnitude(); // (r % n) will be nonnegative
|
||||
else
|
||||
throw "BigInteger modinv: x and n have a common factor";
|
||||
}
|
||||
|
||||
BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent,
|
||||
const BigUnsigned &modulus) {
|
||||
BigUnsigned ans = 1, base2 = (base % modulus).getMagnitude();
|
||||
BigUnsigned::Index i = exponent.bitLength();
|
||||
// For each bit of the exponent, most to least significant...
|
||||
while (i > 0) {
|
||||
i--;
|
||||
// Square.
|
||||
ans *= ans;
|
||||
ans %= modulus;
|
||||
// And multiply if the bit is a 1.
|
||||
if (exponent.getBit(i)) {
|
||||
ans *= base2;
|
||||
ans %= modulus;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef BIGINTEGERALGORITHMS_H
|
||||
#define BIGINTEGERALGORITHMS_H
|
||||
|
||||
#include "BigInteger.hh"
|
||||
|
||||
/* Some mathematical algorithms for big integers.
|
||||
* This code is new and, as such, experimental. */
|
||||
|
||||
// Returns the greatest common divisor of a and b.
|
||||
BigUnsigned gcd(BigUnsigned a, BigUnsigned b);
|
||||
|
||||
/* Extended Euclidean algorithm.
|
||||
* Given m and n, finds gcd g and numbers r, s such that r*m + s*n == g. */
|
||||
void extendedEuclidean(BigInteger m, BigInteger n,
|
||||
BigInteger &g, BigInteger &r, BigInteger &s);
|
||||
|
||||
/* Returns the multiplicative inverse of x modulo n, or throws an exception if
|
||||
* they have a common factor. */
|
||||
BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n);
|
||||
|
||||
// Returns (base ^ exponent) % modulus.
|
||||
BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent,
|
||||
const BigUnsigned &modulus);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
// This header file includes all of the library header files.
|
||||
|
||||
#include "NumberlikeArray.hh"
|
||||
#include "BigUnsigned.hh"
|
||||
#include "BigInteger.hh"
|
||||
#include "BigIntegerAlgorithms.hh"
|
||||
#include "BigUnsignedInABase.hh"
|
||||
#include "BigIntegerUtils.hh"
|
|
@ -0,0 +1,50 @@
|
|||
#include "BigIntegerUtils.hh"
|
||||
#include "BigUnsignedInABase.hh"
|
||||
|
||||
std::string bigUnsignedToString(const BigUnsigned &x) {
|
||||
return std::string(BigUnsignedInABase(x, 10));
|
||||
}
|
||||
|
||||
std::string bigIntegerToString(const BigInteger &x) {
|
||||
return (x.getSign() == BigInteger::negative)
|
||||
? (std::string("-") + bigUnsignedToString(x.getMagnitude()))
|
||||
: (bigUnsignedToString(x.getMagnitude()));
|
||||
}
|
||||
|
||||
BigUnsigned stringToBigUnsigned(const std::string &s) {
|
||||
return BigUnsigned(BigUnsignedInABase(s, 10));
|
||||
}
|
||||
|
||||
BigInteger stringToBigInteger(const std::string &s) {
|
||||
// Recognize a sign followed by a BigUnsigned.
|
||||
return (s[0] == '-') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1)), BigInteger::negative)
|
||||
: (s[0] == '+') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1)))
|
||||
: BigInteger(stringToBigUnsigned(s));
|
||||
}
|
||||
|
||||
std::ostream &operator <<(std::ostream &os, const BigUnsigned &x) {
|
||||
BigUnsignedInABase::Base base;
|
||||
long osFlags = os.flags();
|
||||
if (osFlags & os.dec)
|
||||
base = 10;
|
||||
else if (osFlags & os.hex) {
|
||||
base = 16;
|
||||
if (osFlags & os.showbase)
|
||||
os << "0x";
|
||||
} else if (osFlags & os.oct) {
|
||||
base = 8;
|
||||
if (osFlags & os.showbase)
|
||||
os << '0';
|
||||
} else
|
||||
throw "std::ostream << BigUnsigned: Could not determine the desired base from output-stream flags";
|
||||
std::string s = std::string(BigUnsignedInABase(x, base));
|
||||
os << s;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &operator <<(std::ostream &os, const BigInteger &x) {
|
||||
if (x.getSign() == BigInteger::negative)
|
||||
os << '-';
|
||||
os << x.getMagnitude();
|
||||
return os;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef BIGINTEGERUTILS_H
|
||||
#define BIGINTEGERUTILS_H
|
||||
|
||||
#include "BigInteger.hh"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
/* This file provides:
|
||||
* - Convenient std::string <-> BigUnsigned/BigInteger conversion routines
|
||||
* - std::ostream << operators for BigUnsigned/BigInteger */
|
||||
|
||||
// std::string conversion routines. Base 10 only.
|
||||
std::string bigUnsignedToString(const BigUnsigned &x);
|
||||
std::string bigIntegerToString(const BigInteger &x);
|
||||
BigUnsigned stringToBigUnsigned(const std::string &s);
|
||||
BigInteger stringToBigInteger(const std::string &s);
|
||||
|
||||
// Creates a BigInteger from data such as `char's; read below for details.
|
||||
template <class T>
|
||||
BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign);
|
||||
|
||||
// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'.
|
||||
std::ostream &operator <<(std::ostream &os, const BigUnsigned &x);
|
||||
|
||||
// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'.
|
||||
// My somewhat arbitrary policy: a negative sign comes before a base indicator (like -0xFF).
|
||||
std::ostream &operator <<(std::ostream &os, const BigInteger &x);
|
||||
|
||||
// BEGIN TEMPLATE DEFINITIONS.
|
||||
|
||||
/*
|
||||
* Converts binary data to a BigInteger.
|
||||
* Pass an array `data', its length, and the desired sign.
|
||||
*
|
||||
* Elements of `data' may be of any type `T' that has the following
|
||||
* two properties (this includes almost all integral types):
|
||||
*
|
||||
* (1) `sizeof(T)' correctly gives the amount of binary data in one
|
||||
* value of `T' and is a factor of `sizeof(Blk)'.
|
||||
*
|
||||
* (2) When a value of `T' is casted to a `Blk', the low bytes of
|
||||
* the result contain the desired binary data.
|
||||
*/
|
||||
template <class T>
|
||||
BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign) {
|
||||
// really ceiling(numBytes / sizeof(BigInteger::Blk))
|
||||
unsigned int pieceSizeInBits = 8 * sizeof(T);
|
||||
unsigned int piecesPerBlock = sizeof(BigInteger::Blk) / sizeof(T);
|
||||
unsigned int numBlocks = (length + piecesPerBlock - 1) / piecesPerBlock;
|
||||
|
||||
// Allocate our block array
|
||||
BigInteger::Blk *blocks = new BigInteger::Blk[numBlocks];
|
||||
|
||||
BigInteger::Index blockNum, pieceNum, pieceNumHere;
|
||||
|
||||
// Convert
|
||||
for (blockNum = 0, pieceNum = 0; blockNum < numBlocks; blockNum++) {
|
||||
BigInteger::Blk curBlock = 0;
|
||||
for (pieceNumHere = 0; pieceNumHere < piecesPerBlock && pieceNum < length;
|
||||
pieceNumHere++, pieceNum++)
|
||||
curBlock |= (BigInteger::Blk(data[pieceNum]) << (pieceSizeInBits * pieceNumHere));
|
||||
blocks[blockNum] = curBlock;
|
||||
}
|
||||
|
||||
// Create the BigInteger.
|
||||
BigInteger x(blocks, numBlocks, sign);
|
||||
|
||||
delete [] blocks;
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,697 @@
|
|||
#include "BigUnsigned.hh"
|
||||
|
||||
// Memory management definitions have moved to the bottom of NumberlikeArray.hh.
|
||||
|
||||
// The templates used by these constructors and converters are at the bottom of
|
||||
// BigUnsigned.hh.
|
||||
|
||||
BigUnsigned::BigUnsigned(unsigned long x) { initFromPrimitive (x); }
|
||||
BigUnsigned::BigUnsigned(unsigned int x) { initFromPrimitive (x); }
|
||||
BigUnsigned::BigUnsigned(unsigned short x) { initFromPrimitive (x); }
|
||||
BigUnsigned::BigUnsigned( long x) { initFromSignedPrimitive(x); }
|
||||
BigUnsigned::BigUnsigned( int x) { initFromSignedPrimitive(x); }
|
||||
BigUnsigned::BigUnsigned( short x) { initFromSignedPrimitive(x); }
|
||||
|
||||
unsigned long BigUnsigned::toUnsignedLong () const { return convertToPrimitive <unsigned long >(); }
|
||||
unsigned int BigUnsigned::toUnsignedInt () const { return convertToPrimitive <unsigned int >(); }
|
||||
unsigned short BigUnsigned::toUnsignedShort() const { return convertToPrimitive <unsigned short>(); }
|
||||
long BigUnsigned::toLong () const { return convertToSignedPrimitive< long >(); }
|
||||
int BigUnsigned::toInt () const { return convertToSignedPrimitive< int >(); }
|
||||
short BigUnsigned::toShort () const { return convertToSignedPrimitive< short>(); }
|
||||
|
||||
// BIT/BLOCK ACCESSORS
|
||||
|
||||
void BigUnsigned::setBlock(Index i, Blk newBlock) {
|
||||
if (newBlock == 0) {
|
||||
if (i < len) {
|
||||
blk[i] = 0;
|
||||
zapLeadingZeros();
|
||||
}
|
||||
// If i >= len, no effect.
|
||||
} else {
|
||||
if (i >= len) {
|
||||
// The nonzero block extends the number.
|
||||
allocateAndCopy(i+1);
|
||||
// Zero any added blocks that we aren't setting.
|
||||
for (Index j = len; j < i; j++)
|
||||
blk[j] = 0;
|
||||
len = i+1;
|
||||
}
|
||||
blk[i] = newBlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* Evidently the compiler wants BigUnsigned:: on the return type because, at
|
||||
* that point, it hasn't yet parsed the BigUnsigned:: on the name to get the
|
||||
* proper scope. */
|
||||
BigUnsigned::Index BigUnsigned::bitLength() const {
|
||||
if (isZero())
|
||||
return 0;
|
||||
else {
|
||||
Blk leftmostBlock = getBlock(len - 1);
|
||||
Index leftmostBlockLen = 0;
|
||||
while (leftmostBlock != 0) {
|
||||
leftmostBlock >>= 1;
|
||||
leftmostBlockLen++;
|
||||
}
|
||||
return leftmostBlockLen + (len - 1) * N;
|
||||
}
|
||||
}
|
||||
|
||||
void BigUnsigned::setBit(Index bi, bool newBit) {
|
||||
Index blockI = bi / N;
|
||||
Blk block = getBlock(blockI), mask = Blk(1) << (bi % N);
|
||||
block = newBit ? (block | mask) : (block & ~mask);
|
||||
setBlock(blockI, block);
|
||||
}
|
||||
|
||||
// COMPARISON
|
||||
BigUnsigned::CmpRes BigUnsigned::compareTo(const BigUnsigned &x) const {
|
||||
// A bigger length implies a bigger number.
|
||||
if (len < x.len)
|
||||
return less;
|
||||
else if (len > x.len)
|
||||
return greater;
|
||||
else {
|
||||
// Compare blocks one by one from left to right.
|
||||
Index i = len;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
if (blk[i] == x.blk[i])
|
||||
continue;
|
||||
else if (blk[i] > x.blk[i])
|
||||
return greater;
|
||||
else
|
||||
return less;
|
||||
}
|
||||
// If no blocks differed, the numbers are equal.
|
||||
return equal;
|
||||
}
|
||||
}
|
||||
|
||||
// COPY-LESS OPERATIONS
|
||||
|
||||
/*
|
||||
* On most calls to copy-less operations, it's safe to read the inputs little by
|
||||
* little and write the outputs little by little. However, if one of the
|
||||
* inputs is coming from the same variable into which the output is to be
|
||||
* stored (an "aliased" call), we risk overwriting the input before we read it.
|
||||
* In this case, we first compute the result into a temporary BigUnsigned
|
||||
* variable and then copy it into the requested output variable *this.
|
||||
* Each put-here operation uses the DTRT_ALIASED macro (Do The Right Thing on
|
||||
* aliased calls) to generate code for this check.
|
||||
*
|
||||
* I adopted this approach on 2007.02.13 (see Assignment Operators in
|
||||
* BigUnsigned.hh). Before then, put-here operations rejected aliased calls
|
||||
* with an exception. I think doing the right thing is better.
|
||||
*
|
||||
* Some of the put-here operations can probably handle aliased calls safely
|
||||
* without the extra copy because (for example) they process blocks strictly
|
||||
* right-to-left. At some point I might determine which ones don't need the
|
||||
* copy, but my reasoning would need to be verified very carefully. For now
|
||||
* I'll leave in the copy.
|
||||
*/
|
||||
#define DTRT_ALIASED(cond, op) \
|
||||
if (cond) { \
|
||||
BigUnsigned tmpThis; \
|
||||
tmpThis.op; \
|
||||
*this = tmpThis; \
|
||||
return; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BigUnsigned::add(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, add(a, b));
|
||||
// If one argument is zero, copy the other.
|
||||
if (a.len == 0) {
|
||||
operator =(b);
|
||||
return;
|
||||
} else if (b.len == 0) {
|
||||
operator =(a);
|
||||
return;
|
||||
}
|
||||
// Some variables...
|
||||
// Carries in and out of an addition stage
|
||||
bool carryIn, carryOut;
|
||||
Blk temp;
|
||||
Index i;
|
||||
// a2 points to the longer input, b2 points to the shorter
|
||||
const BigUnsigned *a2, *b2;
|
||||
if (a.len >= b.len) {
|
||||
a2 = &a;
|
||||
b2 = &b;
|
||||
} else {
|
||||
a2 = &b;
|
||||
b2 = &a;
|
||||
}
|
||||
// Set prelimiary length and make room in this BigUnsigned
|
||||
len = a2->len + 1;
|
||||
allocate(len);
|
||||
// For each block index that is present in both inputs...
|
||||
for (i = 0, carryIn = false; i < b2->len; i++) {
|
||||
// Add input blocks
|
||||
temp = a2->blk[i] + b2->blk[i];
|
||||
// If a rollover occurred, the result is less than either input.
|
||||
// This test is used many times in the BigUnsigned code.
|
||||
carryOut = (temp < a2->blk[i]);
|
||||
// If a carry was input, handle it
|
||||
if (carryIn) {
|
||||
temp++;
|
||||
carryOut |= (temp == 0);
|
||||
}
|
||||
blk[i] = temp; // Save the addition result
|
||||
carryIn = carryOut; // Pass the carry along
|
||||
}
|
||||
// If there is a carry left over, increase blocks until
|
||||
// one does not roll over.
|
||||
for (; i < a2->len && carryIn; i++) {
|
||||
temp = a2->blk[i] + 1;
|
||||
carryIn = (temp == 0);
|
||||
blk[i] = temp;
|
||||
}
|
||||
// If the carry was resolved but the larger number
|
||||
// still has blocks, copy them over.
|
||||
for (; i < a2->len; i++)
|
||||
blk[i] = a2->blk[i];
|
||||
// Set the extra block if there's still a carry, decrease length otherwise
|
||||
if (carryIn)
|
||||
blk[i] = 1;
|
||||
else
|
||||
len--;
|
||||
}
|
||||
|
||||
void BigUnsigned::subtract(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, subtract(a, b));
|
||||
if (b.len == 0) {
|
||||
// If b is zero, copy a.
|
||||
operator =(a);
|
||||
return;
|
||||
} else if (a.len < b.len)
|
||||
// If a is shorter than b, the result is negative.
|
||||
throw "BigUnsigned::subtract: "
|
||||
"Negative result in unsigned calculation";
|
||||
// Some variables...
|
||||
bool borrowIn, borrowOut;
|
||||
Blk temp;
|
||||
Index i;
|
||||
// Set preliminary length and make room
|
||||
len = a.len;
|
||||
allocate(len);
|
||||
// For each block index that is present in both inputs...
|
||||
for (i = 0, borrowIn = false; i < b.len; i++) {
|
||||
temp = a.blk[i] - b.blk[i];
|
||||
// If a reverse rollover occurred,
|
||||
// the result is greater than the block from a.
|
||||
borrowOut = (temp > a.blk[i]);
|
||||
// Handle an incoming borrow
|
||||
if (borrowIn) {
|
||||
borrowOut |= (temp == 0);
|
||||
temp--;
|
||||
}
|
||||
blk[i] = temp; // Save the subtraction result
|
||||
borrowIn = borrowOut; // Pass the borrow along
|
||||
}
|
||||
// If there is a borrow left over, decrease blocks until
|
||||
// one does not reverse rollover.
|
||||
for (; i < a.len && borrowIn; i++) {
|
||||
borrowIn = (a.blk[i] == 0);
|
||||
blk[i] = a.blk[i] - 1;
|
||||
}
|
||||
/* If there's still a borrow, the result is negative.
|
||||
* Throw an exception, but zero out this object so as to leave it in a
|
||||
* predictable state. */
|
||||
if (borrowIn) {
|
||||
len = 0;
|
||||
throw "BigUnsigned::subtract: Negative result in unsigned calculation";
|
||||
} else
|
||||
// Copy over the rest of the blocks
|
||||
for (; i < a.len; i++)
|
||||
blk[i] = a.blk[i];
|
||||
// Zap leading zeros
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
/*
|
||||
* About the multiplication and division algorithms:
|
||||
*
|
||||
* I searched unsucessfully for fast C++ built-in operations like the `b_0'
|
||||
* and `c_0' Knuth describes in Section 4.3.1 of ``The Art of Computer
|
||||
* Programming'' (replace `place' by `Blk'):
|
||||
*
|
||||
* ``b_0[:] multiplication of a one-place integer by another one-place
|
||||
* integer, giving a two-place answer;
|
||||
*
|
||||
* ``c_0[:] division of a two-place integer by a one-place integer,
|
||||
* provided that the quotient is a one-place integer, and yielding
|
||||
* also a one-place remainder.''
|
||||
*
|
||||
* I also missed his note that ``[b]y adjusting the word size, if
|
||||
* necessary, nearly all computers will have these three operations
|
||||
* available'', so I gave up on trying to use algorithms similar to his.
|
||||
* A future version of the library might include such algorithms; I
|
||||
* would welcome contributions from others for this.
|
||||
*
|
||||
* I eventually decided to use bit-shifting algorithms. To multiply `a'
|
||||
* and `b', we zero out the result. Then, for each `1' bit in `a', we
|
||||
* shift `b' left the appropriate amount and add it to the result.
|
||||
* Similarly, to divide `a' by `b', we shift `b' left varying amounts,
|
||||
* repeatedly trying to subtract it from `a'. When we succeed, we note
|
||||
* the fact by setting a bit in the quotient. While these algorithms
|
||||
* have the same O(n^2) time complexity as Knuth's, the ``constant factor''
|
||||
* is likely to be larger.
|
||||
*
|
||||
* Because I used these algorithms, which require single-block addition
|
||||
* and subtraction rather than single-block multiplication and division,
|
||||
* the innermost loops of all four routines are very similar. Study one
|
||||
* of them and all will become clear.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a little inline function used by both the multiplication
|
||||
* routine and the division routine.
|
||||
*
|
||||
* `getShiftedBlock' returns the `x'th block of `num << y'.
|
||||
* `y' may be anything from 0 to N - 1, and `x' may be anything from
|
||||
* 0 to `num.len'.
|
||||
*
|
||||
* Two things contribute to this block:
|
||||
*
|
||||
* (1) The `N - y' low bits of `num.blk[x]', shifted `y' bits left.
|
||||
*
|
||||
* (2) The `y' high bits of `num.blk[x-1]', shifted `N - y' bits right.
|
||||
*
|
||||
* But we must be careful if `x == 0' or `x == num.len', in
|
||||
* which case we should use 0 instead of (2) or (1), respectively.
|
||||
*
|
||||
* If `y == 0', then (2) contributes 0, as it should. However,
|
||||
* in some computer environments, for a reason I cannot understand,
|
||||
* `a >> b' means `a >> (b % N)'. This means `num.blk[x-1] >> (N - y)'
|
||||
* will return `num.blk[x-1]' instead of the desired 0 when `y == 0';
|
||||
* the test `y == 0' handles this case specially.
|
||||
*/
|
||||
inline BigUnsigned::Blk getShiftedBlock(const BigUnsigned &num,
|
||||
BigUnsigned::Index x, unsigned int y) {
|
||||
BigUnsigned::Blk part1 = (x == 0 || y == 0) ? 0 : (num.blk[x - 1] >> (BigUnsigned::N - y));
|
||||
BigUnsigned::Blk part2 = (x == num.len) ? 0 : (num.blk[x] << y);
|
||||
return part1 | part2;
|
||||
}
|
||||
|
||||
void BigUnsigned::multiply(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, multiply(a, b));
|
||||
// If either a or b is zero, set to zero.
|
||||
if (a.len == 0 || b.len == 0) {
|
||||
len = 0;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Overall method:
|
||||
*
|
||||
* Set this = 0.
|
||||
* For each 1-bit of `a' (say the `i2'th bit of block `i'):
|
||||
* Add `b << (i blocks and i2 bits)' to *this.
|
||||
*/
|
||||
// Variables for the calculation
|
||||
Index i, j, k;
|
||||
unsigned int i2;
|
||||
Blk temp;
|
||||
bool carryIn, carryOut;
|
||||
// Set preliminary length and make room
|
||||
len = a.len + b.len;
|
||||
allocate(len);
|
||||
// Zero out this object
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = 0;
|
||||
// For each block of the first number...
|
||||
for (i = 0; i < a.len; i++) {
|
||||
// For each 1-bit of that block...
|
||||
for (i2 = 0; i2 < N; i2++) {
|
||||
if ((a.blk[i] & (Blk(1) << i2)) == 0)
|
||||
continue;
|
||||
/*
|
||||
* Add b to this, shifted left i blocks and i2 bits.
|
||||
* j is the index in b, and k = i + j is the index in this.
|
||||
*
|
||||
* `getShiftedBlock', a short inline function defined above,
|
||||
* is now used for the bit handling. It replaces the more
|
||||
* complex `bHigh' code, in which each run of the loop dealt
|
||||
* immediately with the low bits and saved the high bits to
|
||||
* be picked up next time. The last run of the loop used to
|
||||
* leave leftover high bits, which were handled separately.
|
||||
* Instead, this loop runs an additional time with j == b.len.
|
||||
* These changes were made on 2005.01.11.
|
||||
*/
|
||||
for (j = 0, k = i, carryIn = false; j <= b.len; j++, k++) {
|
||||
/*
|
||||
* The body of this loop is very similar to the body of the first loop
|
||||
* in `add', except that this loop does a `+=' instead of a `+'.
|
||||
*/
|
||||
temp = blk[k] + getShiftedBlock(b, j, i2);
|
||||
carryOut = (temp < blk[k]);
|
||||
if (carryIn) {
|
||||
temp++;
|
||||
carryOut |= (temp == 0);
|
||||
}
|
||||
blk[k] = temp;
|
||||
carryIn = carryOut;
|
||||
}
|
||||
// No more extra iteration to deal with `bHigh'.
|
||||
// Roll-over a carry as necessary.
|
||||
for (; carryIn; k++) {
|
||||
blk[k]++;
|
||||
carryIn = (blk[k] == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Zap possible leading zero
|
||||
if (blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
/*
|
||||
* DIVISION WITH REMAINDER
|
||||
* This monstrous function mods *this by the given divisor b while storing the
|
||||
* quotient in the given object q; at the end, *this contains the remainder.
|
||||
* The seemingly bizarre pattern of inputs and outputs was chosen so that the
|
||||
* function copies as little as possible (since it is implemented by repeated
|
||||
* subtraction of multiples of b from *this).
|
||||
*
|
||||
* "modWithQuotient" might be a better name for this function, but I would
|
||||
* rather not change the name now.
|
||||
*/
|
||||
void BigUnsigned::divideWithRemainder(const BigUnsigned &b, BigUnsigned &q) {
|
||||
/* Defending against aliased calls is more complex than usual because we
|
||||
* are writing to both *this and q.
|
||||
*
|
||||
* It would be silly to try to write quotient and remainder to the
|
||||
* same variable. Rule that out right away. */
|
||||
if (this == &q)
|
||||
throw "BigUnsigned::divideWithRemainder: Cannot write quotient and remainder into the same variable";
|
||||
/* Now *this and q are separate, so the only concern is that b might be
|
||||
* aliased to one of them. If so, use a temporary copy of b. */
|
||||
if (this == &b || &q == &b) {
|
||||
BigUnsigned tmpB(b);
|
||||
divideWithRemainder(tmpB, q);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Knuth's definition of mod (which this function uses) is somewhat
|
||||
* different from the C++ definition of % in case of division by 0.
|
||||
*
|
||||
* We let a / 0 == 0 (it doesn't matter much) and a % 0 == a, no
|
||||
* exceptions thrown. This allows us to preserve both Knuth's demand
|
||||
* that a mod 0 == a and the useful property that
|
||||
* (a / b) * b + (a % b) == a.
|
||||
*/
|
||||
if (b.len == 0) {
|
||||
q.len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If *this.len < b.len, then *this < b, and we can be sure that b doesn't go into
|
||||
* *this at all. The quotient is 0 and *this is already the remainder (so leave it alone).
|
||||
*/
|
||||
if (len < b.len) {
|
||||
q.len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we know (*this).len >= b.len > 0. (Whew!)
|
||||
|
||||
/*
|
||||
* Overall method:
|
||||
*
|
||||
* For each appropriate i and i2, decreasing:
|
||||
* Subtract (b << (i blocks and i2 bits)) from *this, storing the
|
||||
* result in subtractBuf.
|
||||
* If the subtraction succeeds with a nonnegative result:
|
||||
* Turn on bit i2 of block i of the quotient q.
|
||||
* Copy subtractBuf back into *this.
|
||||
* Otherwise bit i2 of block i remains off, and *this is unchanged.
|
||||
*
|
||||
* Eventually q will contain the entire quotient, and *this will
|
||||
* be left with the remainder.
|
||||
*
|
||||
* subtractBuf[x] corresponds to blk[x], not blk[x+i], since 2005.01.11.
|
||||
* But on a single iteration, we don't touch the i lowest blocks of blk
|
||||
* (and don't use those of subtractBuf) because these blocks are
|
||||
* unaffected by the subtraction: we are subtracting
|
||||
* (b << (i blocks and i2 bits)), which ends in at least `i' zero
|
||||
* blocks. */
|
||||
// Variables for the calculation
|
||||
Index i, j, k;
|
||||
unsigned int i2;
|
||||
Blk temp;
|
||||
bool borrowIn, borrowOut;
|
||||
|
||||
/*
|
||||
* Make sure we have an extra zero block just past the value.
|
||||
*
|
||||
* When we attempt a subtraction, we might shift `b' so
|
||||
* its first block begins a few bits left of the dividend,
|
||||
* and then we'll try to compare these extra bits with
|
||||
* a nonexistent block to the left of the dividend. The
|
||||
* extra zero block ensures sensible behavior; we need
|
||||
* an extra block in `subtractBuf' for exactly the same reason.
|
||||
*/
|
||||
Index origLen = len; // Save real length.
|
||||
/* To avoid an out-of-bounds access in case of reallocation, allocate
|
||||
* first and then increment the logical length. */
|
||||
allocateAndCopy(len + 1);
|
||||
len++;
|
||||
blk[origLen] = 0; // Zero the added block.
|
||||
|
||||
// subtractBuf holds part of the result of a subtraction; see above.
|
||||
Blk *subtractBuf = new Blk[len];
|
||||
|
||||
// Set preliminary length for quotient and make room
|
||||
q.len = origLen - b.len + 1;
|
||||
q.allocate(q.len);
|
||||
// Zero out the quotient
|
||||
for (i = 0; i < q.len; i++)
|
||||
q.blk[i] = 0;
|
||||
|
||||
// For each possible left-shift of b in blocks...
|
||||
i = q.len;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
// For each possible left-shift of b in bits...
|
||||
// (Remember, N is the number of bits in a Blk.)
|
||||
q.blk[i] = 0;
|
||||
i2 = N;
|
||||
while (i2 > 0) {
|
||||
i2--;
|
||||
/*
|
||||
* Subtract b, shifted left i blocks and i2 bits, from *this,
|
||||
* and store the answer in subtractBuf. In the for loop, `k == i + j'.
|
||||
*
|
||||
* Compare this to the middle section of `multiply'. They
|
||||
* are in many ways analogous. See especially the discussion
|
||||
* of `getShiftedBlock'.
|
||||
*/
|
||||
for (j = 0, k = i, borrowIn = false; j <= b.len; j++, k++) {
|
||||
temp = blk[k] - getShiftedBlock(b, j, i2);
|
||||
borrowOut = (temp > blk[k]);
|
||||
if (borrowIn) {
|
||||
borrowOut |= (temp == 0);
|
||||
temp--;
|
||||
}
|
||||
// Since 2005.01.11, indices of `subtractBuf' directly match those of `blk', so use `k'.
|
||||
subtractBuf[k] = temp;
|
||||
borrowIn = borrowOut;
|
||||
}
|
||||
// No more extra iteration to deal with `bHigh'.
|
||||
// Roll-over a borrow as necessary.
|
||||
for (; k < origLen && borrowIn; k++) {
|
||||
borrowIn = (blk[k] == 0);
|
||||
subtractBuf[k] = blk[k] - 1;
|
||||
}
|
||||
/*
|
||||
* If the subtraction was performed successfully (!borrowIn),
|
||||
* set bit i2 in block i of the quotient.
|
||||
*
|
||||
* Then, copy the portion of subtractBuf filled by the subtraction
|
||||
* back to *this. This portion starts with block i and ends--
|
||||
* where? Not necessarily at block `i + b.len'! Well, we
|
||||
* increased k every time we saved a block into subtractBuf, so
|
||||
* the region of subtractBuf we copy is just [i, k).
|
||||
*/
|
||||
if (!borrowIn) {
|
||||
q.blk[i] |= (Blk(1) << i2);
|
||||
while (k > i) {
|
||||
k--;
|
||||
blk[k] = subtractBuf[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Zap possible leading zero in quotient
|
||||
if (q.blk[q.len - 1] == 0)
|
||||
q.len--;
|
||||
// Zap any/all leading zeros in remainder
|
||||
zapLeadingZeros();
|
||||
// Deallocate subtractBuf.
|
||||
// (Thanks to Brad Spencer for noticing my accidental omission of this!)
|
||||
delete [] subtractBuf;
|
||||
}
|
||||
|
||||
/* BITWISE OPERATORS
|
||||
* These are straightforward blockwise operations except that they differ in
|
||||
* the output length and the necessity of zapLeadingZeros. */
|
||||
|
||||
void BigUnsigned::bitAnd(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, bitAnd(a, b));
|
||||
// The bitwise & can't be longer than either operand.
|
||||
len = (a.len >= b.len) ? b.len : a.len;
|
||||
allocate(len);
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = a.blk[i] & b.blk[i];
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
void BigUnsigned::bitOr(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, bitOr(a, b));
|
||||
Index i;
|
||||
const BigUnsigned *a2, *b2;
|
||||
if (a.len >= b.len) {
|
||||
a2 = &a;
|
||||
b2 = &b;
|
||||
} else {
|
||||
a2 = &b;
|
||||
b2 = &a;
|
||||
}
|
||||
allocate(a2->len);
|
||||
for (i = 0; i < b2->len; i++)
|
||||
blk[i] = a2->blk[i] | b2->blk[i];
|
||||
for (; i < a2->len; i++)
|
||||
blk[i] = a2->blk[i];
|
||||
len = a2->len;
|
||||
// Doesn't need zapLeadingZeros.
|
||||
}
|
||||
|
||||
void BigUnsigned::bitXor(const BigUnsigned &a, const BigUnsigned &b) {
|
||||
DTRT_ALIASED(this == &a || this == &b, bitXor(a, b));
|
||||
Index i;
|
||||
const BigUnsigned *a2, *b2;
|
||||
if (a.len >= b.len) {
|
||||
a2 = &a;
|
||||
b2 = &b;
|
||||
} else {
|
||||
a2 = &b;
|
||||
b2 = &a;
|
||||
}
|
||||
allocate(a2->len);
|
||||
for (i = 0; i < b2->len; i++)
|
||||
blk[i] = a2->blk[i] ^ b2->blk[i];
|
||||
for (; i < a2->len; i++)
|
||||
blk[i] = a2->blk[i];
|
||||
len = a2->len;
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
void BigUnsigned::bitShiftLeft(const BigUnsigned &a, int b) {
|
||||
DTRT_ALIASED(this == &a, bitShiftLeft(a, b));
|
||||
if (b < 0) {
|
||||
if (b << 1 == 0)
|
||||
throw "BigUnsigned::bitShiftLeft: "
|
||||
"Pathological shift amount not implemented";
|
||||
else {
|
||||
bitShiftRight(a, -b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Index shiftBlocks = b / N;
|
||||
unsigned int shiftBits = b % N;
|
||||
// + 1: room for high bits nudged left into another block
|
||||
len = a.len + shiftBlocks + 1;
|
||||
allocate(len);
|
||||
Index i, j;
|
||||
for (i = 0; i < shiftBlocks; i++)
|
||||
blk[i] = 0;
|
||||
for (j = 0, i = shiftBlocks; j <= a.len; j++, i++)
|
||||
blk[i] = getShiftedBlock(a, j, shiftBits);
|
||||
// Zap possible leading zero
|
||||
if (blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
void BigUnsigned::bitShiftRight(const BigUnsigned &a, int b) {
|
||||
DTRT_ALIASED(this == &a, bitShiftRight(a, b));
|
||||
if (b < 0) {
|
||||
if (b << 1 == 0)
|
||||
throw "BigUnsigned::bitShiftRight: "
|
||||
"Pathological shift amount not implemented";
|
||||
else {
|
||||
bitShiftLeft(a, -b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This calculation is wacky, but expressing the shift as a left bit shift
|
||||
// within each block lets us use getShiftedBlock.
|
||||
Index rightShiftBlocks = (b + N - 1) / N;
|
||||
unsigned int leftShiftBits = N * rightShiftBlocks - b;
|
||||
// Now (N * rightShiftBlocks - leftShiftBits) == b
|
||||
// and 0 <= leftShiftBits < N.
|
||||
if (rightShiftBlocks >= a.len + 1) {
|
||||
// All of a is guaranteed to be shifted off, even considering the left
|
||||
// bit shift.
|
||||
len = 0;
|
||||
return;
|
||||
}
|
||||
// Now we're allocating a positive amount.
|
||||
// + 1: room for high bits nudged left into another block
|
||||
len = a.len + 1 - rightShiftBlocks;
|
||||
allocate(len);
|
||||
Index i, j;
|
||||
for (j = rightShiftBlocks, i = 0; j <= a.len; j++, i++)
|
||||
blk[i] = getShiftedBlock(a, j, leftShiftBits);
|
||||
// Zap possible leading zero
|
||||
if (blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
// INCREMENT/DECREMENT OPERATORS
|
||||
|
||||
// Prefix increment
|
||||
void BigUnsigned::operator ++() {
|
||||
Index i;
|
||||
bool carry = true;
|
||||
for (i = 0; i < len && carry; i++) {
|
||||
blk[i]++;
|
||||
carry = (blk[i] == 0);
|
||||
}
|
||||
if (carry) {
|
||||
// Allocate and then increase length, as in divideWithRemainder
|
||||
allocateAndCopy(len + 1);
|
||||
len++;
|
||||
blk[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Postfix increment: same as prefix
|
||||
void BigUnsigned::operator ++(int) {
|
||||
operator ++();
|
||||
}
|
||||
|
||||
// Prefix decrement
|
||||
void BigUnsigned::operator --() {
|
||||
if (len == 0)
|
||||
throw "BigUnsigned::operator --(): Cannot decrement an unsigned zero";
|
||||
Index i;
|
||||
bool borrow = true;
|
||||
for (i = 0; borrow; i++) {
|
||||
borrow = (blk[i] == 0);
|
||||
blk[i]--;
|
||||
}
|
||||
// Zap possible leading zero (there can only be one)
|
||||
if (blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
// Postfix decrement: same as prefix
|
||||
void BigUnsigned::operator --(int) {
|
||||
operator --();
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
#ifndef BIGUNSIGNED_H
|
||||
#define BIGUNSIGNED_H
|
||||
|
||||
#include "NumberlikeArray.hh"
|
||||
|
||||
/* A BigUnsigned object represents a nonnegative integer of size limited only by
|
||||
* available memory. BigUnsigneds support most mathematical operators and can
|
||||
* be converted to and from most primitive integer types.
|
||||
*
|
||||
* The number is stored as a NumberlikeArray of unsigned longs as if it were
|
||||
* written in base 256^sizeof(unsigned long). The least significant block is
|
||||
* first, and the length is such that the most significant block is nonzero. */
|
||||
class BigUnsigned : protected NumberlikeArray<unsigned long> {
|
||||
|
||||
public:
|
||||
// Enumeration for the result of a comparison.
|
||||
enum CmpRes { less = -1, equal = 0, greater = 1 };
|
||||
|
||||
// BigUnsigneds are built with a Blk type of unsigned long.
|
||||
typedef unsigned long Blk;
|
||||
|
||||
typedef NumberlikeArray<Blk>::Index Index;
|
||||
using NumberlikeArray<Blk>::N;
|
||||
|
||||
protected:
|
||||
// Creates a BigUnsigned with a capacity; for internal use.
|
||||
BigUnsigned(int, Index c) : NumberlikeArray<Blk>(0, c) {}
|
||||
|
||||
// Decreases len to eliminate any leading zero blocks.
|
||||
void zapLeadingZeros() {
|
||||
while (len > 0 && blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructs zero.
|
||||
BigUnsigned() : NumberlikeArray<Blk>() {}
|
||||
|
||||
// Copy constructor
|
||||
BigUnsigned(const BigUnsigned &x) : NumberlikeArray<Blk>(x) {}
|
||||
|
||||
// Assignment operator
|
||||
void operator=(const BigUnsigned &x) {
|
||||
NumberlikeArray<Blk>::operator =(x);
|
||||
}
|
||||
|
||||
// Constructor that copies from a given array of blocks.
|
||||
BigUnsigned(const Blk *b, Index blen) : NumberlikeArray<Blk>(b, blen) {
|
||||
// Eliminate any leading zeros we may have been passed.
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
// Destructor. NumberlikeArray does the delete for us.
|
||||
~BigUnsigned() {}
|
||||
|
||||
// Constructors from primitive integer types
|
||||
BigUnsigned(unsigned long x);
|
||||
BigUnsigned( long x);
|
||||
BigUnsigned(unsigned int x);
|
||||
BigUnsigned( int x);
|
||||
BigUnsigned(unsigned short x);
|
||||
BigUnsigned( short x);
|
||||
protected:
|
||||
// Helpers
|
||||
template <class X> void initFromPrimitive (X x);
|
||||
template <class X> void initFromSignedPrimitive(X x);
|
||||
public:
|
||||
|
||||
/* Converters to primitive integer types
|
||||
* The implicit conversion operators caused trouble, so these are now
|
||||
* named. */
|
||||
unsigned long toUnsignedLong () const;
|
||||
long toLong () const;
|
||||
unsigned int toUnsignedInt () const;
|
||||
int toInt () const;
|
||||
unsigned short toUnsignedShort() const;
|
||||
short toShort () const;
|
||||
protected:
|
||||
// Helpers
|
||||
template <class X> X convertToSignedPrimitive() const;
|
||||
template <class X> X convertToPrimitive () const;
|
||||
public:
|
||||
|
||||
// BIT/BLOCK ACCESSORS
|
||||
|
||||
// Expose these from NumberlikeArray directly.
|
||||
using NumberlikeArray<Blk>::getCapacity;
|
||||
using NumberlikeArray<Blk>::getLength;
|
||||
|
||||
/* Returns the requested block, or 0 if it is beyond the length (as if
|
||||
* the number had 0s infinitely to the left). */
|
||||
Blk getBlock(Index i) const { return i >= len ? 0 : blk[i]; }
|
||||
/* Sets the requested block. The number grows or shrinks as necessary. */
|
||||
void setBlock(Index i, Blk newBlock);
|
||||
|
||||
// The number is zero if and only if the canonical length is zero.
|
||||
bool isZero() const { return NumberlikeArray<Blk>::isEmpty(); }
|
||||
|
||||
/* Returns the length of the number in bits, i.e., zero if the number
|
||||
* is zero and otherwise one more than the largest value of bi for
|
||||
* which getBit(bi) returns true. */
|
||||
Index bitLength() const;
|
||||
/* Get the state of bit bi, which has value 2^bi. Bits beyond the
|
||||
* number's length are considered to be 0. */
|
||||
bool getBit(Index bi) const {
|
||||
return (getBlock(bi / N) & (Blk(1) << (bi % N))) != 0;
|
||||
}
|
||||
/* Sets the state of bit bi to newBit. The number grows or shrinks as
|
||||
* necessary. */
|
||||
void setBit(Index bi, bool newBit);
|
||||
|
||||
// COMPARISONS
|
||||
|
||||
// Compares this to x like Perl's <=>
|
||||
CmpRes compareTo(const BigUnsigned &x) const;
|
||||
|
||||
// Ordinary comparison operators
|
||||
bool operator ==(const BigUnsigned &x) const {
|
||||
return NumberlikeArray<Blk>::operator ==(x);
|
||||
}
|
||||
bool operator !=(const BigUnsigned &x) const {
|
||||
return NumberlikeArray<Blk>::operator !=(x);
|
||||
}
|
||||
bool operator < (const BigUnsigned &x) const { return compareTo(x) == less ; }
|
||||
bool operator <=(const BigUnsigned &x) const { return compareTo(x) != greater; }
|
||||
bool operator >=(const BigUnsigned &x) const { return compareTo(x) != less ; }
|
||||
bool operator > (const BigUnsigned &x) const { return compareTo(x) == greater; }
|
||||
|
||||
/*
|
||||
* BigUnsigned and BigInteger both provide three kinds of operators.
|
||||
* Here ``big-integer'' refers to BigInteger or BigUnsigned.
|
||||
*
|
||||
* (1) Overloaded ``return-by-value'' operators:
|
||||
* +, -, *, /, %, unary -, &, |, ^, <<, >>.
|
||||
* Big-integer code using these operators looks identical to code using
|
||||
* the primitive integer types. These operators take one or two
|
||||
* big-integer inputs and return a big-integer result, which can then
|
||||
* be assigned to a BigInteger variable or used in an expression.
|
||||
* Example:
|
||||
* BigInteger a(1), b = 1;
|
||||
* BigInteger c = a + b;
|
||||
*
|
||||
* (2) Overloaded assignment operators:
|
||||
* +=, -=, *=, /=, %=, flipSign, &=, |=, ^=, <<=, >>=, ++, --.
|
||||
* Again, these are used on big integers just like on ints. They take
|
||||
* one writable big integer that both provides an operand and receives a
|
||||
* result. Most also take a second read-only operand.
|
||||
* Example:
|
||||
* BigInteger a(1), b(1);
|
||||
* a += b;
|
||||
*
|
||||
* (3) Copy-less operations: `add', `subtract', etc.
|
||||
* These named methods take operands as arguments and store the result
|
||||
* in the receiver (*this), avoiding unnecessary copies and allocations.
|
||||
* `divideWithRemainder' is special: it both takes the dividend from and
|
||||
* stores the remainder into the receiver, and it takes a separate
|
||||
* object in which to store the quotient. NOTE: If you are wondering
|
||||
* why these don't return a value, you probably mean to use the
|
||||
* overloaded return-by-value operators instead.
|
||||
*
|
||||
* Examples:
|
||||
* BigInteger a(43), b(7), c, d;
|
||||
*
|
||||
* c = a + b; // Now c == 50.
|
||||
* c.add(a, b); // Same effect but without the two copies.
|
||||
*
|
||||
* c.divideWithRemainder(b, d);
|
||||
* // 50 / 7; now d == 7 (quotient) and c == 1 (remainder).
|
||||
*
|
||||
* // ``Aliased'' calls now do the right thing using a temporary
|
||||
* // copy, but see note on `divideWithRemainder'.
|
||||
* a.add(a, b);
|
||||
*/
|
||||
|
||||
// COPY-LESS OPERATIONS
|
||||
|
||||
// These 8: Arguments are read-only operands, result is saved in *this.
|
||||
void add(const BigUnsigned &a, const BigUnsigned &b);
|
||||
void subtract(const BigUnsigned &a, const BigUnsigned &b);
|
||||
void multiply(const BigUnsigned &a, const BigUnsigned &b);
|
||||
void bitAnd(const BigUnsigned &a, const BigUnsigned &b);
|
||||
void bitOr(const BigUnsigned &a, const BigUnsigned &b);
|
||||
void bitXor(const BigUnsigned &a, const BigUnsigned &b);
|
||||
/* Negative shift amounts translate to opposite-direction shifts,
|
||||
* except for -2^(8*sizeof(int)-1) which is unimplemented. */
|
||||
void bitShiftLeft(const BigUnsigned &a, int b);
|
||||
void bitShiftRight(const BigUnsigned &a, int b);
|
||||
|
||||
/* `a.divideWithRemainder(b, q)' is like `q = a / b, a %= b'.
|
||||
* / and % use semantics similar to Knuth's, which differ from the
|
||||
* primitive integer semantics under division by zero. See the
|
||||
* implementation in BigUnsigned.cc for details.
|
||||
* `a.divideWithRemainder(b, a)' throws an exception: it doesn't make
|
||||
* sense to write quotient and remainder into the same variable. */
|
||||
void divideWithRemainder(const BigUnsigned &b, BigUnsigned &q);
|
||||
|
||||
/* `divide' and `modulo' are no longer offered. Use
|
||||
* `divideWithRemainder' instead. */
|
||||
|
||||
// OVERLOADED RETURN-BY-VALUE OPERATORS
|
||||
BigUnsigned operator +(const BigUnsigned &x) const;
|
||||
BigUnsigned operator -(const BigUnsigned &x) const;
|
||||
BigUnsigned operator *(const BigUnsigned &x) const;
|
||||
BigUnsigned operator /(const BigUnsigned &x) const;
|
||||
BigUnsigned operator %(const BigUnsigned &x) const;
|
||||
/* OK, maybe unary minus could succeed in one case, but it really
|
||||
* shouldn't be used, so it isn't provided. */
|
||||
BigUnsigned operator &(const BigUnsigned &x) const;
|
||||
BigUnsigned operator |(const BigUnsigned &x) const;
|
||||
BigUnsigned operator ^(const BigUnsigned &x) const;
|
||||
BigUnsigned operator <<(int b) const;
|
||||
BigUnsigned operator >>(int b) const;
|
||||
|
||||
// OVERLOADED ASSIGNMENT OPERATORS
|
||||
void operator +=(const BigUnsigned &x);
|
||||
void operator -=(const BigUnsigned &x);
|
||||
void operator *=(const BigUnsigned &x);
|
||||
void operator /=(const BigUnsigned &x);
|
||||
void operator %=(const BigUnsigned &x);
|
||||
void operator &=(const BigUnsigned &x);
|
||||
void operator |=(const BigUnsigned &x);
|
||||
void operator ^=(const BigUnsigned &x);
|
||||
void operator <<=(int b);
|
||||
void operator >>=(int b);
|
||||
|
||||
/* INCREMENT/DECREMENT OPERATORS
|
||||
* To discourage messy coding, these do not return *this, so prefix
|
||||
* and postfix behave the same. */
|
||||
void operator ++( );
|
||||
void operator ++(int);
|
||||
void operator --( );
|
||||
void operator --(int);
|
||||
|
||||
// Helper function that needs access to BigUnsigned internals
|
||||
friend Blk getShiftedBlock(const BigUnsigned &num, Index x,
|
||||
unsigned int y);
|
||||
|
||||
// See BigInteger.cc.
|
||||
template <class X>
|
||||
friend X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a);
|
||||
};
|
||||
|
||||
/* Implementing the return-by-value and assignment operators in terms of the
|
||||
* copy-less operations. The copy-less operations are responsible for making
|
||||
* any necessary temporary copies to work around aliasing. */
|
||||
|
||||
inline BigUnsigned BigUnsigned::operator +(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.add(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator -(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.subtract(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator *(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.multiply(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator /(const BigUnsigned &x) const {
|
||||
if (x.isZero()) throw "BigUnsigned::operator /: division by zero";
|
||||
BigUnsigned q, r;
|
||||
r = *this;
|
||||
r.divideWithRemainder(x, q);
|
||||
return q;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator %(const BigUnsigned &x) const {
|
||||
if (x.isZero()) throw "BigUnsigned::operator %: division by zero";
|
||||
BigUnsigned q, r;
|
||||
r = *this;
|
||||
r.divideWithRemainder(x, q);
|
||||
return r;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator &(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.bitAnd(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator |(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.bitOr(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator ^(const BigUnsigned &x) const {
|
||||
BigUnsigned ans;
|
||||
ans.bitXor(*this, x);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator <<(int b) const {
|
||||
BigUnsigned ans;
|
||||
ans.bitShiftLeft(*this, b);
|
||||
return ans;
|
||||
}
|
||||
inline BigUnsigned BigUnsigned::operator >>(int b) const {
|
||||
BigUnsigned ans;
|
||||
ans.bitShiftRight(*this, b);
|
||||
return ans;
|
||||
}
|
||||
|
||||
inline void BigUnsigned::operator +=(const BigUnsigned &x) {
|
||||
add(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator -=(const BigUnsigned &x) {
|
||||
subtract(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator *=(const BigUnsigned &x) {
|
||||
multiply(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator /=(const BigUnsigned &x) {
|
||||
if (x.isZero()) throw "BigUnsigned::operator /=: division by zero";
|
||||
/* The following technique is slightly faster than copying *this first
|
||||
* when x is large. */
|
||||
BigUnsigned q;
|
||||
divideWithRemainder(x, q);
|
||||
// *this contains the remainder, but we overwrite it with the quotient.
|
||||
*this = q;
|
||||
}
|
||||
inline void BigUnsigned::operator %=(const BigUnsigned &x) {
|
||||
if (x.isZero()) throw "BigUnsigned::operator %=: division by zero";
|
||||
BigUnsigned q;
|
||||
// Mods *this by x. Don't care about quotient left in q.
|
||||
divideWithRemainder(x, q);
|
||||
}
|
||||
inline void BigUnsigned::operator &=(const BigUnsigned &x) {
|
||||
bitAnd(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator |=(const BigUnsigned &x) {
|
||||
bitOr(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator ^=(const BigUnsigned &x) {
|
||||
bitXor(*this, x);
|
||||
}
|
||||
inline void BigUnsigned::operator <<=(int b) {
|
||||
bitShiftLeft(*this, b);
|
||||
}
|
||||
inline void BigUnsigned::operator >>=(int b) {
|
||||
bitShiftRight(*this, b);
|
||||
}
|
||||
|
||||
/* Templates for conversions of BigUnsigned to and from primitive integers.
|
||||
* BigInteger.cc needs to instantiate convertToPrimitive, and the uses in
|
||||
* BigUnsigned.cc didn't do the trick; I think g++ inlined convertToPrimitive
|
||||
* instead of generating linkable instantiations. So for consistency, I put
|
||||
* all the templates here. */
|
||||
|
||||
// CONSTRUCTION FROM PRIMITIVE INTEGERS
|
||||
|
||||
/* Initialize this BigUnsigned from the given primitive integer. The same
|
||||
* pattern works for all primitive integer types, so I put it into a template to
|
||||
* reduce code duplication. (Don't worry: this is protected and we instantiate
|
||||
* it only with primitive integer types.) Type X could be signed, but x is
|
||||
* known to be nonnegative. */
|
||||
template <class X>
|
||||
void BigUnsigned::initFromPrimitive(X x) {
|
||||
if (x == 0)
|
||||
; // NumberlikeArray already initialized us to zero.
|
||||
else {
|
||||
// Create a single block. blk is NULL; no need to delete it.
|
||||
cap = 1;
|
||||
blk = new Blk[1];
|
||||
len = 1;
|
||||
blk[0] = Blk(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ditto, but first check that x is nonnegative. I could have put the check in
|
||||
* initFromPrimitive and let the compiler optimize it out for unsigned-type
|
||||
* instantiations, but I wanted to avoid the warning stupidly issued by g++ for
|
||||
* a condition that is constant in *any* instantiation, even if not in all. */
|
||||
template <class X>
|
||||
void BigUnsigned::initFromSignedPrimitive(X x) {
|
||||
if (x < 0)
|
||||
throw "BigUnsigned constructor: "
|
||||
"Cannot construct a BigUnsigned from a negative number";
|
||||
else
|
||||
initFromPrimitive(x);
|
||||
}
|
||||
|
||||
// CONVERSION TO PRIMITIVE INTEGERS
|
||||
|
||||
/* Template with the same idea as initFromPrimitive. This might be slightly
|
||||
* slower than the previous version with the masks, but it's much shorter and
|
||||
* clearer, which is the library's stated goal. */
|
||||
template <class X>
|
||||
X BigUnsigned::convertToPrimitive() const {
|
||||
if (len == 0)
|
||||
// The number is zero; return zero.
|
||||
return 0;
|
||||
else if (len == 1) {
|
||||
// The single block might fit in an X. Try the conversion.
|
||||
X x = X(blk[0]);
|
||||
// Make sure the result accurately represents the block.
|
||||
if (Blk(x) == blk[0])
|
||||
// Successful conversion.
|
||||
return x;
|
||||
// Otherwise fall through.
|
||||
}
|
||||
throw "BigUnsigned::to<Primitive>: "
|
||||
"Value is too big to fit in the requested type";
|
||||
}
|
||||
|
||||
/* Wrap the above in an x >= 0 test to make sure we got a nonnegative result,
|
||||
* not a negative one that happened to convert back into the correct nonnegative
|
||||
* one. (E.g., catch incorrect conversion of 2^31 to the long -2^31.) Again,
|
||||
* separated to avoid a g++ warning. */
|
||||
template <class X>
|
||||
X BigUnsigned::convertToSignedPrimitive() const {
|
||||
X x = convertToPrimitive<X>();
|
||||
if (x >= 0)
|
||||
return x;
|
||||
else
|
||||
throw "BigUnsigned::to(Primitive): "
|
||||
"Value is too big to fit in the requested type";
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
#include "BigUnsignedInABase.hh"
|
||||
|
||||
BigUnsignedInABase::BigUnsignedInABase(const Digit *d, Index l, Base base)
|
||||
: NumberlikeArray<Digit>(d, l), base(base) {
|
||||
// Check the base
|
||||
if (base < 2)
|
||||
throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): The base must be at least 2";
|
||||
|
||||
// Validate the digits.
|
||||
for (Index i = 0; i < l; i++)
|
||||
if (blk[i] >= base)
|
||||
throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base";
|
||||
|
||||
// Eliminate any leading zeros we may have been passed.
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
namespace {
|
||||
unsigned int bitLen(unsigned int x) {
|
||||
unsigned int len = 0;
|
||||
while (x > 0) {
|
||||
x >>= 1;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
unsigned int ceilingDiv(unsigned int a, unsigned int b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
}
|
||||
|
||||
BigUnsignedInABase::BigUnsignedInABase(const BigUnsigned &x, Base base) {
|
||||
// Check the base
|
||||
if (base < 2)
|
||||
throw "BigUnsignedInABase(BigUnsigned, Base): The base must be at least 2";
|
||||
this->base = base;
|
||||
|
||||
// Get an upper bound on how much space we need
|
||||
int maxBitLenOfX = x.getLength() * BigUnsigned::N;
|
||||
int minBitsPerDigit = bitLen(base) - 1;
|
||||
int maxDigitLenOfX = ceilingDiv(maxBitLenOfX, minBitsPerDigit);
|
||||
len = maxDigitLenOfX; // Another change to comply with `staying in bounds'.
|
||||
allocate(len); // Get the space
|
||||
|
||||
BigUnsigned x2(x), buBase(base);
|
||||
Index digitNum = 0;
|
||||
|
||||
while (!x2.isZero()) {
|
||||
// Get last digit. This is like `lastDigit = x2 % buBase, x2 /= buBase'.
|
||||
BigUnsigned lastDigit(x2);
|
||||
lastDigit.divideWithRemainder(buBase, x2);
|
||||
// Save the digit.
|
||||
blk[digitNum] = lastDigit.toUnsignedShort();
|
||||
// Move on. We can't run out of room: we figured it out above.
|
||||
digitNum++;
|
||||
}
|
||||
|
||||
// Save the actual length.
|
||||
len = digitNum;
|
||||
}
|
||||
|
||||
BigUnsignedInABase::operator BigUnsigned() const {
|
||||
BigUnsigned ans(0), buBase(base), temp;
|
||||
Index digitNum = len;
|
||||
while (digitNum > 0) {
|
||||
digitNum--;
|
||||
temp.multiply(ans, buBase);
|
||||
ans.add(temp, BigUnsigned(blk[digitNum]));
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
BigUnsignedInABase::BigUnsignedInABase(const std::string &s, Base base) {
|
||||
// Check the base.
|
||||
if (base > 36)
|
||||
throw "BigUnsignedInABase(std::string, Base): The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine.";
|
||||
// Save the base.
|
||||
// This pattern is seldom seen in C++, but the analogous ``this.'' is common in Java.
|
||||
this->base = base;
|
||||
|
||||
// `s.length()' is a `size_t', while `len' is a `NumberlikeArray::Index',
|
||||
// also known as an `unsigned int'. Some compilers warn without this cast.
|
||||
len = Index(s.length());
|
||||
allocate(len);
|
||||
|
||||
Index digitNum, symbolNumInString;
|
||||
for (digitNum = 0; digitNum < len; digitNum++) {
|
||||
symbolNumInString = len - 1 - digitNum;
|
||||
char theSymbol = s[symbolNumInString];
|
||||
if (theSymbol >= '0' && theSymbol <= '9')
|
||||
blk[digitNum] = theSymbol - '0';
|
||||
else if (theSymbol >= 'A' && theSymbol <= 'Z')
|
||||
blk[digitNum] = theSymbol - 'A' + 10;
|
||||
else if (theSymbol >= 'a' && theSymbol <= 'z')
|
||||
blk[digitNum] = theSymbol - 'a' + 10;
|
||||
else
|
||||
throw "BigUnsignedInABase(std::string, Base): Bad symbol in input. Only 0-9, A-Z, a-z are accepted.";
|
||||
|
||||
if (blk[digitNum] >= base)
|
||||
throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base";
|
||||
}
|
||||
zapLeadingZeros();
|
||||
}
|
||||
|
||||
BigUnsignedInABase::operator std::string() const {
|
||||
if (base > 36)
|
||||
throw "BigUnsignedInABase ==> std::string: The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine.";
|
||||
if (len == 0)
|
||||
return std::string("0");
|
||||
// Some compilers don't have push_back, so use a char * buffer instead.
|
||||
char *s = new char[len + 1];
|
||||
s[len] = '\0';
|
||||
Index digitNum, symbolNumInString;
|
||||
for (symbolNumInString = 0; symbolNumInString < len; symbolNumInString++) {
|
||||
digitNum = len - 1 - symbolNumInString;
|
||||
Digit theDigit = blk[digitNum];
|
||||
if (theDigit < 10)
|
||||
s[symbolNumInString] = char('0' + theDigit);
|
||||
else
|
||||
s[symbolNumInString] = char('A' + theDigit - 10);
|
||||
}
|
||||
std::string s2(s);
|
||||
delete [] s;
|
||||
return s2;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#ifndef BIGUNSIGNEDINABASE_H
|
||||
#define BIGUNSIGNEDINABASE_H
|
||||
|
||||
#include "NumberlikeArray.hh"
|
||||
#include "BigUnsigned.hh"
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
* A BigUnsignedInABase object represents a nonnegative integer of size limited
|
||||
* only by available memory, represented in a user-specified base that can fit
|
||||
* in an `unsigned short' (most can, and this saves memory).
|
||||
*
|
||||
* BigUnsignedInABase is intended as an intermediary class with little
|
||||
* functionality of its own. BigUnsignedInABase objects can be constructed
|
||||
* from, and converted to, BigUnsigneds (requiring multiplication, mods, etc.)
|
||||
* and `std::string's (by switching digit values for appropriate characters).
|
||||
*
|
||||
* BigUnsignedInABase is similar to BigUnsigned. Note the following:
|
||||
*
|
||||
* (1) They represent the number in exactly the same way, except that
|
||||
* BigUnsignedInABase uses ``digits'' (or Digit) where BigUnsigned uses
|
||||
* ``blocks'' (or Blk).
|
||||
*
|
||||
* (2) Both use the management features of NumberlikeArray. (In fact, my desire
|
||||
* to add a BigUnsignedInABase class without duplicating a lot of code led me to
|
||||
* introduce NumberlikeArray.)
|
||||
*
|
||||
* (3) The only arithmetic operation supported by BigUnsignedInABase is an
|
||||
* equality test. Use BigUnsigned for arithmetic.
|
||||
*/
|
||||
|
||||
class BigUnsignedInABase : protected NumberlikeArray<unsigned short> {
|
||||
|
||||
public:
|
||||
// The digits of a BigUnsignedInABase are unsigned shorts.
|
||||
typedef unsigned short Digit;
|
||||
// That's also the type of a base.
|
||||
typedef Digit Base;
|
||||
|
||||
protected:
|
||||
// The base in which this BigUnsignedInABase is expressed
|
||||
Base base;
|
||||
|
||||
// Creates a BigUnsignedInABase with a capacity; for internal use.
|
||||
BigUnsignedInABase(int, Index c) : NumberlikeArray<Digit>(0, c) {}
|
||||
|
||||
// Decreases len to eliminate any leading zero digits.
|
||||
void zapLeadingZeros() {
|
||||
while (len > 0 && blk[len - 1] == 0)
|
||||
len--;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructs zero in base 2.
|
||||
BigUnsignedInABase() : NumberlikeArray<Digit>(), base(2) {}
|
||||
|
||||
// Copy constructor
|
||||
BigUnsignedInABase(const BigUnsignedInABase &x) : NumberlikeArray<Digit>(x), base(x.base) {}
|
||||
|
||||
// Assignment operator
|
||||
void operator =(const BigUnsignedInABase &x) {
|
||||
NumberlikeArray<Digit>::operator =(x);
|
||||
base = x.base;
|
||||
}
|
||||
|
||||
// Constructor that copies from a given array of digits.
|
||||
BigUnsignedInABase(const Digit *d, Index l, Base base);
|
||||
|
||||
// Destructor. NumberlikeArray does the delete for us.
|
||||
~BigUnsignedInABase() {}
|
||||
|
||||
// LINKS TO BIGUNSIGNED
|
||||
BigUnsignedInABase(const BigUnsigned &x, Base base);
|
||||
operator BigUnsigned() const;
|
||||
|
||||
/* LINKS TO STRINGS
|
||||
*
|
||||
* These use the symbols ``0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'' to
|
||||
* represent digits of 0 through 35. When parsing strings, lowercase is
|
||||
* also accepted.
|
||||
*
|
||||
* All string representations are big-endian (big-place-value digits
|
||||
* first). (Computer scientists have adopted zero-based counting; why
|
||||
* can't they tolerate little-endian numbers?)
|
||||
*
|
||||
* No string representation has a ``base indicator'' like ``0x''.
|
||||
*
|
||||
* An exception is made for zero: it is converted to ``0'' and not the
|
||||
* empty string.
|
||||
*
|
||||
* If you want different conventions, write your own routines to go
|
||||
* between BigUnsignedInABase and strings. It's not hard.
|
||||
*/
|
||||
operator std::string() const;
|
||||
BigUnsignedInABase(const std::string &s, Base base);
|
||||
|
||||
public:
|
||||
|
||||
// ACCESSORS
|
||||
Base getBase() const { return base; }
|
||||
|
||||
// Expose these from NumberlikeArray directly.
|
||||
using NumberlikeArray<Digit>::getCapacity;
|
||||
using NumberlikeArray<Digit>::getLength;
|
||||
|
||||
/* Returns the requested digit, or 0 if it is beyond the length (as if
|
||||
* the number had 0s infinitely to the left). */
|
||||
Digit getDigit(Index i) const { return i >= len ? 0 : blk[i]; }
|
||||
|
||||
// The number is zero if and only if the canonical length is zero.
|
||||
bool isZero() const { return NumberlikeArray<Digit>::isEmpty(); }
|
||||
|
||||
/* Equality test. For the purposes of this test, two BigUnsignedInABase
|
||||
* values must have the same base to be equal. */
|
||||
bool operator ==(const BigUnsignedInABase &x) const {
|
||||
return base == x.base && NumberlikeArray<Digit>::operator ==(x);
|
||||
}
|
||||
bool operator !=(const BigUnsignedInABase &x) const { return !operator ==(x); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,146 @@
|
|||
Change Log
|
||||
|
||||
These entries tell you what was added, fixed, or improved in each version as
|
||||
compared to the previous one. In case you haven't noticed, a version number
|
||||
roughly corresponds to the release date of that version in `YYYY.MM.DD[.N]'
|
||||
format, where `.N' goes `.2', `.3', etc. if there are multiple versions on the
|
||||
same day. The topmost version listed is the one you have.
|
||||
|
||||
2010.04.30
|
||||
----------
|
||||
- Strengthen the advice about build/IDE configuration in the README.
|
||||
|
||||
2009.05.03
|
||||
----------
|
||||
- BigUnsigned::{get,set}Bit: Change two remaining `1 <<' to `Blk(1) <<' to work
|
||||
on systems where sizeof(unsigned int) != sizeof(Blk). Bug reported by Brad
|
||||
Spencer.
|
||||
- dataToBigInteger: Change a `delete' to `delete []' to avoid leaking memory.
|
||||
Bug reported by Nicolás Carrasco.
|
||||
|
||||
2009.03.26
|
||||
----------
|
||||
- BigUnsignedInABase(std::string) Reject digits too big for the base.
|
||||
Bug reported by Niakam Kazemi.
|
||||
|
||||
2008.07.20
|
||||
----------
|
||||
Dennis Yew pointed out serious problems with ambiguities and unwanted
|
||||
conversions when mixing BigInteger/BigUnsigned and primitive integers. To fix
|
||||
these, I removed the implicit conversions from BigInteger/BigUnsigned to
|
||||
primitive integers and from BigInteger to BigUnsigned. Removing the
|
||||
BigInteger-to-BigUnsigned conversion required changing BigInteger to have a
|
||||
BigUnsigned field instead of inheriting from it; this was a complex task but
|
||||
ultimately gave a saner design. At the same time, I went through the entire
|
||||
codebase, making the formatting and comments prettier and reworking anything I
|
||||
thought was unclear. I also added a testsuite (currently for 32-bit systems
|
||||
only); it doesn't yet cover the entire library but should help to ensure that
|
||||
things work the way they should.
|
||||
|
||||
A number of changes from version 2007.07.07 break compatibility with existing
|
||||
code that uses the library, but updating that code should be pretty easy:
|
||||
- BigInteger can no longer be implicitly converted to BigUnsigned. Use
|
||||
getMagnitude() instead.
|
||||
- BigUnsigned and BigInteger can no longer be implicitly converted to primitive
|
||||
integers. Use the toInt() family of functions instead.
|
||||
- The easy* functions have been renamed to more mature names:
|
||||
bigUnsignedToString, bigIntegerToString, stringToBigUnsigned,
|
||||
stringToBigInteger, dataToBigInteger.
|
||||
- BigInteger no longer supports bitwise operations. Get the magnitude with
|
||||
getMagnitude() and operate on that instead.
|
||||
- The old {BigUnsigned,BigInteger}::{divide,modulo} copy-less options have been
|
||||
removed. Use divideWithRemainder instead.
|
||||
- Added a base argument to BigUnsignedInABase's digit-array constructor. I
|
||||
ope no one used that constructor in its broken state anyway.
|
||||
|
||||
Other notable changes:
|
||||
- Added BigUnsigned functions setBlock, bitLength, getBit, setBit.
|
||||
- The bit-shifting operations now support negative shift amounts, which shift in
|
||||
the other direction.
|
||||
- Added some big-integer algorithms in BigIntegerAlgorithms.hh: gcd,
|
||||
extendedEuclidean, modinv, modexp.
|
||||
|
||||
2007.07.07
|
||||
----------
|
||||
Update the "Running the sample program produces this output:" comment in
|
||||
sample.cc for the bitwise operators.
|
||||
|
||||
2007.06.14
|
||||
----------
|
||||
- Implement << and >> for BigUnsigned in response to email from Marco Schulze.
|
||||
- Fix name: DOTR_ALIASED -> DTRT_ALIASED.
|
||||
- Demonstrate all bitwise operators (&, |, ^, <<, >>) in sample.cc.
|
||||
|
||||
2007.02.16
|
||||
----------
|
||||
Boris Dessy pointed out that the library threw an exception on "a *= a", so I changed all the put-here operations to handle aliased calls correctly using a temporary copy instead of throwing exceptions.
|
||||
|
||||
2006.08.14
|
||||
----------
|
||||
In BigUnsigned::bitXor, change allocate(b2->len) to allocate(a2->len): we should allocate enough space for the longer number, not the shorter one! Thanks to Sriram Sankararaman for pointing this out.
|
||||
|
||||
2006.05.03
|
||||
----------
|
||||
I ran the sample program using valgrind and discovered a `delete s' that should be `delete [] s' and a `len++' before an `allocateAndCopy(len)' that should have been after an `allocateAndCopy(len + 1)'. I fixed both. Yay for valgrind!
|
||||
|
||||
2006.05.01
|
||||
----------
|
||||
I fixed incorrect results reported by Mohand Mezmaz and related memory corruption on platforms where Blk is bigger than int. I replaced (1 << x) with (Blk(1) << x) in two places in BigUnsigned.cc.
|
||||
|
||||
2006.04.24
|
||||
----------
|
||||
Two bug fixes: BigUnsigned "++x" no longer segfaults when x grows in length, and BigUnsigned == and != are now redeclared so as to be usable. I redid the Makefile: I removed the *.tag mechanism and hard-coded the library's header dependencies, I added comments, and I made the Makefile more useful for building one's own programs instead of just the sample.
|
||||
|
||||
2006.02.26
|
||||
----------
|
||||
A few tweaks in preparation for a group to distribute the library. The project Web site has moved; I updated the references. I fixed a typo and added a missing function in NumberlikeArray.hh. I'm using Eclipse now, so you get Eclipse project files.
|
||||
|
||||
2005.03.30
|
||||
----------
|
||||
Sam Larkin found a bug in `BigInteger::subtract'; I fixed it.
|
||||
|
||||
2005.01.18
|
||||
----------
|
||||
I fixed some problems with `easyDataToBI'. Due to some multiply declared variables, this function would not compile. However, it is a template function, so the compiler parses it and doesn't compile the parsed representation until something uses the function; this is how I missed the problems. I also removed debugging output from this function.
|
||||
|
||||
2005.01.17
|
||||
----------
|
||||
A fix to some out-of-bounds accesses reported by Milan Tomic (see the comment under `BigUnsigned::divideWithRemainder'). `BigUnsigned::multiply' and `BigUnsigned::divideWithRemainder' implementations neatened up a bit with the help of a function `getShiftedBlock'. I (finally!) introduced a constant `BigUnsigned::N', the number of bits in a `BigUnsigned::Blk', which varies depending on machine word size. In both code and comments, it replaces the much clunkier `8*sizeof(Blk)'. Numerous other small changes. There's a new conversion routine `easyDataToBI' that will convert almost any format of binary data to a `BigInteger'.
|
||||
|
||||
I have inserted a significant number of new comments. Most explain unobvious aspects of the code.
|
||||
|
||||
2005.01.06
|
||||
----------
|
||||
Some changes to the way zero-length arrays are handled by `NumberlikeArray', which fixed a memory leak reported by Milan Tomic.
|
||||
|
||||
2004.12.24.2
|
||||
------------
|
||||
I tied down a couple of loose ends involving division/modulo. I added an explanation of put-here vs. overloaded operators in the sample program; this has confused too many people. Miscellaneous other improvements.
|
||||
|
||||
I believe that, at this point, the Big Integer Library makes no assumptions about the word size of the machine it is using. `BigUnsigned::Blk' is always an `unsigned long', whatever that may be, and its size is computed with `sizeof' when necessary. However, just in case, I would be interested to have someone test the library on a non-32-bit machine to see if it works.
|
||||
|
||||
2004.12.24
|
||||
----------
|
||||
This is a _major_ upgrade to the library. Among the things that have changed:
|
||||
|
||||
I wrote the original version of the library, particularly the four ``classical algorithms'' in `BigUnsigned.cc', using array indexing. Then I rewrote it to use pointers because I thought that would be faster. But recently, I revisited the code in `BigUnsigned.cc' and found that I could not begin to understand what it was doing.
|
||||
|
||||
I have decided that the drawbacks of pointers, increased coding difficulty and reduced code readability, far outweigh their speed benefits. Plus, any modern optimizing compiler should produce fast code either way. Therefore, I rewrote the library to use array indexing again. (Thank goodness for regular-expression find-and-replace. It saved me a lot of time.)
|
||||
|
||||
The put-here operations `divide' and `modulo' of each of `BigUnsigned' and `BigInteger' have been supplanted by a single operation `divideWithRemainder'. Read the profuse comments for more information on its exact behavior.
|
||||
|
||||
There is a new class `BigUnsignedInABase' that is like `BigUnsigned' but uses a user-specified, small base instead of `256 ^ sizeof(unsigned long)'. Much of the code common to the two has been factored out into `NumberlikeArray'.
|
||||
|
||||
`BigUnsignedInABase' facilitates conversion between `BigUnsigned's and digit-by-digit string representations using `std::string'. Convenience routines to do this conversion are in `BigIntegerUtils.hh'. `iostream' compatibility has been improved.
|
||||
|
||||
I would like to thank Chris Morbitzer for the e-mail message that catalyzed this major upgrade. He wanted a way to convert a string to a BigInteger. One thing just led to another, roughly in reverse order from how they are listed here.
|
||||
|
||||
2004.1216
|
||||
---------
|
||||
Brad Spencer pointed out a memory leak in `BigUnsigned::divide'. It is fixed in the December 16, 2004 version.
|
||||
|
||||
2004.1205
|
||||
---------
|
||||
After months of inactivity, I fixed a bug in the `BigInteger' division routine; thanks to David Allen for reporting the bug. I also added simple routines for decimal output to `std::ostream's, and there is a demo that prints out powers of 3.
|
||||
|
||||
~~~~
|
|
@ -0,0 +1,73 @@
|
|||
# Mention default target.
|
||||
all:
|
||||
|
||||
# Implicit rule to compile C++ files. Modify to your taste.
|
||||
%.o: %.cc
|
||||
g++ -c -O2 -Wall -Wextra -pedantic $<
|
||||
|
||||
# Components of the library.
|
||||
library-objects = \
|
||||
BigUnsigned.o \
|
||||
BigInteger.o \
|
||||
BigIntegerAlgorithms.o \
|
||||
BigUnsignedInABase.o \
|
||||
BigIntegerUtils.o \
|
||||
|
||||
library-headers = \
|
||||
NumberlikeArray.hh \
|
||||
BigUnsigned.hh \
|
||||
BigInteger.hh \
|
||||
BigIntegerAlgorithms.hh \
|
||||
BigUnsignedInABase.hh \
|
||||
BigIntegerLibrary.hh \
|
||||
|
||||
# To ``make the library'', make all its objects using the implicit rule.
|
||||
library: $(library-objects)
|
||||
|
||||
# Conservatively assume that all the objects depend on all the headers.
|
||||
$(library-objects): $(library-headers)
|
||||
|
||||
# TESTSUITE (NOTE: Currently expects a 32-bit system)
|
||||
# Compiling the testsuite.
|
||||
testsuite.o: $(library-headers)
|
||||
testsuite: testsuite.o $(library-objects)
|
||||
g++ $^ -o $@
|
||||
# Extract the expected output from the testsuite source.
|
||||
testsuite.expected: testsuite.cc
|
||||
nl -ba -p -s: $< | sed -nre 's,^ +([0-9]+):.*//([^ ]),Line \1: \2,p' >$@
|
||||
# Run the testsuite.
|
||||
.PHONY: test
|
||||
test: testsuite testsuite.expected
|
||||
./run-testsuite
|
||||
testsuite-cleanfiles = \
|
||||
testsuite.o testsuite testsuite.expected \
|
||||
testsuite.out testsuite.err
|
||||
|
||||
# The rules below build a program that uses the library. They are preset to
|
||||
# build ``sample'' from ``sample.cc''. You can change the name(s) of the
|
||||
# source file(s) and program file to build your own program, or you can write
|
||||
# your own Makefile.
|
||||
|
||||
# Components of the program.
|
||||
program = sample
|
||||
program-objects = sample.o
|
||||
|
||||
# Conservatively assume all the program source files depend on all the library
|
||||
# headers. You can change this if it is not the case.
|
||||
$(program-objects) : $(library-headers)
|
||||
|
||||
# How to link the program. The implicit rule covers individual objects.
|
||||
$(program) : $(program-objects) $(library-objects)
|
||||
g++ $^ -o $@
|
||||
|
||||
# Delete all generated files we know about.
|
||||
clean :
|
||||
rm -f $(library-objects) $(testsuite-cleanfiles) $(program-objects) $(program)
|
||||
|
||||
# I removed the *.tag dependency tracking system because it had few advantages
|
||||
# over manually entering all the dependencies. If there were a portable,
|
||||
# reliable dependency tracking system, I'd use it, but I know of no such;
|
||||
# cons and depcomp are almost good enough.
|
||||
|
||||
# Come back and define default target.
|
||||
all : library $(program)
|
|
@ -0,0 +1,177 @@
|
|||
#ifndef NUMBERLIKEARRAY_H
|
||||
#define NUMBERLIKEARRAY_H
|
||||
|
||||
// Make sure we have NULL.
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
/* A NumberlikeArray<Blk> object holds a heap-allocated array of Blk with a
|
||||
* length and a capacity and provides basic memory management features.
|
||||
* BigUnsigned and BigUnsignedInABase both subclass it.
|
||||
*
|
||||
* NumberlikeArray provides no information hiding. Subclasses should use
|
||||
* nonpublic inheritance and manually expose members as desired using
|
||||
* declarations like this:
|
||||
*
|
||||
* public:
|
||||
* NumberlikeArray< the-type-argument >::getLength;
|
||||
*/
|
||||
template <class Blk>
|
||||
class NumberlikeArray {
|
||||
public:
|
||||
|
||||
// Type for the index of a block in the array
|
||||
typedef unsigned int Index;
|
||||
// The number of bits in a block, defined below.
|
||||
static const unsigned int N;
|
||||
|
||||
// The current allocated capacity of this NumberlikeArray (in blocks)
|
||||
Index cap;
|
||||
// The actual length of the value stored in this NumberlikeArray (in blocks)
|
||||
Index len;
|
||||
// Heap-allocated array of the blocks (can be NULL if len == 0)
|
||||
Blk *blk;
|
||||
|
||||
// Constructs a ``zero'' NumberlikeArray with the given capacity.
|
||||
NumberlikeArray(Index c) : cap(c), len(0) {
|
||||
blk = (cap > 0) ? (new Blk[cap]) : NULL;
|
||||
}
|
||||
|
||||
/* Constructs a zero NumberlikeArray without allocating a backing array.
|
||||
* A subclass that doesn't know the needed capacity at initialization
|
||||
* time can use this constructor and then overwrite blk without first
|
||||
* deleting it. */
|
||||
NumberlikeArray() : cap(0), len(0) {
|
||||
blk = NULL;
|
||||
}
|
||||
|
||||
// Destructor. Note that `delete NULL' is a no-op.
|
||||
~NumberlikeArray() {
|
||||
delete [] blk;
|
||||
}
|
||||
|
||||
/* Ensures that the array has at least the requested capacity; may
|
||||
* destroy the contents. */
|
||||
void allocate(Index c);
|
||||
|
||||
/* Ensures that the array has at least the requested capacity; does not
|
||||
* destroy the contents. */
|
||||
void allocateAndCopy(Index c);
|
||||
|
||||
// Copy constructor
|
||||
NumberlikeArray(const NumberlikeArray<Blk> &x);
|
||||
|
||||
// Assignment operator
|
||||
void operator=(const NumberlikeArray<Blk> &x);
|
||||
|
||||
// Constructor that copies from a given array of blocks
|
||||
NumberlikeArray(const Blk *b, Index blen);
|
||||
|
||||
// ACCESSORS
|
||||
Index getCapacity() const { return cap; }
|
||||
Index getLength() const { return len; }
|
||||
Blk getBlock(Index i) const { return blk[i]; }
|
||||
bool isEmpty() const { return len == 0; }
|
||||
|
||||
/* Equality comparison: checks if both objects have the same length and
|
||||
* equal (==) array elements to that length. Subclasses may wish to
|
||||
* override. */
|
||||
bool operator ==(const NumberlikeArray<Blk> &x) const;
|
||||
|
||||
bool operator !=(const NumberlikeArray<Blk> &x) const {
|
||||
return !operator ==(x);
|
||||
}
|
||||
};
|
||||
|
||||
/* BEGIN TEMPLATE DEFINITIONS. They are present here so that source files that
|
||||
* include this header file can generate the necessary real definitions. */
|
||||
|
||||
template <class Blk>
|
||||
const unsigned int NumberlikeArray<Blk>::N = 8 * sizeof(Blk);
|
||||
|
||||
template <class Blk>
|
||||
void NumberlikeArray<Blk>::allocate(Index c) {
|
||||
// If the requested capacity is more than the current capacity...
|
||||
if (c > cap) {
|
||||
// Delete the old number array
|
||||
delete [] blk;
|
||||
// Allocate the new array
|
||||
cap = c;
|
||||
blk = new Blk[cap];
|
||||
}
|
||||
}
|
||||
|
||||
template <class Blk>
|
||||
void NumberlikeArray<Blk>::allocateAndCopy(Index c) {
|
||||
// If the requested capacity is more than the current capacity...
|
||||
if (c > cap) {
|
||||
Blk *oldBlk = blk;
|
||||
// Allocate the new number array
|
||||
cap = c;
|
||||
blk = new Blk[cap];
|
||||
// Copy number blocks
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = oldBlk[i];
|
||||
// Delete the old array
|
||||
delete [] oldBlk;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Blk>
|
||||
NumberlikeArray<Blk>::NumberlikeArray(const NumberlikeArray<Blk> &x)
|
||||
: len(x.len) {
|
||||
// Create array
|
||||
cap = len;
|
||||
blk = new Blk[cap];
|
||||
// Copy blocks
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = x.blk[i];
|
||||
}
|
||||
|
||||
template <class Blk>
|
||||
void NumberlikeArray<Blk>::operator=(const NumberlikeArray<Blk> &x) {
|
||||
/* Calls like a = a have no effect; catch them before the aliasing
|
||||
* causes a problem */
|
||||
if (this == &x)
|
||||
return;
|
||||
// Copy length
|
||||
len = x.len;
|
||||
// Expand array if necessary
|
||||
allocate(len);
|
||||
// Copy number blocks
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = x.blk[i];
|
||||
}
|
||||
|
||||
template <class Blk>
|
||||
NumberlikeArray<Blk>::NumberlikeArray(const Blk *b, Index blen)
|
||||
: cap(blen), len(blen) {
|
||||
// Create array
|
||||
blk = new Blk[cap];
|
||||
// Copy blocks
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
blk[i] = b[i];
|
||||
}
|
||||
|
||||
template <class Blk>
|
||||
bool NumberlikeArray<Blk>::operator ==(const NumberlikeArray<Blk> &x) const {
|
||||
if (len != x.len)
|
||||
// Definitely unequal.
|
||||
return false;
|
||||
else {
|
||||
// Compare corresponding blocks one by one.
|
||||
Index i;
|
||||
for (i = 0; i < len; i++)
|
||||
if (blk[i] != x.blk[i])
|
||||
return false;
|
||||
// No blocks differed, so the objects are equal.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
Note by Clifford Wolf:
|
||||
This version of bigint was downloaded at 2012-08-29 from
|
||||
https://mattmccutchen.net/bigint/bigint-2010.04.30.tar.bz2
|
||||
|
||||
Some minor changes were made to the source code (e.g. "using"
|
||||
was added to access declarations to prohibit compiler warnings).
|
||||
|
||||
|
||||
==============================================================================
|
||||
|
||||
C++ Big Integer Library
|
||||
(see ChangeLog for version)
|
||||
|
||||
http://mattmccutchen.net/bigint/
|
||||
|
||||
Written and maintained by Matt McCutchen <matt@mattmccutchen.net>
|
||||
|
||||
You can use this library in a C++ program to do arithmetic on integers of size
|
||||
limited only by your computer's memory. The library provides BigUnsigned and
|
||||
BigInteger classes that represent nonnegative integers and signed integers,
|
||||
respectively. Most of the C++ arithmetic operators are overloaded for these
|
||||
classes, so big-integer calculations are as easy as:
|
||||
|
||||
#include "BigIntegerLibrary.hh"
|
||||
|
||||
BigInteger a = 65536;
|
||||
cout << (a * a * a * a * a * a * a * a);
|
||||
|
||||
(prints 340282366920938463463374607431768211456)
|
||||
|
||||
The code in `sample.cc' demonstrates the most important features of the library.
|
||||
To get started quickly, read the code and explanations in that file and run it.
|
||||
If you want more detail or a feature not shown in `sample.cc', consult the
|
||||
consult the actual header and source files, which are thoroughly commented.
|
||||
|
||||
This library emphasizes ease of use and clarity of implementation over speed;
|
||||
some users will prefer GMP (http://swox.com/gmp/), which is faster. The code is
|
||||
intended to be reasonably portable across computers and modern C++ compilers; in
|
||||
particular, it uses whatever word size the computer provides (32-bit, 64-bit, or
|
||||
otherwise).
|
||||
|
||||
Compiling programs that use the library
|
||||
---------------------------------------
|
||||
The library consists of a folder full of C++ header files (`.hh') and source
|
||||
files (`.cc'). Your own programs should `#include' the necessary header files
|
||||
and link with the source files. A makefile that builds the sample program
|
||||
(`sample.cc') is included; you can adapt it to replace the sample with your own
|
||||
program.
|
||||
|
||||
Alternatively, you can use your own build system or IDE. In that case, you must
|
||||
put the library header files where the compiler will find them and arrange to
|
||||
have your program linked with the library source files; otherwise, you will get
|
||||
errors about missing header files or "undefined references". To learn how to do
|
||||
this, consult the documentation for the build system or IDE; don't bother asking
|
||||
me. Adding all the library files to your project will work in many IDEs but may
|
||||
not be the most desirable approach.
|
||||
|
||||
Resources
|
||||
---------
|
||||
The library's Web site (above) provides links to released versions, the current
|
||||
development version, and a mailing list for release announcements, questions,
|
||||
bug reports, and other discussion of the library. I would be delighted to hear
|
||||
from you if you like this library and/or find a good use for it.
|
||||
|
||||
Bugs and enhancements
|
||||
---------------------
|
||||
The library has been tested by me and others but is by no means bug-free. If
|
||||
you find a bug, please report it, whether it comes in the form of compiling
|
||||
trouble, a mathematically inaccurate result, or a memory-management blooper
|
||||
(since I use Java, these are altogether too common in my C++). I generally fix
|
||||
all reported bugs. You are also welcome to request enhancements, but I am
|
||||
unlikely to do substantial amounts of work on enhancements at this point.
|
||||
|
||||
Legal
|
||||
-----
|
||||
I, Matt McCutchen, the sole author of the original Big Integer Library, waive my
|
||||
copyright to it, placing it in the public domain. The library comes with
|
||||
absolutely no warranty.
|
||||
|
||||
~~~~
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
bad=
|
||||
|
||||
# If you encounter the following problem with Valgrind like I did:
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=455644
|
||||
# you can pass the environment variable NO_VALGRIND=1 to run the testsuite
|
||||
# without it.
|
||||
if [ "$NO_VALGRIND" ]; then
|
||||
cmd=(./testsuite)
|
||||
else
|
||||
cmd=(valgrind --error-exitcode=1 --leak-check=full ./testsuite)
|
||||
fi
|
||||
|
||||
set -o pipefail
|
||||
# Stdout goes directly to testsuite.out; stderr goes down the pipe.
|
||||
if ! "${cmd[@]}" 2>&1 >testsuite.out | tee testsuite.err; then
|
||||
echo >&2 'Memory errors!'
|
||||
bad=1
|
||||
fi
|
||||
|
||||
if grep 'LEAK SUMMARY' testsuite.err >/dev/null; then
|
||||
echo >&2 'Memory leaks!'
|
||||
bad=1
|
||||
fi
|
||||
|
||||
if ! diff -u testsuite.expected testsuite.out; then
|
||||
echo >&2 'Output is incorrect!'
|
||||
bad=1
|
||||
fi
|
||||
|
||||
if [ $bad ]; then
|
||||
echo >&2 'Test suite failed!'
|
||||
exit 1
|
||||
else
|
||||
echo 'Test suite passed.'
|
||||
fi
|
|
@ -0,0 +1,125 @@
|
|||
// Sample program demonstrating the use of the Big Integer Library.
|
||||
|
||||
// Standard libraries
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
// `BigIntegerLibrary.hh' includes all of the library headers.
|
||||
#include "BigIntegerLibrary.hh"
|
||||
|
||||
int main() {
|
||||
/* The library throws `const char *' error messages when things go
|
||||
* wrong. It's a good idea to catch them using a `try' block like this
|
||||
* one. Your C++ compiler might need a command-line option to compile
|
||||
* code that uses exceptions. */
|
||||
try {
|
||||
BigInteger a; // a is 0
|
||||
int b = 535;
|
||||
|
||||
/* Any primitive integer can be converted implicitly to a
|
||||
* BigInteger. */
|
||||
a = b;
|
||||
|
||||
/* The reverse conversion requires a method call (implicit
|
||||
* conversions were previously supported but caused trouble).
|
||||
* If a were too big for an int, the library would throw an
|
||||
* exception. */
|
||||
b = a.toInt();
|
||||
|
||||
BigInteger c(a); // Copy a BigInteger.
|
||||
|
||||
// The int literal is converted to a BigInteger.
|
||||
BigInteger d(-314159265);
|
||||
|
||||
/* This won't compile (at least on 32-bit machines) because the
|
||||
* number is too big to be a primitive integer literal, and
|
||||
* there's no such thing as a BigInteger literal. */
|
||||
//BigInteger e(3141592653589793238462643383279);
|
||||
|
||||
// Instead you can convert the number from a string.
|
||||
std::string s("3141592653589793238462643383279");
|
||||
BigInteger f = stringToBigInteger(s);
|
||||
|
||||
// You can convert the other way too.
|
||||
std::string s2 = bigIntegerToString(f);
|
||||
|
||||
// f is implicitly stringified and sent to std::cout.
|
||||
std::cout << f << std::endl;
|
||||
|
||||
/* Let's do some math! The library overloads most of the
|
||||
* mathematical operators (including assignment operators) to
|
||||
* work on BigIntegers. There are also ``copy-less''
|
||||
* operations; see `BigUnsigned.hh' for details. */
|
||||
|
||||
// Arithmetic operators
|
||||
BigInteger g(314159), h(265);
|
||||
std::cout << (g + h) << '\n'
|
||||
<< (g - h) << '\n'
|
||||
<< (g * h) << '\n'
|
||||
<< (g / h) << '\n'
|
||||
<< (g % h) << std::endl;
|
||||
|
||||
// Bitwise operators
|
||||
BigUnsigned i(0xFF0000FF), j(0x0000FFFF);
|
||||
// The library's << operator recognizes base flags.
|
||||
std::cout.flags(std::ios::hex | std::ios::showbase);
|
||||
std::cout << (i & j) << '\n'
|
||||
<< (i | j) << '\n'
|
||||
<< (i ^ j) << '\n'
|
||||
// Shift distances are ordinary unsigned ints.
|
||||
<< (j << 21) << '\n'
|
||||
<< (j >> 10) << '\n';
|
||||
std::cout.flags(std::ios::dec);
|
||||
|
||||
// Let's do some heavy lifting and calculate powers of 314.
|
||||
int maxPower = 10;
|
||||
BigUnsigned x(1), big314(314);
|
||||
for (int power = 0; power <= maxPower; power++) {
|
||||
std::cout << "314^" << power << " = " << x << std::endl;
|
||||
x *= big314; // A BigInteger assignment operator
|
||||
}
|
||||
|
||||
// Some big-integer algorithms (albeit on small integers).
|
||||
std::cout << gcd(BigUnsigned(60), 72) << '\n'
|
||||
<< modinv(BigUnsigned(7), 11) << '\n'
|
||||
<< modexp(BigUnsigned(314), 159, 2653) << std::endl;
|
||||
|
||||
// Add your own code here to experiment with the library.
|
||||
} catch(char const* err) {
|
||||
std::cout << "The library threw an exception:\n"
|
||||
<< err << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The original sample program produces this output:
|
||||
|
||||
3141592653589793238462643383279
|
||||
314424
|
||||
313894
|
||||
83252135
|
||||
1185
|
||||
134
|
||||
0xFF
|
||||
0xFF00FFFF
|
||||
0xFF00FF00
|
||||
0x1FFFE00000
|
||||
0x3F
|
||||
314^0 = 1
|
||||
314^1 = 314
|
||||
314^2 = 98596
|
||||
314^3 = 30959144
|
||||
314^4 = 9721171216
|
||||
314^5 = 3052447761824
|
||||
314^6 = 958468597212736
|
||||
314^7 = 300959139524799104
|
||||
314^8 = 94501169810786918656
|
||||
314^9 = 29673367320587092457984
|
||||
314^10 = 9317437338664347031806976
|
||||
12
|
||||
8
|
||||
1931
|
||||
|
||||
*/
|
|
@ -0,0 +1,326 @@
|
|||
/* Test suite for the library. First, it ``tests'' that all the constructs it
|
||||
* uses compile successfully. Then, its output to stdout is compared to the
|
||||
* expected output automatically extracted from slash-slash comments below.
|
||||
*
|
||||
* NOTE: For now, the test suite expects a 32-bit system. On others, some tests
|
||||
* may fail, and it may be ineffective at catching bugs. TODO: Remedy this. */
|
||||
|
||||
#include "BigIntegerLibrary.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
// Evaluate expr and print the result or "error" as appropriate.
|
||||
#define TEST(expr) do {\
|
||||
cout << "Line " << __LINE__ << ": ";\
|
||||
try {\
|
||||
cout << (expr);\
|
||||
} catch (const char *err) {\
|
||||
cout << "error";\
|
||||
}\
|
||||
cout << endl;\
|
||||
} while (0)
|
||||
|
||||
const BigUnsigned &check(const BigUnsigned &x) {
|
||||
unsigned int l = x.getLength();
|
||||
if (l != 0 && x.getBlock(l-1) == 0)
|
||||
cout << "check: Unzapped number!" << endl;
|
||||
if (l > x.getCapacity())
|
||||
cout << "check: Capacity inconsistent with length!" << endl;
|
||||
return x;
|
||||
}
|
||||
|
||||
const BigInteger &check(const BigInteger &x) {
|
||||
if (x.getSign() == 0 && !x.getMagnitude().isZero())
|
||||
cout << "check: Sign should not be zero!" << endl;
|
||||
if (x.getSign() != 0 && x.getMagnitude().isZero())
|
||||
cout << "check: Sign should be zero!" << endl;
|
||||
check(x.getMagnitude());
|
||||
return x;
|
||||
}
|
||||
|
||||
short pathologicalShort = ~((unsigned short)(~0) >> 1);
|
||||
int pathologicalInt = ~((unsigned int)(~0) >> 1);
|
||||
long pathologicalLong = ~((unsigned long)(~0) >> 1);
|
||||
|
||||
int main() {
|
||||
|
||||
try {
|
||||
|
||||
BigUnsigned z(0), one(1), ten(10);
|
||||
TEST(z); //0
|
||||
TEST(1); //1
|
||||
TEST(10); //10
|
||||
|
||||
// TODO: Comprehensively test the general and special cases of each function.
|
||||
|
||||
// === Default constructors ===
|
||||
|
||||
TEST(check(BigUnsigned())); //0
|
||||
TEST(check(BigInteger())); //0
|
||||
|
||||
// === Block-array constructors ===
|
||||
|
||||
BigUnsigned::Blk myBlocks[3];
|
||||
myBlocks[0] = 3;
|
||||
myBlocks[1] = 4;
|
||||
myBlocks[2] = 0;
|
||||
BigUnsigned bu(myBlocks, 3);
|
||||
TEST(check(bu)); //17179869187
|
||||
TEST(check(BigInteger(myBlocks, 3))); //17179869187
|
||||
TEST(check(BigInteger(bu ))); //17179869187
|
||||
|
||||
// For nonzero magnitude, reject zero and invalid signs.
|
||||
TEST(check(BigInteger(myBlocks, 3, BigInteger::positive))); //17179869187
|
||||
TEST(check(BigInteger(myBlocks, 3, BigInteger::negative))); //-17179869187
|
||||
TEST(check(BigInteger(myBlocks, 3, BigInteger::zero ))); //error
|
||||
TEST(check(BigInteger(bu, BigInteger::positive))); //17179869187
|
||||
TEST(check(BigInteger(bu, BigInteger::negative))); //-17179869187
|
||||
TEST(check(BigInteger(bu, BigInteger::zero ))); //error
|
||||
|
||||
// For zero magnitude, force the sign to zero without error.
|
||||
BigUnsigned::Blk myZeroBlocks[1];
|
||||
myZeroBlocks[0] = 0;
|
||||
TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::positive))); //0
|
||||
TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::negative))); //0
|
||||
TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::zero ))); //0
|
||||
|
||||
// === BigUnsigned conversion limits ===
|
||||
|
||||
TEST(BigUnsigned(0).toUnsignedLong()); //0
|
||||
TEST(BigUnsigned(4294967295U).toUnsignedLong()); //4294967295
|
||||
TEST(stringToBigUnsigned("4294967296").toUnsignedLong()); //error
|
||||
|
||||
TEST(BigUnsigned(0).toLong()); //0
|
||||
TEST(BigUnsigned(2147483647).toLong()); //2147483647
|
||||
TEST(BigUnsigned(2147483648U).toLong()); //error
|
||||
|
||||
// int is the same as long on a 32-bit system
|
||||
TEST(BigUnsigned(0).toUnsignedInt()); //0
|
||||
TEST(BigUnsigned(4294967295U).toUnsignedInt()); //4294967295
|
||||
TEST(stringToBigUnsigned("4294967296").toUnsignedInt()); //error
|
||||
|
||||
TEST(BigUnsigned(0).toInt()); //0
|
||||
TEST(BigUnsigned(2147483647).toInt()); //2147483647
|
||||
TEST(BigUnsigned(2147483648U).toInt()); //error
|
||||
|
||||
TEST(BigUnsigned(0).toUnsignedShort()); //0
|
||||
TEST(BigUnsigned(65535).toUnsignedShort()); //65535
|
||||
TEST(BigUnsigned(65536).toUnsignedShort()); //error
|
||||
|
||||
TEST(BigUnsigned(0).toShort()); //0
|
||||
TEST(BigUnsigned(32767).toShort()); //32767
|
||||
TEST(BigUnsigned(32768).toShort()); //error
|
||||
|
||||
// === BigInteger conversion limits ===
|
||||
|
||||
TEST(BigInteger(-1).toUnsignedLong()); //error
|
||||
TEST(BigInteger(0).toUnsignedLong()); //0
|
||||
TEST(BigInteger(4294967295U).toUnsignedLong()); //4294967295
|
||||
TEST(stringToBigInteger("4294967296").toUnsignedLong()); //error
|
||||
|
||||
TEST(stringToBigInteger("-2147483649").toLong()); //error
|
||||
TEST(stringToBigInteger("-2147483648").toLong()); //-2147483648
|
||||
TEST(BigInteger(-2147483647).toLong()); //-2147483647
|
||||
TEST(BigInteger(0).toLong()); //0
|
||||
TEST(BigInteger(2147483647).toLong()); //2147483647
|
||||
TEST(BigInteger(2147483648U).toLong()); //error
|
||||
|
||||
// int is the same as long on a 32-bit system
|
||||
TEST(BigInteger(-1).toUnsignedInt()); //error
|
||||
TEST(BigInteger(0).toUnsignedInt()); //0
|
||||
TEST(BigInteger(4294967295U).toUnsignedInt()); //4294967295
|
||||
TEST(stringToBigInteger("4294967296").toUnsignedInt()); //error
|
||||
|
||||
TEST(stringToBigInteger("-2147483649").toInt()); //error
|
||||
TEST(stringToBigInteger("-2147483648").toInt()); //-2147483648
|
||||
TEST(BigInteger(-2147483647).toInt()); //-2147483647
|
||||
TEST(BigInteger(0).toInt()); //0
|
||||
TEST(BigInteger(2147483647).toInt()); //2147483647
|
||||
TEST(BigInteger(2147483648U).toInt()); //error
|
||||
|
||||
TEST(BigInteger(-1).toUnsignedShort()); //error
|
||||
TEST(BigInteger(0).toUnsignedShort()); //0
|
||||
TEST(BigInteger(65535).toUnsignedShort()); //65535
|
||||
TEST(BigInteger(65536).toUnsignedShort()); //error
|
||||
|
||||
TEST(BigInteger(-32769).toShort()); //error
|
||||
TEST(BigInteger(-32768).toShort()); //-32768
|
||||
TEST(BigInteger(-32767).toShort()); //-32767
|
||||
TEST(BigInteger(0).toShort()); //0
|
||||
TEST(BigInteger(32767).toShort()); //32767
|
||||
TEST(BigInteger(32768).toShort()); //error
|
||||
|
||||
// === Negative BigUnsigneds ===
|
||||
|
||||
// ...during construction
|
||||
TEST(BigUnsigned(short(-1))); //error
|
||||
TEST(BigUnsigned(pathologicalShort)); //error
|
||||
TEST(BigUnsigned(-1)); //error
|
||||
TEST(BigUnsigned(pathologicalInt)); //error
|
||||
TEST(BigUnsigned(long(-1))); //error
|
||||
TEST(BigUnsigned(pathologicalLong)); //error
|
||||
|
||||
// ...during subtraction
|
||||
TEST(BigUnsigned(5) - BigUnsigned(6)); //error
|
||||
TEST(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979324")); //error
|
||||
TEST(check(BigUnsigned(5) - BigUnsigned(5))); //0
|
||||
TEST(check(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979323"))); //0
|
||||
TEST(check(stringToBigUnsigned("4294967296") - BigUnsigned(1))); //4294967295
|
||||
|
||||
// === BigUnsigned addition ===
|
||||
|
||||
TEST(check(BigUnsigned(0) + 0)); //0
|
||||
TEST(check(BigUnsigned(0) + 1)); //1
|
||||
// Ordinary carry
|
||||
TEST(check(stringToBigUnsigned("8589934591" /* 2^33 - 1*/)
|
||||
+ stringToBigUnsigned("4294967298" /* 2^32 + 2 */))); //12884901889
|
||||
// Creation of a new block
|
||||
TEST(check(BigUnsigned(0xFFFFFFFFU) + 1)); //4294967296
|
||||
|
||||
// === BigUnsigned subtraction ===
|
||||
|
||||
TEST(check(BigUnsigned(1) - 0)); //1
|
||||
TEST(check(BigUnsigned(1) - 1)); //0
|
||||
TEST(check(BigUnsigned(2) - 1)); //1
|
||||
// Ordinary borrow
|
||||
TEST(check(stringToBigUnsigned("12884901889")
|
||||
- stringToBigUnsigned("4294967298"))); //8589934591
|
||||
// Borrow that removes a block
|
||||
TEST(check(stringToBigUnsigned("4294967296") - 1)); //4294967295
|
||||
|
||||
// === BigUnsigned multiplication and division ===
|
||||
|
||||
BigUnsigned a = check(BigUnsigned(314159265) * 358979323);
|
||||
TEST(a); //112776680263877595
|
||||
TEST(a / 123); //916883579381118
|
||||
TEST(a % 123); //81
|
||||
|
||||
TEST(BigUnsigned(5) / 0); //error
|
||||
|
||||
// === Block accessors ===
|
||||
|
||||
BigUnsigned b;
|
||||
TEST(b); //0
|
||||
TEST(b.getBlock(0)); //0
|
||||
b.setBlock(1, 314);
|
||||
// Did b grow properly? And did we zero intermediate blocks?
|
||||
TEST(check(b)); //1348619730944
|
||||
TEST(b.getLength()); //2
|
||||
TEST(b.getBlock(0)); //0
|
||||
TEST(b.getBlock(1)); //314
|
||||
// Did b shrink properly?
|
||||
b.setBlock(1, 0);
|
||||
TEST(check(b)); //0
|
||||
|
||||
BigUnsigned bb(314);
|
||||
bb.setBlock(1, 159);
|
||||
// Make sure we used allocateAndCopy, not allocate
|
||||
TEST(bb.getBlock(0)); //314
|
||||
TEST(bb.getBlock(1)); //159
|
||||
// Blocks beyond the number should be zero regardless of whether they are
|
||||
// within the capacity.
|
||||
bb.add(1, 2);
|
||||
TEST(bb.getBlock(0)); //3
|
||||
TEST(bb.getBlock(1)); //0
|
||||
TEST(bb.getBlock(2)); //0
|
||||
TEST(bb.getBlock(314159)); //0
|
||||
|
||||
// === Bit accessors ===
|
||||
|
||||
TEST(BigUnsigned(0).bitLength()); //0
|
||||
TEST(BigUnsigned(1).bitLength()); //1
|
||||
TEST(BigUnsigned(4095).bitLength()); //12
|
||||
TEST(BigUnsigned(4096).bitLength()); //13
|
||||
// 5 billion is between 2^32 (about 4 billion) and 2^33 (about 8 billion).
|
||||
TEST(stringToBigUnsigned("5000000000").bitLength()); //33
|
||||
|
||||
// 25 is binary 11001.
|
||||
BigUnsigned bbb(25);
|
||||
TEST(bbb.getBit(4)); //1
|
||||
TEST(bbb.getBit(3)); //1
|
||||
TEST(bbb.getBit(2)); //0
|
||||
TEST(bbb.getBit(1)); //0
|
||||
TEST(bbb.getBit(0)); //1
|
||||
TEST(bbb.bitLength()); //5
|
||||
// Effectively add 2^32.
|
||||
bbb.setBit(32, true);
|
||||
TEST(bbb); //4294967321
|
||||
bbb.setBit(31, true);
|
||||
bbb.setBit(32, false);
|
||||
TEST(check(bbb)); //2147483673
|
||||
|
||||
// === Combining BigUnsigned, BigInteger, and primitive integers ===
|
||||
|
||||
BigUnsigned p1 = BigUnsigned(3) * 5;
|
||||
TEST(p1); //15
|
||||
/* In this case, we would like g++ to implicitly promote the BigUnsigned to a
|
||||
* BigInteger, but it seems to prefer converting the -5 to a BigUnsigned, which
|
||||
* causes an error. If I take out constructors for BigUnsigned from signed
|
||||
* primitive integers, the BigUnsigned(3) becomes ambiguous, and if I take out
|
||||
* all the constructors but BigUnsigned(unsigned long), g++ uses that
|
||||
* constructor and gets a wrong (positive) answer. Thus, I think we'll just
|
||||
* have to live with this cast. */
|
||||
BigInteger p2 = BigInteger(BigUnsigned(3)) * -5;
|
||||
TEST(p2); //-15
|
||||
|
||||
// === Test some previous bugs ===
|
||||
|
||||
{
|
||||
/* Test that BigInteger division sets the sign to zero.
|
||||
* Bug reported by David Allen. */
|
||||
BigInteger num(3), denom(5), quotient;
|
||||
num.divideWithRemainder(denom, quotient);
|
||||
check(quotient);
|
||||
num = 5;
|
||||
num.divideWithRemainder(denom, quotient);
|
||||
check(num);
|
||||
}
|
||||
|
||||
{
|
||||
/* Test that BigInteger subtraction sets the sign properly.
|
||||
* Bug reported by Samuel Larkin. */
|
||||
BigInteger zero(0), three(3), ans;
|
||||
ans = zero - three;
|
||||
TEST(check(ans).getSign()); //-1
|
||||
}
|
||||
|
||||
{
|
||||
/* Test that BigInteger multiplication shifts bits properly on systems
|
||||
* where long is bigger than int. (Obviously, this would only catch the
|
||||
* bug when run on such a system.)
|
||||
* Bug reported by Mohand Mezmaz. */
|
||||
BigInteger f=4; f*=3;
|
||||
TEST(check(f)); //12
|
||||
}
|
||||
|
||||
{
|
||||
/* Test that bitwise XOR allocates the larger length.
|
||||
* Bug reported by Sriram Sankararaman. */
|
||||
BigUnsigned a(0), b(3), ans;
|
||||
ans = a ^ b;
|
||||
TEST(ans); //3
|
||||
}
|
||||
|
||||
{
|
||||
/* Test that an aliased multiplication works.
|
||||
* Bug reported by Boris Dessy. */
|
||||
BigInteger num(5);
|
||||
num *= num;
|
||||
TEST(check(num)); //25
|
||||
}
|
||||
|
||||
{
|
||||
/* Test that BigUnsignedInABase(std::string) constructor rejects digits
|
||||
* too big for the specified base.
|
||||
* Bug reported by Niakam Kazemi. */
|
||||
TEST(BigUnsignedInABase("f", 10)); //error
|
||||
}
|
||||
|
||||
} catch (const char *err) {
|
||||
cout << "UNCAUGHT ERROR: " << err << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
OBJS += frontends/ast/ast.o
|
||||
OBJS += frontends/ast/simplify.o
|
||||
OBJS += frontends/ast/genrtlil.o
|
||||
|
|
@ -0,0 +1,859 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This is the AST frontend library.
|
||||
*
|
||||
* The AST frontend library is not a frontend on it's own but provides a
|
||||
* generic abstract syntax tree (AST) abstraction for HDL code and can be
|
||||
* used by HDL frontends. See "ast.h" for an overview of the API and the
|
||||
* Verilog frontend for an usage example.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/sha1.h"
|
||||
#include "ast.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace AST;
|
||||
using namespace AST_INTERNAL;
|
||||
|
||||
// instanciate global variables (public API)
|
||||
namespace AST {
|
||||
std::string current_filename;
|
||||
void (*set_line_num)(int) = NULL;
|
||||
int (*get_line_num)() = NULL;
|
||||
}
|
||||
|
||||
// instanciate global variables (private API)
|
||||
namespace AST_INTERNAL {
|
||||
bool flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg;
|
||||
AstNode *current_ast, *current_ast_mod;
|
||||
std::map<std::string, AstNode*> current_scope;
|
||||
RTLIL::SigSpec *genRTLIL_subst_from = NULL;
|
||||
RTLIL::SigSpec *genRTLIL_subst_to = NULL;
|
||||
AstNode *current_top_block, *current_block, *current_block_child;
|
||||
AstModule *current_module;
|
||||
}
|
||||
|
||||
// convert node types to string
|
||||
std::string AST::type2str(AstNodeType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
#define X(_item) case _item: return #_item;
|
||||
X(AST_NONE)
|
||||
X(AST_DESIGN)
|
||||
X(AST_MODULE)
|
||||
X(AST_TASK)
|
||||
X(AST_FUNCTION)
|
||||
X(AST_WIRE)
|
||||
X(AST_MEMORY)
|
||||
X(AST_AUTOWIRE)
|
||||
X(AST_PARAMETER)
|
||||
X(AST_LOCALPARAM)
|
||||
X(AST_PARASET)
|
||||
X(AST_ARGUMENT)
|
||||
X(AST_RANGE)
|
||||
X(AST_CONSTANT)
|
||||
X(AST_CELLTYPE)
|
||||
X(AST_IDENTIFIER)
|
||||
X(AST_FCALL)
|
||||
X(AST_TO_SIGNED)
|
||||
X(AST_TO_UNSIGNED)
|
||||
X(AST_CONCAT)
|
||||
X(AST_REPLICATE)
|
||||
X(AST_BIT_NOT)
|
||||
X(AST_BIT_AND)
|
||||
X(AST_BIT_OR)
|
||||
X(AST_BIT_XOR)
|
||||
X(AST_BIT_XNOR)
|
||||
X(AST_REDUCE_AND)
|
||||
X(AST_REDUCE_OR)
|
||||
X(AST_REDUCE_XOR)
|
||||
X(AST_REDUCE_XNOR)
|
||||
X(AST_REDUCE_BOOL)
|
||||
X(AST_SHIFT_LEFT)
|
||||
X(AST_SHIFT_RIGHT)
|
||||
X(AST_SHIFT_SLEFT)
|
||||
X(AST_SHIFT_SRIGHT)
|
||||
X(AST_LT)
|
||||
X(AST_LE)
|
||||
X(AST_EQ)
|
||||
X(AST_NE)
|
||||
X(AST_GE)
|
||||
X(AST_GT)
|
||||
X(AST_ADD)
|
||||
X(AST_SUB)
|
||||
X(AST_MUL)
|
||||
X(AST_DIV)
|
||||
X(AST_MOD)
|
||||
X(AST_POW)
|
||||
X(AST_POS)
|
||||
X(AST_NEG)
|
||||
X(AST_LOGIC_AND)
|
||||
X(AST_LOGIC_OR)
|
||||
X(AST_LOGIC_NOT)
|
||||
X(AST_TERNARY)
|
||||
X(AST_MEMRD)
|
||||
X(AST_MEMWR)
|
||||
X(AST_TCALL)
|
||||
X(AST_ASSIGN)
|
||||
X(AST_CELL)
|
||||
X(AST_PRIMITIVE)
|
||||
X(AST_ALWAYS)
|
||||
X(AST_BLOCK)
|
||||
X(AST_ASSIGN_EQ)
|
||||
X(AST_ASSIGN_LE)
|
||||
X(AST_CASE)
|
||||
X(AST_COND)
|
||||
X(AST_DEFAULT)
|
||||
X(AST_FOR)
|
||||
X(AST_GENVAR)
|
||||
X(AST_GENFOR)
|
||||
X(AST_GENIF)
|
||||
X(AST_GENBLOCK)
|
||||
X(AST_POSEDGE)
|
||||
X(AST_NEGEDGE)
|
||||
X(AST_EDGE)
|
||||
#undef X
|
||||
default:
|
||||
assert(!"Missing enum to string def in AST::type2str().");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// create new node (AstNode constructor)
|
||||
// (the optional child arguments make it easier to create AST trees)
|
||||
AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2)
|
||||
{
|
||||
this->type = type;
|
||||
filename = current_filename;
|
||||
linenum = get_line_num();
|
||||
is_input = false;
|
||||
is_output = false;
|
||||
is_reg = false;
|
||||
is_signed = false;
|
||||
range_valid = false;
|
||||
port_id = 0;
|
||||
range_left = -1;
|
||||
range_right = 0;
|
||||
integer = 0;
|
||||
id2ast = NULL;
|
||||
|
||||
if (child1)
|
||||
children.push_back(child1);
|
||||
if (child2)
|
||||
children.push_back(child2);
|
||||
}
|
||||
|
||||
// create a (deep recursive) copy of a node
|
||||
AstNode *AstNode::clone()
|
||||
{
|
||||
AstNode *that = new AstNode;
|
||||
*that = *this;
|
||||
for (auto &it : that->children)
|
||||
it = it->clone();
|
||||
for (auto &it : that->attributes)
|
||||
it.second = it.second->clone();
|
||||
return that;
|
||||
}
|
||||
|
||||
// create a (deep recursive) copy of a node use 'other' as target root node
|
||||
void AstNode::cloneInto(AstNode *other)
|
||||
{
|
||||
AstNode *tmp = clone();
|
||||
other->delete_children();
|
||||
*other = *tmp;
|
||||
tmp->children.clear();
|
||||
tmp->attributes.clear();
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
// delete all children in this node
|
||||
void AstNode::delete_children()
|
||||
{
|
||||
for (auto &it : children)
|
||||
delete it;
|
||||
children.clear();
|
||||
|
||||
for (auto &it : attributes)
|
||||
delete it.second;
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
// AstNode destructor
|
||||
AstNode::~AstNode()
|
||||
{
|
||||
delete_children();
|
||||
}
|
||||
|
||||
// create a nice text representation of the node
|
||||
// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
|
||||
void AstNode::dumpAst(FILE *f, std::string indent, AstNode *other)
|
||||
{
|
||||
if (f == NULL) {
|
||||
for (auto f : log_files)
|
||||
dumpAst(f, indent, other);
|
||||
return;
|
||||
}
|
||||
if (other != NULL) {
|
||||
if (type != other->type)
|
||||
goto found_diff_to_other;
|
||||
if (children.size() != other->children.size())
|
||||
goto found_diff_to_other;
|
||||
if (str != other->str)
|
||||
goto found_diff_to_other;
|
||||
if (bits != other->bits)
|
||||
goto found_diff_to_other;
|
||||
if (is_input != other->is_input)
|
||||
goto found_diff_to_other;
|
||||
if (is_output != other->is_output)
|
||||
goto found_diff_to_other;
|
||||
if (is_reg != other->is_reg)
|
||||
goto found_diff_to_other;
|
||||
if (is_signed != other->is_signed)
|
||||
goto found_diff_to_other;
|
||||
if (range_valid != other->range_valid)
|
||||
goto found_diff_to_other;
|
||||
if (port_id != other->port_id)
|
||||
goto found_diff_to_other;
|
||||
if (range_left != other->range_left)
|
||||
goto found_diff_to_other;
|
||||
if (range_right != other->range_right)
|
||||
goto found_diff_to_other;
|
||||
if (integer != other->integer)
|
||||
goto found_diff_to_other;
|
||||
if (0) {
|
||||
found_diff_to_other:
|
||||
other->dumpAst(f, indent + "- ");
|
||||
this->dumpAst(f, indent + "+ ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string type_name = type2str(type);
|
||||
fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
|
||||
if (!str.empty())
|
||||
fprintf(f, " str='%s'", str.c_str());
|
||||
if (!bits.empty()) {
|
||||
fprintf(f, " bits='");
|
||||
for (size_t i = bits.size(); i > 0; i--)
|
||||
fprintf(f, "%c", bits[i-1] == RTLIL::S0 ? '0' :
|
||||
bits[i-1] == RTLIL::S1 ? '1' :
|
||||
bits[i-1] == RTLIL::Sx ? 'x' :
|
||||
bits[i-1] == RTLIL::Sz ? 'z' : '?');
|
||||
fprintf(f, "'(%zd)", bits.size());
|
||||
}
|
||||
if (is_input)
|
||||
fprintf(f, " input");
|
||||
if (is_output)
|
||||
fprintf(f, " output");
|
||||
if (is_reg)
|
||||
fprintf(f, " reg");
|
||||
if (is_signed)
|
||||
fprintf(f, " signed");
|
||||
if (port_id > 0)
|
||||
fprintf(f, " port=%d", port_id);
|
||||
if (range_valid || range_left != -1 || range_right != 0)
|
||||
fprintf(f, " range=[%d:%d]%s", range_left, range_right, range_valid ? "" : "!");
|
||||
if (integer != 0)
|
||||
fprintf(f, " int=%u", (int)integer);
|
||||
fprintf(f, "\n");
|
||||
|
||||
for (size_t i = 0; i < children.size(); i++)
|
||||
children[i]->dumpAst(f, indent + " ", other ? other->children[i] : NULL);
|
||||
}
|
||||
|
||||
// helper function for AstNode::dumpVlog()
|
||||
static std::string id2vl(std::string txt)
|
||||
{
|
||||
if (txt.size() > 1 && txt[0] == '\\')
|
||||
txt = txt.substr(1);
|
||||
for (size_t i = 0; i < txt.size(); i++) {
|
||||
if ('A' <= txt[i] && txt[i] <= 'Z') continue;
|
||||
if ('a' <= txt[i] && txt[i] <= 'z') continue;
|
||||
if ('0' <= txt[i] && txt[i] <= '9') continue;
|
||||
if (txt[i] == '_') continue;
|
||||
txt = "\\" + txt + " ";
|
||||
break;
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
// dump AST node as verilog pseudo-code
|
||||
void AstNode::dumpVlog(FILE *f, std::string indent)
|
||||
{
|
||||
bool first = true;
|
||||
std::string txt;
|
||||
std::vector<AstNode*> rem_children1, rem_children2;
|
||||
|
||||
if (f == NULL) {
|
||||
for (auto f : log_files)
|
||||
dumpVlog(f, indent);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AST_MODULE:
|
||||
fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str());
|
||||
for (auto child : children)
|
||||
if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
|
||||
fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str());
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, ");\n");
|
||||
|
||||
for (auto child : children)
|
||||
if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
else
|
||||
rem_children1.push_back(child);
|
||||
|
||||
for (auto child : rem_children1)
|
||||
if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
else
|
||||
rem_children2.push_back(child);
|
||||
rem_children1.clear();
|
||||
|
||||
for (auto child : rem_children2)
|
||||
if (child->type == AST_TASK || child->type == AST_FUNCTION)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
else
|
||||
rem_children1.push_back(child);
|
||||
rem_children2.clear();
|
||||
|
||||
for (auto child : rem_children1)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
rem_children1.clear();
|
||||
|
||||
fprintf(f, "%s" "endmodule\n", indent.c_str());
|
||||
break;
|
||||
|
||||
case AST_WIRE:
|
||||
if (is_input && is_output)
|
||||
fprintf(f, "%s" "inout", indent.c_str());
|
||||
else if (is_input)
|
||||
fprintf(f, "%s" "input", indent.c_str());
|
||||
else if (is_output)
|
||||
fprintf(f, "%s" "output", indent.c_str());
|
||||
else if (!is_reg)
|
||||
fprintf(f, "%s" "wire", indent.c_str());
|
||||
if (is_reg)
|
||||
fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str());
|
||||
if (is_signed)
|
||||
fprintf(f, " signed");
|
||||
for (auto child : children) {
|
||||
fprintf(f, " ");
|
||||
child->dumpVlog(f, "");
|
||||
}
|
||||
fprintf(f, " %s", id2vl(str).c_str());
|
||||
fprintf(f, ";\n");
|
||||
break;
|
||||
|
||||
case AST_MEMORY:
|
||||
fprintf(f, "%s" "memory", indent.c_str());
|
||||
if (is_signed)
|
||||
fprintf(f, " signed");
|
||||
for (auto child : children) {
|
||||
fprintf(f, " ");
|
||||
child->dumpVlog(f, "");
|
||||
if (first)
|
||||
fprintf(f, " %s", id2vl(str).c_str());
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, ";\n");
|
||||
break;
|
||||
|
||||
case AST_RANGE:
|
||||
if (range_valid)
|
||||
fprintf(f, "[%d:%d]", range_left, range_right);
|
||||
else {
|
||||
for (auto child : children) {
|
||||
fprintf(f, "%c", first ? '[' : ':');
|
||||
child->dumpVlog(f, "");
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, "]");
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_ALWAYS:
|
||||
fprintf(f, "%s" "always @(", indent.c_str());
|
||||
for (auto child : children) {
|
||||
if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
|
||||
continue;
|
||||
if (!first)
|
||||
fprintf(f, ", ");
|
||||
child->dumpVlog(f, "");
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, ")\n");
|
||||
for (auto child : children) {
|
||||
if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_POSEDGE:
|
||||
case AST_NEGEDGE:
|
||||
case AST_EDGE:
|
||||
if (type == AST_POSEDGE)
|
||||
fprintf(f, "posedge ");
|
||||
if (type == AST_NEGEDGE)
|
||||
fprintf(f, "negedge ");
|
||||
for (auto child : children)
|
||||
child->dumpVlog(f, "");
|
||||
break;
|
||||
|
||||
case AST_IDENTIFIER:
|
||||
fprintf(f, "%s", id2vl(str).c_str());
|
||||
for (auto child : children)
|
||||
child->dumpVlog(f, "");
|
||||
break;
|
||||
|
||||
case AST_CONSTANT:
|
||||
if (!str.empty())
|
||||
fprintf(f, "\"%s\"", str.c_str());
|
||||
else if (bits.size() == 32)
|
||||
fprintf(f, "%d", RTLIL::Const(bits).as_int());
|
||||
else
|
||||
fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str());
|
||||
break;
|
||||
|
||||
case AST_BLOCK:
|
||||
if (children.size() == 1) {
|
||||
children[0]->dumpVlog(f, indent);
|
||||
} else {
|
||||
fprintf(f, "%s" "begin\n", indent.c_str());
|
||||
for (auto child : children)
|
||||
child->dumpVlog(f, indent + " ");
|
||||
fprintf(f, "%s" "end\n", indent.c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_CASE:
|
||||
fprintf(f, "%s" "case (", indent.c_str());
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, ")\n");
|
||||
for (size_t i = 1; i < children.size(); i++) {
|
||||
AstNode *child = children[i];
|
||||
child->dumpVlog(f, indent + " ");
|
||||
}
|
||||
fprintf(f, "%s" "endcase\n", indent.c_str());
|
||||
break;
|
||||
|
||||
case AST_COND:
|
||||
for (auto child : children) {
|
||||
if (child->type == AST_BLOCK) {
|
||||
fprintf(f, ":\n");
|
||||
child->dumpVlog(f, indent + " ");
|
||||
first = true;
|
||||
} else {
|
||||
fprintf(f, "%s", first ? indent.c_str() : ", ");
|
||||
if (child->type == AST_DEFAULT)
|
||||
fprintf(f, "default");
|
||||
else
|
||||
child->dumpVlog(f, "");
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_ASSIGN_EQ:
|
||||
case AST_ASSIGN_LE:
|
||||
fprintf(f, "%s", indent.c_str());
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, " %s ", type == AST_ASSIGN_EQ ? "=" : "<=");
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, ";\n");
|
||||
break;
|
||||
|
||||
case AST_CONCAT:
|
||||
fprintf(f, "{");
|
||||
for (auto child : children) {
|
||||
if (!first)
|
||||
fprintf(f, ", ");
|
||||
child->dumpVlog(f, "");
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, "}");
|
||||
break;
|
||||
|
||||
case AST_REPLICATE:
|
||||
fprintf(f, "{");
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, "{");
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, "}}");
|
||||
break;
|
||||
|
||||
if (0) { case AST_BIT_NOT: txt = "~"; }
|
||||
if (0) { case AST_REDUCE_AND: txt = "&"; }
|
||||
if (0) { case AST_REDUCE_OR: txt = "|"; }
|
||||
if (0) { case AST_REDUCE_XOR: txt = "^"; }
|
||||
if (0) { case AST_REDUCE_XNOR: txt = "~^"; }
|
||||
if (0) { case AST_REDUCE_BOOL: txt = "|"; }
|
||||
if (0) { case AST_POS: txt = "+"; }
|
||||
if (0) { case AST_NEG: txt = "-"; }
|
||||
if (0) { case AST_LOGIC_NOT: txt = "!"; }
|
||||
fprintf(f, "%s(", txt.c_str());
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
|
||||
if (0) { case AST_BIT_AND: txt = "&"; }
|
||||
if (0) { case AST_BIT_OR: txt = "|"; }
|
||||
if (0) { case AST_BIT_XOR: txt = "^"; }
|
||||
if (0) { case AST_BIT_XNOR: txt = "~^"; }
|
||||
if (0) { case AST_SHIFT_LEFT: txt = "<<"; }
|
||||
if (0) { case AST_SHIFT_RIGHT: txt = ">>"; }
|
||||
if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; }
|
||||
if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; }
|
||||
if (0) { case AST_LT: txt = "<"; }
|
||||
if (0) { case AST_LE: txt = "<="; }
|
||||
if (0) { case AST_EQ: txt = "=="; }
|
||||
if (0) { case AST_NE: txt = "!="; }
|
||||
if (0) { case AST_GE: txt = ">="; }
|
||||
if (0) { case AST_GT: txt = ">"; }
|
||||
if (0) { case AST_ADD: txt = "+"; }
|
||||
if (0) { case AST_SUB: txt = "-"; }
|
||||
if (0) { case AST_MUL: txt = "*"; }
|
||||
if (0) { case AST_DIV: txt = "/"; }
|
||||
if (0) { case AST_MOD: txt = "%"; }
|
||||
if (0) { case AST_POW: txt = "**"; }
|
||||
if (0) { case AST_LOGIC_AND: txt = "&&"; }
|
||||
if (0) { case AST_LOGIC_OR: txt = "||"; }
|
||||
fprintf(f, "(");
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, ")%s(", txt.c_str());
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
|
||||
case AST_TERNARY:
|
||||
fprintf(f, "(");
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, ") ? (");
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, ") : (");
|
||||
children[2]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
|
||||
default:
|
||||
std::string type_name = type2str(type);
|
||||
fprintf(f, "%s" "/** %s **/%s", indent.c_str(), type_name.c_str(), indent.empty() ? "" : "\n");
|
||||
// dumpAst(f, indent, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// check if two AST nodes are identical
|
||||
bool AstNode::operator==(const AstNode &other) const
|
||||
{
|
||||
if (type != other.type)
|
||||
return false;
|
||||
if (children.size() != other.children.size())
|
||||
return false;
|
||||
if (str != other.str)
|
||||
return false;
|
||||
if (bits != other.bits)
|
||||
return false;
|
||||
if (is_input != other.is_input)
|
||||
return false;
|
||||
if (is_output != other.is_output)
|
||||
return false;
|
||||
if (is_reg != other.is_reg)
|
||||
return false;
|
||||
if (is_signed != other.is_signed)
|
||||
return false;
|
||||
if (range_valid != other.range_valid)
|
||||
return false;
|
||||
if (port_id != other.port_id)
|
||||
return false;
|
||||
if (range_left != other.range_left)
|
||||
return false;
|
||||
if (range_right != other.range_right)
|
||||
return false;
|
||||
if (integer != other.integer)
|
||||
return false;
|
||||
for (size_t i = 0; i < children.size(); i++)
|
||||
if (*children[i] != *other.children[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if two AST nodes are not identical
|
||||
bool AstNode::operator!=(const AstNode &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// check if this AST contains the given node
|
||||
bool AstNode::contains(const AstNode *other) const
|
||||
{
|
||||
if (this == other)
|
||||
return true;
|
||||
for (auto child : children)
|
||||
if (child->contains(other))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create an AST node for a constant (using a 32 bit int as value)
|
||||
AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width)
|
||||
{
|
||||
AstNode *node = new AstNode(AST_CONSTANT);
|
||||
node->integer = v;
|
||||
node->is_signed = is_signed;
|
||||
for (int i = 0; i < width; i++) {
|
||||
node->bits.push_back((v & 1) ? RTLIL::S1 : RTLIL::S0);
|
||||
v = v >> 1;
|
||||
}
|
||||
node->range_valid = true;
|
||||
node->range_left = width-1;
|
||||
node->range_right = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
// create an AST node for a constant (using a bit vector as value)
|
||||
AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
|
||||
{
|
||||
AstNode *node = new AstNode(AST_CONSTANT);
|
||||
node->is_signed = is_signed;
|
||||
node->bits = v;
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
if (i < node->bits.size())
|
||||
node->integer |= (node->bits[i] == RTLIL::S1) << i;
|
||||
else if (is_signed)
|
||||
node->integer |= (node->bits.back() == RTLIL::S1) << i;
|
||||
}
|
||||
node->range_valid = true;
|
||||
node->range_left = node->bits.size();
|
||||
node->range_right = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
// create a new AstModule from an AST_MODULE AST node
|
||||
static AstModule* process_module(AstNode *ast)
|
||||
{
|
||||
assert(ast->type == AST_MODULE);
|
||||
log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());
|
||||
|
||||
current_ast_mod = ast;
|
||||
AstNode *ast_before_simplify = ast->clone();
|
||||
|
||||
while (ast->simplify(false, false, false, 0)) { }
|
||||
|
||||
if (flag_dump_ast) {
|
||||
log("Dumping verilog AST (as requested by %s option):\n", flag_dump_ast_diff ? "dump_ast_diff" : "dump_ast");
|
||||
ast->dumpAst(NULL, " ", flag_dump_ast_diff ? ast_before_simplify : NULL);
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
|
||||
if (flag_dump_vlog) {
|
||||
log("Dumping verilog AST (as requested by dump_vlog option):\n");
|
||||
ast->dumpVlog(NULL, " ");
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
|
||||
current_module = new AstModule;
|
||||
current_module->ast = NULL;
|
||||
current_module->name = ast->str;
|
||||
current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
|
||||
for (auto &attr : ast->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), ast->filename.c_str(), ast->linenum);
|
||||
current_module->attributes[attr.first].str = attr.second->str;
|
||||
current_module->attributes[attr.first].bits = attr.second->bits;
|
||||
}
|
||||
for (size_t i = 0; i < ast->children.size(); i++) {
|
||||
AstNode *node = ast->children[i];
|
||||
if (node->type == AST_WIRE || node->type == AST_MEMORY)
|
||||
node->genRTLIL();
|
||||
}
|
||||
for (size_t i = 0; i < ast->children.size(); i++) {
|
||||
AstNode *node = ast->children[i];
|
||||
if (node->type != AST_WIRE && node->type != AST_MEMORY)
|
||||
node->genRTLIL();
|
||||
}
|
||||
|
||||
current_module->ast = ast_before_simplify;
|
||||
current_module->nolatches = flag_nolatches;
|
||||
current_module->nomem2reg = flag_nomem2reg;
|
||||
return current_module;
|
||||
}
|
||||
|
||||
// create AstModule instances for all modules in the AST tree and add them to 'design'
|
||||
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ast_diff, bool dump_vlog, bool nolatches, bool nomem2reg)
|
||||
{
|
||||
current_ast = ast;
|
||||
flag_dump_ast = dump_ast;
|
||||
flag_dump_ast_diff = dump_ast_diff;
|
||||
flag_dump_vlog = dump_vlog;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
|
||||
assert(current_ast->type == AST_DESIGN);
|
||||
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) {
|
||||
if (design->modules.count((*it)->str) != 0)
|
||||
log_error("Re-definition of module `%s' at %s:%d!\n",
|
||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
||||
design->modules[(*it)->str] = process_module(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// AstModule destructor
|
||||
AstModule::~AstModule()
|
||||
{
|
||||
if (ast != NULL)
|
||||
delete ast;
|
||||
}
|
||||
|
||||
// create a new parametric module (when needed) and return the name of the generated module
|
||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters)
|
||||
{
|
||||
log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", name.c_str());
|
||||
|
||||
current_ast = NULL;
|
||||
flag_dump_ast = false;
|
||||
flag_dump_ast_diff = false;
|
||||
flag_dump_vlog = false;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
use_internal_line_num();
|
||||
|
||||
std::vector<unsigned char> hash_data;
|
||||
hash_data.insert(hash_data.end(), name.begin(), name.end());
|
||||
hash_data.push_back(0);
|
||||
|
||||
AstNode *new_ast = ast->clone();
|
||||
|
||||
int para_counter = 0;
|
||||
for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) {
|
||||
AstNode *child = *it;
|
||||
if (child->type != AST_PARAMETER)
|
||||
continue;
|
||||
para_counter++;
|
||||
std::string para_id = child->str;
|
||||
if (parameters.count(child->str) > 0) {
|
||||
log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
|
||||
rewrite_parameter:
|
||||
child->delete_children();
|
||||
child->children.push_back(AstNode::mkconst_bits(parameters[para_id].bits, false));
|
||||
hash_data.insert(hash_data.end(), child->str.begin(), child->str.end());
|
||||
hash_data.push_back(0);
|
||||
hash_data.insert(hash_data.end(), parameters[para_id].bits.begin(), parameters[para_id].bits.end());
|
||||
hash_data.push_back(0xff);
|
||||
parameters.erase(para_id);
|
||||
continue;
|
||||
}
|
||||
char buf[100];
|
||||
snprintf(buf, 100, "$%d", para_counter);
|
||||
if (parameters.count(buf) > 0) {
|
||||
para_id = buf;
|
||||
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||
goto rewrite_parameter;
|
||||
}
|
||||
}
|
||||
if (parameters.size() > 0)
|
||||
log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str());
|
||||
|
||||
unsigned char hash[20];
|
||||
unsigned char *hash_data2 = new unsigned char[hash_data.size()];
|
||||
for (size_t i = 0; i < hash_data.size(); i++)
|
||||
hash_data2[i] = hash_data[i];
|
||||
sha1::calc(hash_data2, hash_data.size(), hash);
|
||||
delete[] hash_data2;
|
||||
|
||||
char hexstring[41];
|
||||
sha1::toHexString(hash, hexstring);
|
||||
|
||||
std::string modname = "$paramod$" + std::string(hexstring) + "$" + name;
|
||||
|
||||
if (design->modules.count(modname) == 0) {
|
||||
new_ast->str = modname;
|
||||
design->modules[modname] = process_module(new_ast);
|
||||
} else {
|
||||
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
|
||||
}
|
||||
|
||||
delete new_ast;
|
||||
return modname;
|
||||
}
|
||||
|
||||
// recompile a module from AST with updated widths for auto-wires
|
||||
// (auto-wires are wires that are used but not declared an thus have an automatically determined width)
|
||||
void AstModule::update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes)
|
||||
{
|
||||
log_header("Executing AST frontend in update_auto_wires mode using pre-parsed AST for module `%s'.\n", name.c_str());
|
||||
|
||||
current_ast = NULL;
|
||||
flag_dump_ast = false;
|
||||
flag_dump_ast_diff = false;
|
||||
flag_dump_vlog = false;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
use_internal_line_num();
|
||||
|
||||
for (auto it = auto_sizes.begin(); it != auto_sizes.end(); it++) {
|
||||
log("Adding extra wire declaration to AST: wire [%d:0] %s\n", it->second - 1, it->first.c_str());
|
||||
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(it->second - 1, true), AstNode::mkconst_int(0, true)));
|
||||
wire->str = it->first;
|
||||
ast->children.insert(ast->children.begin(), wire);
|
||||
}
|
||||
|
||||
AstModule *newmod = process_module(ast);
|
||||
|
||||
delete ast;
|
||||
ast = newmod->ast;
|
||||
newmod->ast = NULL;
|
||||
|
||||
wires.swap(newmod->wires);
|
||||
cells.swap(newmod->cells);
|
||||
processes.swap(newmod->processes);
|
||||
connections.swap(newmod->connections);
|
||||
attributes.swap(newmod->attributes);
|
||||
delete newmod;
|
||||
}
|
||||
|
||||
// internal dummy line number callbacks
|
||||
namespace {
|
||||
int internal_line_num;
|
||||
void internal_set_line_num(int n) {
|
||||
internal_line_num = n;
|
||||
}
|
||||
int internal_get_line_num() {
|
||||
return internal_line_num;
|
||||
}
|
||||
}
|
||||
|
||||
// use internal dummy line number callbacks
|
||||
void AST::use_internal_line_num()
|
||||
{
|
||||
set_line_num = &internal_set_line_num;
|
||||
get_line_num = &internal_get_line_num;
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This is the AST frontend library.
|
||||
*
|
||||
* The AST frontend library is not a frontend on it's own but provides a
|
||||
* generic abstract syntax tree (AST) abstraction for HDL code and can be
|
||||
* used by HDL frontends. See "ast.h" for an overview of the API and the
|
||||
* Verilog frontend for an usage example.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
|
||||
namespace AST
|
||||
{
|
||||
// all node types, type2str() must be extended
|
||||
// whenever a new node type is added here
|
||||
enum AstNodeType
|
||||
{
|
||||
AST_NONE,
|
||||
AST_DESIGN,
|
||||
AST_MODULE,
|
||||
AST_TASK,
|
||||
AST_FUNCTION,
|
||||
|
||||
AST_WIRE,
|
||||
AST_MEMORY,
|
||||
AST_AUTOWIRE,
|
||||
AST_PARAMETER,
|
||||
AST_LOCALPARAM,
|
||||
AST_PARASET,
|
||||
AST_ARGUMENT,
|
||||
AST_RANGE,
|
||||
AST_CONSTANT,
|
||||
AST_CELLTYPE,
|
||||
AST_IDENTIFIER,
|
||||
|
||||
AST_FCALL,
|
||||
AST_TO_SIGNED,
|
||||
AST_TO_UNSIGNED,
|
||||
AST_CONCAT,
|
||||
AST_REPLICATE,
|
||||
AST_BIT_NOT,
|
||||
AST_BIT_AND,
|
||||
AST_BIT_OR,
|
||||
AST_BIT_XOR,
|
||||
AST_BIT_XNOR,
|
||||
AST_REDUCE_AND,
|
||||
AST_REDUCE_OR,
|
||||
AST_REDUCE_XOR,
|
||||
AST_REDUCE_XNOR,
|
||||
AST_REDUCE_BOOL,
|
||||
AST_SHIFT_LEFT,
|
||||
AST_SHIFT_RIGHT,
|
||||
AST_SHIFT_SLEFT,
|
||||
AST_SHIFT_SRIGHT,
|
||||
AST_LT,
|
||||
AST_LE,
|
||||
AST_EQ,
|
||||
AST_NE,
|
||||
AST_GE,
|
||||
AST_GT,
|
||||
AST_ADD,
|
||||
AST_SUB,
|
||||
AST_MUL,
|
||||
AST_DIV,
|
||||
AST_MOD,
|
||||
AST_POW,
|
||||
AST_POS,
|
||||
AST_NEG,
|
||||
AST_LOGIC_AND,
|
||||
AST_LOGIC_OR,
|
||||
AST_LOGIC_NOT,
|
||||
AST_TERNARY,
|
||||
AST_MEMRD,
|
||||
AST_MEMWR,
|
||||
|
||||
AST_TCALL,
|
||||
AST_ASSIGN,
|
||||
AST_CELL,
|
||||
AST_PRIMITIVE,
|
||||
AST_ALWAYS,
|
||||
AST_BLOCK,
|
||||
AST_ASSIGN_EQ,
|
||||
AST_ASSIGN_LE,
|
||||
AST_CASE,
|
||||
AST_COND,
|
||||
AST_DEFAULT,
|
||||
AST_FOR,
|
||||
|
||||
AST_GENVAR,
|
||||
AST_GENFOR,
|
||||
AST_GENIF,
|
||||
AST_GENBLOCK,
|
||||
|
||||
AST_POSEDGE,
|
||||
AST_NEGEDGE,
|
||||
AST_EDGE
|
||||
};
|
||||
|
||||
// convert an node type to a string (e.g. for debug output)
|
||||
std::string type2str(AstNodeType type);
|
||||
|
||||
// The AST is built using instances of this struct
|
||||
struct AstNode
|
||||
{
|
||||
// this nodes type
|
||||
AstNodeType type;
|
||||
|
||||
// the list of child nodes for this node
|
||||
std::vector<AstNode*> children;
|
||||
|
||||
// the list of attributes assigned to this node
|
||||
std::map<RTLIL::IdString, AstNode*> attributes;
|
||||
|
||||
// node content - most of it is unused in most node types
|
||||
std::string str;
|
||||
std::vector<RTLIL::State> bits;
|
||||
bool is_input, is_output, is_reg, is_signed, range_valid;
|
||||
int port_id, range_left, range_right;
|
||||
uint32_t integer;
|
||||
|
||||
// this is set by simplify and used during RTLIL generation
|
||||
AstNode *id2ast;
|
||||
|
||||
// this is the original sourcecode location that resulted in this AST node
|
||||
// it is automatically set by the constructor using AST::current_filename and
|
||||
// the AST::get_line_num() callback function.
|
||||
std::string filename;
|
||||
int linenum;
|
||||
|
||||
// creating and deleting nodes
|
||||
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL);
|
||||
AstNode *clone();
|
||||
void cloneInto(AstNode *other);
|
||||
void delete_children();
|
||||
~AstNode();
|
||||
|
||||
// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
|
||||
// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
|
||||
bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage);
|
||||
void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
|
||||
void replace_ids(std::map<std::string, std::string> &rules);
|
||||
void mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc);
|
||||
void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block);
|
||||
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
|
||||
|
||||
// create a human-readable text representation of the AST (for debugging)
|
||||
void dumpAst(FILE *f, std::string indent, AstNode *other = NULL);
|
||||
void dumpVlog(FILE *f, std::string indent);
|
||||
|
||||
// create RTLIL code for this AST node
|
||||
// for expressions the resulting signal vector is returned
|
||||
// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
|
||||
RTLIL::SigSpec genRTLIL(int width_hint = -1);
|
||||
RTLIL::SigSpec genWidthRTLIL(int width, RTLIL::SigSpec *subst_from = NULL, RTLIL::SigSpec *subst_to = NULL);
|
||||
|
||||
// compare AST nodes
|
||||
bool operator==(const AstNode &other) const;
|
||||
bool operator!=(const AstNode &other) const;
|
||||
bool contains(const AstNode *other) const;
|
||||
|
||||
// helper functions for creating AST nodes for constants
|
||||
static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
|
||||
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
|
||||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast = false, bool dump_ast_diff = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
// therfore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||
struct AstModule : RTLIL::Module {
|
||||
AstNode *ast;
|
||||
bool nolatches, nomem2reg;
|
||||
virtual ~AstModule();
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters);
|
||||
virtual void update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes);
|
||||
};
|
||||
|
||||
// this must be set by the language frontend before parsing the sources
|
||||
// the AstNode constructor then uses current_filename and get_line_num()
|
||||
// to initialize the filename and linenum properties of new nodes
|
||||
extern std::string current_filename;
|
||||
extern void (*set_line_num)(int);
|
||||
extern int (*get_line_num)();
|
||||
|
||||
// set set_line_num and get_line_num to internal dummy functions
|
||||
// (done by simplify(), AstModule::derive and AstModule::update_auto_wires to control
|
||||
// the filename and linenum properties of new nodes not generated by a frontend parser)
|
||||
void use_internal_line_num();
|
||||
}
|
||||
|
||||
namespace AST_INTERNAL
|
||||
{
|
||||
// internal state variables
|
||||
extern bool flag_dump_ast, flag_dump_ast_diff, flag_nolatches, flag_nomem2reg;
|
||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
||||
extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to;
|
||||
extern AST::AstNode *current_top_block, *current_block, *current_block_child;
|
||||
extern AST::AstModule *current_module;
|
||||
struct ProcessGenerator;
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
|||
|
||||
GENFILES += frontends/ilang/parser.tab.cc
|
||||
GENFILES += frontends/ilang/parser.tab.h
|
||||
GENFILES += frontends/ilang/parser.output
|
||||
GENFILES += frontends/ilang/lexer.cc
|
||||
|
||||
frontends/ilang/parser.tab.cc frontends/ilang/parser.tab.h: frontends/ilang/parser.y
|
||||
bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y
|
||||
mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc
|
||||
|
||||
frontends/ilang/lexer.cc: frontends/ilang/lexer.l
|
||||
flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l
|
||||
|
||||
OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o
|
||||
OBJS += frontends/ilang/ilang_frontend.o
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ilang_frontend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
void rtlil_frontend_ilang_yyerror(char const *s)
|
||||
{
|
||||
log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s);
|
||||
}
|
||||
|
||||
struct IlangFrontend : public Frontend {
|
||||
IlangFrontend() : Frontend("ilang") { }
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing ILANG frontend.\n");
|
||||
extra_args(f, filename, args, 1);
|
||||
log("Input filename: %s\n", filename.c_str());
|
||||
|
||||
ILANG_FRONTEND::current_design = design;
|
||||
rtlil_frontend_ilang_yydebug = false;
|
||||
rtlil_frontend_ilang_yyrestart(f);
|
||||
rtlil_frontend_ilang_yyparse();
|
||||
rtlil_frontend_ilang_yylex_destroy();
|
||||
}
|
||||
} IlangFrontend;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ILANG_FRONTEND_H
|
||||
#define ILANG_FRONTEND_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ILANG_FRONTEND {
|
||||
void ilang_frontend(FILE *f, RTLIL::Design *design);
|
||||
extern RTLIL::Design *current_design;
|
||||
}
|
||||
|
||||
extern int rtlil_frontend_ilang_yydebug;
|
||||
int rtlil_frontend_ilang_yylex(void);
|
||||
void rtlil_frontend_ilang_yyerror(char const *s);
|
||||
void rtlil_frontend_ilang_yyrestart(FILE *f);
|
||||
int rtlil_frontend_ilang_yyparse(void);
|
||||
void rtlil_frontend_ilang_yylex_destroy(void);
|
||||
int rtlil_frontend_ilang_yyget_lineno(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
#include "kernel/rtlil.h"
|
||||
#include "parser.tab.h"
|
||||
%}
|
||||
|
||||
%option yylineno
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option prefix="rtlil_frontend_ilang_yy"
|
||||
|
||||
%x STRING
|
||||
|
||||
%%
|
||||
|
||||
"module" { return TOK_MODULE; }
|
||||
"attribute" { return TOK_ATTRIBUTE; }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"memory" { return TOK_MEMORY; }
|
||||
"auto" { return TOK_AUTO; }
|
||||
"width" { return TOK_WIDTH; }
|
||||
"offset" { return TOK_OFFSET; }
|
||||
"size" { return TOK_SIZE; }
|
||||
"input" { return TOK_INPUT; }
|
||||
"output" { return TOK_OUTPUT; }
|
||||
"inout" { return TOK_INOUT; }
|
||||
"cell" { return TOK_CELL; }
|
||||
"connect" { return TOK_CONNECT; }
|
||||
"switch" { return TOK_SWITCH; }
|
||||
"case" { return TOK_CASE; }
|
||||
"assign" { return TOK_ASSIGN; }
|
||||
"sync" { return TOK_SYNC; }
|
||||
"low" { return TOK_LOW; }
|
||||
"high" { return TOK_HIGH; }
|
||||
"posedge" { return TOK_POSEDGE; }
|
||||
"negedge" { return TOK_NEGEDGE; }
|
||||
"edge" { return TOK_EDGE; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"update" { return TOK_UPDATE; }
|
||||
"process" { return TOK_PROCESS; }
|
||||
"end" { return TOK_END; }
|
||||
|
||||
[a-z]+ { return TOK_INVALID; }
|
||||
|
||||
"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
|
||||
[0-9]+'[01xzm-]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
|
||||
[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
|
||||
|
||||
\" { BEGIN(STRING); }
|
||||
<STRING>\\. { yymore(); }
|
||||
<STRING>\" {
|
||||
BEGIN(0);
|
||||
char *yystr = strdup(yytext);
|
||||
yystr[strlen(yytext) - 1] = 0;
|
||||
int i = 0, j = 0;
|
||||
while (yystr[i]) {
|
||||
if (yystr[i] == '\\' && yystr[i + 1]) {
|
||||
i++;
|
||||
if (yystr[i] == 'n')
|
||||
yystr[i] = '\n';
|
||||
else if (yystr[i] == 't')
|
||||
yystr[i] = '\t';
|
||||
else if ('0' <= yystr[i] && yystr[i] <= '7') {
|
||||
yystr[i] = yystr[i] - '0';
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yystr[j++] = yystr[i++];
|
||||
}
|
||||
yystr[j] = 0;
|
||||
rtlil_frontend_ilang_yylval.string = yystr;
|
||||
return TOK_STRING;
|
||||
}
|
||||
<STRING>. { yymore(); }
|
||||
|
||||
"#"[^\n]*\n /* ignore comments */
|
||||
[ \t] /* ignore non-newline whitespaces */
|
||||
[\r\n]+ { return TOK_EOL; }
|
||||
|
||||
. { return *yytext; }
|
||||
|
||||
%%
|
||||
|
||||
// this is a hack to avoid the 'yyinput defined but not used' error msgs
|
||||
void *rtlil_frontend_ilang_avoid_input_warnings() {
|
||||
return (void*)&yyinput;
|
||||
}
|
||||
|
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <list>
|
||||
#include "ilang_frontend.h"
|
||||
namespace ILANG_FRONTEND {
|
||||
RTLIL::Design *current_design;
|
||||
RTLIL::Module *current_module;
|
||||
RTLIL::Wire *current_wire;
|
||||
RTLIL::Memory *current_memory;
|
||||
RTLIL::Cell *current_cell;
|
||||
RTLIL::Process *current_process;
|
||||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
}
|
||||
using namespace ILANG_FRONTEND;
|
||||
%}
|
||||
|
||||
%name-prefix="rtlil_frontend_ilang_yy"
|
||||
|
||||
%union {
|
||||
char *string;
|
||||
int integer;
|
||||
RTLIL::Const *data;
|
||||
RTLIL::SigSpec *sigspec;
|
||||
}
|
||||
|
||||
%token <string> TOK_ID TOK_VALUE TOK_STRING
|
||||
%token <integer> TOK_INT
|
||||
%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
|
||||
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS
|
||||
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_AUTO TOK_MEMORY TOK_SIZE
|
||||
|
||||
%type <sigspec> sigspec sigspec_list
|
||||
%type <integer> sync_type
|
||||
%type <data> constant
|
||||
|
||||
%expect 0
|
||||
%debug
|
||||
|
||||
%%
|
||||
|
||||
input:
|
||||
optional_eol {
|
||||
attrbuf.clear();
|
||||
} design {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
};
|
||||
|
||||
optional_eol:
|
||||
optional_eol TOK_EOL | /* empty */;
|
||||
|
||||
design:
|
||||
design module |
|
||||
design attr_stmt |
|
||||
/* empty */;
|
||||
|
||||
module:
|
||||
TOK_MODULE TOK_ID TOK_EOL {
|
||||
if (current_design->modules.count($2) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = $2;
|
||||
current_module->attributes = attrbuf;
|
||||
current_design->modules[$2] = current_module;
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
} module_body TOK_END {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
} TOK_EOL;
|
||||
|
||||
module_body:
|
||||
module_body module_stmt |
|
||||
/* empty */;
|
||||
|
||||
module_stmt:
|
||||
attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
|
||||
|
||||
attr_stmt:
|
||||
TOK_ATTRIBUTE TOK_ID constant TOK_EOL {
|
||||
attrbuf[$2] = *$3;
|
||||
delete $3;
|
||||
};
|
||||
|
||||
wire_stmt:
|
||||
TOK_WIRE {
|
||||
current_wire = new RTLIL::Wire;
|
||||
current_wire->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} wire_options TOK_ID TOK_EOL {
|
||||
if (current_module->wires.count($4) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_wire->name = $4;
|
||||
current_module->wires[$4] = current_wire;
|
||||
free($4);
|
||||
};
|
||||
|
||||
wire_options:
|
||||
wire_options TOK_AUTO {
|
||||
current_wire->auto_width = true;
|
||||
} |
|
||||
wire_options TOK_WIDTH TOK_INT {
|
||||
current_wire->width = $3;
|
||||
} |
|
||||
wire_options TOK_OFFSET TOK_INT {
|
||||
current_wire->start_offset = $3;
|
||||
} |
|
||||
wire_options TOK_INPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = false;
|
||||
} |
|
||||
wire_options TOK_OUTPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = false;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
wire_options TOK_INOUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
memory_stmt:
|
||||
TOK_MEMORY {
|
||||
current_memory = new RTLIL::Memory;
|
||||
current_memory->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} memory_options TOK_ID TOK_EOL {
|
||||
if (current_module->memories.count($4) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_memory->name = $4;
|
||||
current_module->memories[$4] = current_memory;
|
||||
free($4);
|
||||
};
|
||||
|
||||
memory_options:
|
||||
memory_options TOK_WIDTH TOK_INT {
|
||||
current_wire->width = $3;
|
||||
} |
|
||||
memory_options TOK_SIZE TOK_INT {
|
||||
current_memory->size = $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
cell_stmt:
|
||||
TOK_CELL TOK_ID TOK_ID TOK_EOL {
|
||||
if (current_module->cells.count($3) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_cell = new RTLIL::Cell;
|
||||
current_cell->type = $2;
|
||||
current_cell->name = $3;
|
||||
current_cell->attributes = attrbuf;
|
||||
current_module->cells[$3] = current_cell;
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
free($3);
|
||||
} cell_body TOK_END TOK_EOL;
|
||||
|
||||
cell_body:
|
||||
cell_body TOK_PARAMETER TOK_ID constant TOK_EOL {
|
||||
current_cell->parameters[$3] = *$4;
|
||||
free($3);
|
||||
delete $4;
|
||||
} |
|
||||
cell_body TOK_CONNECT TOK_ID sigspec TOK_EOL {
|
||||
if (current_cell->connections.count($3) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_cell->connections[$3] = *$4;
|
||||
delete $4;
|
||||
free($3);
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
proc_stmt:
|
||||
TOK_PROCESS TOK_ID TOK_EOL {
|
||||
if (current_module->processes.count($2) != 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
current_process = new RTLIL::Process;
|
||||
current_process->name = $2;
|
||||
current_process->attributes = attrbuf;
|
||||
current_module->processes[$2] = current_process;
|
||||
switch_stack.clear();
|
||||
switch_stack.push_back(¤t_process->root_case.switches);
|
||||
case_stack.clear();
|
||||
case_stack.push_back(¤t_process->root_case);
|
||||
free($2);
|
||||
} case_body sync_list TOK_END TOK_EOL;
|
||||
|
||||
switch_stmt:
|
||||
attr_list TOK_SWITCH sigspec TOK_EOL {
|
||||
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
|
||||
rule->signal = *$3;
|
||||
rule->attributes = attrbuf;
|
||||
switch_stack.back()->push_back(rule);
|
||||
attrbuf.clear();
|
||||
delete $3;
|
||||
} switch_body TOK_END TOK_EOL;
|
||||
|
||||
attr_list:
|
||||
/* empty */ |
|
||||
attr_list attr_stmt;
|
||||
|
||||
switch_body:
|
||||
switch_body TOK_CASE {
|
||||
RTLIL::CaseRule *rule = new RTLIL::CaseRule;
|
||||
switch_stack.back()->back()->cases.push_back(rule);
|
||||
switch_stack.push_back(&rule->switches);
|
||||
case_stack.push_back(rule);
|
||||
} compare_list TOK_EOL case_body {
|
||||
switch_stack.pop_back();
|
||||
case_stack.pop_back();
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
compare_list:
|
||||
sigspec {
|
||||
case_stack.back()->compare.push_back(*$1);
|
||||
delete $1;
|
||||
} |
|
||||
compare_list ',' sigspec {
|
||||
case_stack.back()->compare.push_back(*$3);
|
||||
delete $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
case_body:
|
||||
switch_stmt case_body |
|
||||
assign_stmt case_body |
|
||||
/* empty */;
|
||||
|
||||
assign_stmt:
|
||||
TOK_ASSIGN sigspec sigspec TOK_EOL {
|
||||
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
||||
sync_list:
|
||||
sync_list TOK_SYNC sync_type sigspec TOK_EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType($3);
|
||||
rule->signal = *$4;
|
||||
current_process->syncs.push_back(rule);
|
||||
delete $4;
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_ALWAYS TOK_EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STa;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
/* empty */;
|
||||
|
||||
sync_type:
|
||||
TOK_LOW { $$ = RTLIL::ST0; } |
|
||||
TOK_HIGH { $$ = RTLIL::ST1; } |
|
||||
TOK_POSEDGE { $$ = RTLIL::STp; } |
|
||||
TOK_NEGEDGE { $$ = RTLIL::STn; } |
|
||||
TOK_EDGE { $$ = RTLIL::STe; };
|
||||
|
||||
update_list:
|
||||
update_list TOK_UPDATE sigspec sigspec TOK_EOL {
|
||||
current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4));
|
||||
delete $3;
|
||||
delete $4;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
constant:
|
||||
TOK_VALUE {
|
||||
char *ep;
|
||||
int width = strtol($1, &ep, 10);
|
||||
std::list<RTLIL::State> bits;
|
||||
while (*(++ep) != 0) {
|
||||
RTLIL::State bit = RTLIL::Sx;
|
||||
switch (*ep) {
|
||||
case '0': bit = RTLIL::S0; break;
|
||||
case '1': bit = RTLIL::S1; break;
|
||||
case 'x': bit = RTLIL::Sx; break;
|
||||
case 'z': bit = RTLIL::Sz; break;
|
||||
case '-': bit = RTLIL::Sa; break;
|
||||
case 'm': bit = RTLIL::Sm; break;
|
||||
}
|
||||
bits.push_front(bit);
|
||||
}
|
||||
if (bits.size() == 0)
|
||||
bits.push_back(RTLIL::Sx);
|
||||
while ((int)bits.size() < width) {
|
||||
RTLIL::State bit = bits.back();
|
||||
if (bit == RTLIL::S1)
|
||||
bit = RTLIL::S0;
|
||||
bits.push_back(bit);
|
||||
}
|
||||
while ((int)bits.size() > width)
|
||||
bits.pop_back();
|
||||
$$ = new RTLIL::Const;
|
||||
for (auto it = bits.begin(); it != bits.end(); it++)
|
||||
$$->bits.push_back(*it);
|
||||
free($1);
|
||||
} |
|
||||
TOK_INT {
|
||||
$$ = new RTLIL::Const($1, 32);
|
||||
} |
|
||||
TOK_STRING {
|
||||
$$ = new RTLIL::Const($1);
|
||||
free($1);
|
||||
};
|
||||
|
||||
sigspec:
|
||||
constant {
|
||||
RTLIL::SigChunk chunk;
|
||||
chunk.wire = NULL;
|
||||
chunk.width = $1->bits.size();
|
||||
chunk.offset = 0;
|
||||
chunk.data = *$1;
|
||||
$$ = new RTLIL::SigSpec;
|
||||
$$->chunks.push_back(chunk);
|
||||
$$->width = chunk.width;
|
||||
delete $1;
|
||||
} |
|
||||
TOK_ID {
|
||||
if (current_module->wires.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
RTLIL::SigChunk chunk;
|
||||
chunk.wire = current_module->wires[$1];
|
||||
chunk.width = current_module->wires[$1]->width;
|
||||
chunk.offset = 0;
|
||||
$$ = new RTLIL::SigSpec;
|
||||
$$->chunks.push_back(chunk);
|
||||
$$->width = chunk.width;
|
||||
free($1);
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ']' {
|
||||
if (current_module->wires.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
RTLIL::SigChunk chunk;
|
||||
chunk.wire = current_module->wires[$1];
|
||||
chunk.offset = $3;
|
||||
chunk.width = 1;
|
||||
$$ = new RTLIL::SigSpec;
|
||||
$$->chunks.push_back(chunk);
|
||||
$$->width = 1;
|
||||
free($1);
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ':' TOK_INT ']' {
|
||||
if (current_module->wires.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror("scope error");
|
||||
RTLIL::SigChunk chunk;
|
||||
chunk.wire = current_module->wires[$1];
|
||||
chunk.width = $3 - $5 + 1;
|
||||
chunk.offset = $5;
|
||||
$$ = new RTLIL::SigSpec;
|
||||
$$->chunks.push_back(chunk);
|
||||
$$->width = chunk.width;
|
||||
free($1);
|
||||
} |
|
||||
'{' sigspec_list '}' {
|
||||
$$ = $2;
|
||||
};
|
||||
|
||||
sigspec_list:
|
||||
sigspec_list sigspec {
|
||||
$$ = new RTLIL::SigSpec;
|
||||
for (auto it = $2->chunks.begin(); it != $2->chunks.end(); it++) {
|
||||
$$->chunks.push_back(*it);
|
||||
$$->width += it->width;
|
||||
}
|
||||
for (auto it = $1->chunks.begin(); it != $1->chunks.end(); it++) {
|
||||
$$->chunks.push_back(*it);
|
||||
$$->width += it->width;
|
||||
}
|
||||
delete $1;
|
||||
delete $2;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = new RTLIL::SigSpec;
|
||||
};
|
||||
|
||||
conn_stmt:
|
||||
TOK_CONNECT sigspec sigspec TOK_EOL {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
current_module->connections.push_back(RTLIL::SigSig(*$2, *$3));
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
GENFILES += frontends/verilog/parser.tab.cc
|
||||
GENFILES += frontends/verilog/parser.tab.h
|
||||
GENFILES += frontends/verilog/parser.output
|
||||
GENFILES += frontends/verilog/lexer.cc
|
||||
|
||||
frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: frontends/verilog/parser.y
|
||||
bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y
|
||||
mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc
|
||||
|
||||
frontends/verilog/lexer.cc: frontends/verilog/lexer.l
|
||||
flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l
|
||||
|
||||
OBJS += frontends/verilog/parser.tab.o
|
||||
OBJS += frontends/verilog/lexer.o
|
||||
OBJS += frontends/verilog/preproc.o
|
||||
OBJS += frontends/verilog/verilog_frontend.o
|
||||
OBJS += frontends/verilog/const2ast.o
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This file contains an ad-hoc parser for Verilog constants. The Verilog
|
||||
* lexer does only recognize a constant but does not actually split it to its
|
||||
* components. I.e. it just passes the Verilog code for the constant to the
|
||||
* bison parser. The parser then uses the function const2ast() from this file
|
||||
* to create an AST node for the constant.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "kernel/log.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
using namespace AST;
|
||||
|
||||
// divide an arbitrary length decimal number by two and return the rest
|
||||
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
|
||||
{
|
||||
int carry = 0;
|
||||
for (size_t i = 0; i < digits.size(); i++) {
|
||||
assert(digits[i] < 10);
|
||||
digits[i] += carry * 10;
|
||||
carry = digits[i] % 2;
|
||||
digits[i] /= 2;
|
||||
}
|
||||
return carry;
|
||||
}
|
||||
|
||||
// find the number of significant bits in a binary number (not including the sign bit)
|
||||
static int my_ilog2(int x)
|
||||
{
|
||||
int ret = 0;
|
||||
while (x != 0 && x != -1) {
|
||||
x = x >> 1;
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
|
||||
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
|
||||
{
|
||||
// all digits in string (MSB at index 0)
|
||||
std::vector<uint8_t> digits;
|
||||
|
||||
while (*str) {
|
||||
if ('0' <= *str && *str <= '9')
|
||||
digits.push_back(*str - '0');
|
||||
else if ('a' <= *str && *str <= 'f')
|
||||
digits.push_back(10 + *str - 'a');
|
||||
else if ('A' <= *str && *str <= 'F')
|
||||
digits.push_back(10 + *str - 'A');
|
||||
else if (*str == 'x' || *str == 'X')
|
||||
digits.push_back(0xf0);
|
||||
else if (*str == 'z' || *str == 'Z')
|
||||
digits.push_back(0xf1);
|
||||
else if (*str == '?')
|
||||
digits.push_back(0xf2);
|
||||
str++;
|
||||
}
|
||||
|
||||
if (base == 10) {
|
||||
data.clear();
|
||||
if (len_in_bits < 0)
|
||||
len_in_bits = ceil(digits.size()/log10(2));
|
||||
for (int i = 0; i < len_in_bits; i++)
|
||||
data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
|
||||
return;
|
||||
}
|
||||
|
||||
int bits_per_digit = my_ilog2(base-1);
|
||||
if (len_in_bits < 0)
|
||||
len_in_bits = digits.size() * bits_per_digit;
|
||||
|
||||
data.clear();
|
||||
data.resize(len_in_bits);
|
||||
|
||||
for (int i = 0; i < len_in_bits; i++) {
|
||||
int bitmask = 1 << (i % bits_per_digit);
|
||||
int digitidx = digits.size() - (i / bits_per_digit) - 1;
|
||||
if (digitidx < 0) {
|
||||
if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa))
|
||||
data[i] = data[i-1];
|
||||
else
|
||||
data[i] = RTLIL::S0;
|
||||
} else if (digits[digitidx] == 0xf0)
|
||||
data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx;
|
||||
else if (digits[digitidx] == 0xf1)
|
||||
data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz;
|
||||
else if (digits[digitidx] == 0xf2)
|
||||
data[i] = RTLIL::Sa;
|
||||
else
|
||||
data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0;
|
||||
}
|
||||
}
|
||||
|
||||
// convert the verilog code for a constant to an AST node
|
||||
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type)
|
||||
{
|
||||
const char *str = code.c_str();
|
||||
|
||||
// Strings
|
||||
if (*str == '"') {
|
||||
int len = strlen(str) - 2;
|
||||
std::vector<RTLIL::State> data;
|
||||
data.reserve(len * 8);
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char ch = str[len - i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
|
||||
ch = ch >> 1;
|
||||
}
|
||||
}
|
||||
AstNode *ast = AstNode::mkconst_bits(data, false);
|
||||
ast->str = code;
|
||||
return ast;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < code.size(); i++)
|
||||
if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
|
||||
code.erase(code.begin()+(i--));
|
||||
str = code.c_str();
|
||||
|
||||
char *endptr;
|
||||
long intval = strtol(str, &endptr, 10);
|
||||
|
||||
// Simple 32 bit integer
|
||||
if (*endptr == 0)
|
||||
return AstNode::mkconst_int(intval, true);
|
||||
|
||||
// variable length constant
|
||||
if (str == endptr)
|
||||
intval = -1;
|
||||
|
||||
// The "<bits>'[bodh]<digits>" syntax
|
||||
if (*endptr == '\'')
|
||||
{
|
||||
int len_in_bits = intval;
|
||||
std::vector<RTLIL::State> data;
|
||||
bool is_signed = false;
|
||||
if (*(endptr+1) == 's') {
|
||||
is_signed = true;
|
||||
endptr++;
|
||||
}
|
||||
switch (*(endptr+1))
|
||||
{
|
||||
case 'b':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
|
||||
break;
|
||||
case 'o':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
|
||||
break;
|
||||
case 'd':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
|
||||
break;
|
||||
case 'h':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
return AstNode::mkconst_bits(data, is_signed);
|
||||
}
|
||||
|
||||
error:
|
||||
log_error("Value conversion failed: `%s'\n", code.c_str());
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A simple lexer for Verilog code. Non-preprocessor compiler directives are
|
||||
* handled here. The preprocessor stuff is handled in preproc.cc. Everything
|
||||
* else is left to the bison parser (see parser.y).
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "verilog_frontend.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
#include "parser.tab.h"
|
||||
|
||||
using namespace AST;
|
||||
using namespace VERILOG_FRONTEND;
|
||||
|
||||
namespace VERILOG_FRONTEND {
|
||||
std::vector<std::string> fn_stack;
|
||||
std::vector<int> ln_stack;
|
||||
bool lexer_feature_defattr;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%option yylineno
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option prefix="frontend_verilog_yy"
|
||||
|
||||
%x COMMENT
|
||||
%x STRING
|
||||
%x SYNOPSYS_TRANSLATE_OFF
|
||||
%x SYNOPSYS_FLAGS
|
||||
|
||||
%%
|
||||
|
||||
"`file_push "[^\n]* {
|
||||
fn_stack.push_back(current_filename);
|
||||
ln_stack.push_back(frontend_verilog_yyget_lineno());
|
||||
current_filename = yytext+11;
|
||||
frontend_verilog_yyset_lineno(0);
|
||||
}
|
||||
|
||||
"`file_pop"[^\n]*\n {
|
||||
current_filename = fn_stack.back();
|
||||
frontend_verilog_yyset_lineno(ln_stack.back());
|
||||
}
|
||||
|
||||
"`file_notfound "[^\n]* {
|
||||
log_error("Can't open include file `%s'!\n", yytext + 15);
|
||||
}
|
||||
|
||||
"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */
|
||||
|
||||
"`yosys_enable_defattr" lexer_feature_defattr = true;
|
||||
"`yosys_disable_defattr" lexer_feature_defattr = false;
|
||||
|
||||
"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
|
||||
}
|
||||
|
||||
"module" { return TOK_MODULE; }
|
||||
"endmodule" { return TOK_ENDMODULE; }
|
||||
"function" { return TOK_FUNCTION; }
|
||||
"endfunction" { return TOK_ENDFUNCTION; }
|
||||
"task" { return TOK_TASK; }
|
||||
"endtask" { return TOK_ENDTASK; }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"localparam" { return TOK_LOCALPARAM; }
|
||||
"assign" { return TOK_ASSIGN; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"initial" { return TOK_INITIAL; }
|
||||
"begin" { return TOK_BEGIN; }
|
||||
"end" { return TOK_END; }
|
||||
"if" { return TOK_IF; }
|
||||
"else" { return TOK_ELSE; }
|
||||
"for" { return TOK_FOR; }
|
||||
"posedge" { return TOK_POSEDGE; }
|
||||
"negedge" { return TOK_NEGEDGE; }
|
||||
"or" { return TOK_OR; }
|
||||
"case" { return TOK_CASE; }
|
||||
"casex" { return TOK_CASEX; }
|
||||
"casez" { return TOK_CASEZ; }
|
||||
"endcase" { return TOK_ENDCASE; }
|
||||
"default" { return TOK_DEFAULT; }
|
||||
"generate" { return TOK_GENERATE; }
|
||||
"endgenerate" { return TOK_ENDGENERATE; }
|
||||
|
||||
"input" { return TOK_INPUT; }
|
||||
"output" { return TOK_OUTPUT; }
|
||||
"inout" { return TOK_INOUT; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"reg" { return TOK_REG; }
|
||||
"integer" { return TOK_INTEGER; }
|
||||
"signed" { return TOK_SIGNED; }
|
||||
"genvar" { return TOK_GENVAR; }
|
||||
|
||||
[0-9]+ {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_CONST;
|
||||
}
|
||||
|
||||
[0-9]*[ \t]*\'s?[bodh][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_CONST;
|
||||
}
|
||||
|
||||
\" { BEGIN(STRING); }
|
||||
<STRING>\\. { yymore(); }
|
||||
<STRING>\" {
|
||||
BEGIN(0);
|
||||
char *yystr = strdup(yytext);
|
||||
yystr[strlen(yytext) - 1] = 0;
|
||||
int i = 0, j = 0;
|
||||
while (yystr[i]) {
|
||||
if (yystr[i] == '\\' && yystr[i + 1]) {
|
||||
i++;
|
||||
if (yystr[i] == 'n')
|
||||
yystr[i] = '\n';
|
||||
else if (yystr[i] == 't')
|
||||
yystr[i] = '\t';
|
||||
else if ('0' <= yystr[i] && yystr[i] <= '7') {
|
||||
yystr[i] = yystr[i] - '0';
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yystr[j++] = yystr[i++];
|
||||
}
|
||||
yystr[j] = 0;
|
||||
frontend_verilog_yylval.string = new std::string(yystr);
|
||||
free(yystr);
|
||||
return TOK_STRING;
|
||||
}
|
||||
<STRING>. { yymore(); }
|
||||
|
||||
and|nand|or|nor|xor|xnor|not|buf {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_PRIMITIVE;
|
||||
}
|
||||
|
||||
supply0 { return TOK_SUPPLY0; }
|
||||
supply1 { return TOK_SUPPLY1; }
|
||||
|
||||
"$"(display|time|stop|finish) {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
"$signed" { return TOK_TO_SIGNED; }
|
||||
"$unsigned" { return TOK_TO_UNSIGNED; }
|
||||
|
||||
[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
"/*"[ \t]*synopsys[ \t]*translate_off[ \t]*"*/" {
|
||||
log("Warning: Found one of those horrible `synopsys translate_off' comments.\n");
|
||||
log("It is strongly suggested to use `ifdef constructs instead!\n");
|
||||
BEGIN(SYNOPSYS_TRANSLATE_OFF);
|
||||
}
|
||||
<SYNOPSYS_TRANSLATE_OFF>. /* ignore synopsys translate_off body */
|
||||
<SYNOPSYS_TRANSLATE_OFF>\n /* ignore synopsys translate_off body */
|
||||
<SYNOPSYS_TRANSLATE_OFF>"/*"[ \t]*"synopsys"[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); }
|
||||
|
||||
"/*"[ \t]*"synopsys"[ \t]+ {
|
||||
BEGIN(SYNOPSYS_FLAGS);
|
||||
}
|
||||
<SYNOPSYS_FLAGS>full_case {
|
||||
log("Warning: Found one of those horrible `synopsys full_case' comments.\n");
|
||||
log("It is strongly suggested to use verilog x-values and default branches instead!\n");
|
||||
return TOK_SYNOPSYS_FULL_CASE;
|
||||
}
|
||||
<SYNOPSYS_FLAGS>parallel_case {
|
||||
log("Warning: Found one of those horrible `synopsys parallel_case' comments.\n");
|
||||
log("It is strongly suggested to use verilog `parallel_case' attributes instead!\n");
|
||||
return TOK_SYNOPSYS_PARALLEL_CASE;
|
||||
}
|
||||
<SYNOPSYS_FLAGS>. /* ignore everything else */
|
||||
<SYNOPSYS_FLAGS>"*/" { BEGIN(0); }
|
||||
|
||||
"\\"[^ \t\r\n]+ {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
"(*" { return ATTR_BEGIN; }
|
||||
"*)" { return ATTR_END; }
|
||||
|
||||
"{*" { if (lexer_feature_defattr) return DEFATTR_BEGIN; else REJECT; }
|
||||
"*}" { if (lexer_feature_defattr) return DEFATTR_END; else REJECT; }
|
||||
|
||||
"**" { return OP_POW; }
|
||||
"||" { return OP_LOR; }
|
||||
"&&" { return OP_LAND; }
|
||||
"==" { return OP_EQ; }
|
||||
"!=" { return OP_NE; }
|
||||
"<=" { return OP_LE; }
|
||||
">=" { return OP_GE; }
|
||||
|
||||
/* "~&" { return OP_NAND; } */
|
||||
/* "~|" { return OP_NOR; } */
|
||||
"~^" { return OP_XNOR; }
|
||||
"^~" { return OP_XNOR; }
|
||||
|
||||
"<<" { return OP_SHL; }
|
||||
">>" { return OP_SHR; }
|
||||
"<<<" { return OP_SSHL; }
|
||||
">>>" { return OP_SSHR; }
|
||||
|
||||
"/*" { BEGIN(COMMENT); }
|
||||
<COMMENT>. /* ignore comment body */
|
||||
<COMMENT>\n /* ignore comment body */
|
||||
<COMMENT>"*/" { BEGIN(0); }
|
||||
|
||||
[ \t\r\n] /* ignore whitespaces */
|
||||
\\[\r\n] /* ignore continuation sequence */
|
||||
"//"[^\r\n]* /* ignore one-line comments */
|
||||
"#"[$a-zA-Z_0-9\.]+ /* ignore simulation timings */
|
||||
|
||||
. { return *yytext; }
|
||||
|
||||
%%
|
||||
|
||||
// this is a hack to avoid the 'yyinput defined but not used' error msgs
|
||||
void *frontend_verilog_avoid_input_warnings() {
|
||||
return (void*)&yyinput;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* Ad-hoc implementation of a Verilog preprocessor. The directives `define,
|
||||
* `include, `ifdef, `ifndef, `else and `endif are handled here. All other
|
||||
* directives are handled by the lexer (see lexer.l).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
|
||||
static std::list<std::string> output_code;
|
||||
static std::list<std::string> input_buffer;
|
||||
static size_t input_buffer_charp;
|
||||
|
||||
static void return_char(char ch)
|
||||
{
|
||||
if (input_buffer_charp == 0)
|
||||
input_buffer.push_front(std::string() + ch);
|
||||
else
|
||||
input_buffer.front()[--input_buffer_charp] = ch;
|
||||
}
|
||||
|
||||
static void insert_input(std::string str)
|
||||
{
|
||||
if (input_buffer_charp != 0) {
|
||||
input_buffer.front() = input_buffer.front().substr(input_buffer_charp);
|
||||
input_buffer_charp = 0;
|
||||
}
|
||||
input_buffer.push_front(str);
|
||||
}
|
||||
|
||||
static char next_char()
|
||||
{
|
||||
if (input_buffer.size() == 0)
|
||||
return 0;
|
||||
|
||||
assert(input_buffer_charp <= input_buffer.front().size());
|
||||
if (input_buffer_charp == input_buffer.front().size()) {
|
||||
input_buffer_charp = 0;
|
||||
input_buffer.pop_front();
|
||||
return next_char();
|
||||
}
|
||||
|
||||
char ch = input_buffer.front()[input_buffer_charp++];
|
||||
return ch == '\r' ? next_char() : ch;
|
||||
}
|
||||
|
||||
static void skip_spaces()
|
||||
{
|
||||
while (1) {
|
||||
char ch = next_char();
|
||||
if (ch == 0)
|
||||
break;
|
||||
if (ch != ' ' && ch != '\t') {
|
||||
return_char(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string next_token(bool pass_newline = false)
|
||||
{
|
||||
std::string token;
|
||||
|
||||
char ch = next_char();
|
||||
if (ch == 0)
|
||||
return token;
|
||||
|
||||
token += ch;
|
||||
if (ch == '\n') {
|
||||
if (pass_newline) {
|
||||
output_code.push_back(token);
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
if (ch == ' ' || ch == '\t')
|
||||
{
|
||||
while ((ch = next_char()) != 0) {
|
||||
if (ch != ' ' && ch != '\t') {
|
||||
return_char(ch);
|
||||
break;
|
||||
}
|
||||
token += ch;
|
||||
}
|
||||
}
|
||||
else if (ch == '"')
|
||||
{
|
||||
while ((ch = next_char()) != 0) {
|
||||
token += ch;
|
||||
if (ch == '"')
|
||||
break;
|
||||
if (ch == '\\') {
|
||||
if ((ch = next_char()) != 0)
|
||||
token += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ch == '/')
|
||||
{
|
||||
if ((ch = next_char()) != 0) {
|
||||
if (ch == '/') {
|
||||
token += '*';
|
||||
char last_ch = 0;
|
||||
while ((ch = next_char()) != 0) {
|
||||
if (ch == '\n') {
|
||||
return_char(ch);
|
||||
break;
|
||||
}
|
||||
if (last_ch != '*' || ch != '/') {
|
||||
token += ch;
|
||||
last_ch = ch;
|
||||
}
|
||||
}
|
||||
token += " */";
|
||||
}
|
||||
else if (ch == '*') {
|
||||
token += '*';
|
||||
int newline_count = 0;
|
||||
char last_ch = 0;
|
||||
while ((ch = next_char()) != 0) {
|
||||
if (ch == '\n') {
|
||||
newline_count++;
|
||||
token += ' ';
|
||||
} else
|
||||
token += ch;
|
||||
if (last_ch == '*' && ch == '/')
|
||||
break;
|
||||
last_ch = ch;
|
||||
}
|
||||
while (newline_count-- > 0)
|
||||
return_char('\n');
|
||||
}
|
||||
else
|
||||
return_char(ch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
|
||||
while ((ch = next_char()) != 0) {
|
||||
if (strchr(ok, ch) == NULL) {
|
||||
return_char(ch);
|
||||
break;
|
||||
}
|
||||
token += ch;
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static void input_file(FILE *f, std::string filename)
|
||||
{
|
||||
char buffer[513];
|
||||
int rc;
|
||||
|
||||
insert_input("");
|
||||
auto it = input_buffer.begin();
|
||||
|
||||
input_buffer.insert(it, "`file_push " + filename + "\n");
|
||||
while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) {
|
||||
buffer[rc] = 0;
|
||||
input_buffer.insert(it, buffer);
|
||||
}
|
||||
input_buffer.insert(it, "`file_pop\n");
|
||||
}
|
||||
|
||||
static std::string define_to_feature(std::string defname)
|
||||
{
|
||||
if (defname == "__YOSYS_ENABLE_DEFATTR__")
|
||||
return "defattr";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string frontend_verilog_preproc(FILE *f, std::string filename)
|
||||
{
|
||||
std::map<std::string, std::string> defines_map;
|
||||
int ifdef_fail_level = 0;
|
||||
|
||||
output_code.clear();
|
||||
input_buffer.clear();
|
||||
input_buffer_charp = 0;
|
||||
|
||||
input_file(f, filename);
|
||||
defines_map["__YOSYS__"] = "1";
|
||||
|
||||
while (!input_buffer.empty())
|
||||
{
|
||||
std::string tok = next_token();
|
||||
// printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
|
||||
|
||||
if (tok == "`endif") {
|
||||
if (ifdef_fail_level > 0)
|
||||
ifdef_fail_level--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`else") {
|
||||
if (ifdef_fail_level == 0)
|
||||
ifdef_fail_level = 1;
|
||||
else if (ifdef_fail_level == 1)
|
||||
ifdef_fail_level = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`ifdef") {
|
||||
skip_spaces();
|
||||
std::string name = next_token(true);
|
||||
if (ifdef_fail_level > 0 || defines_map.count(name) == 0)
|
||||
ifdef_fail_level++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`ifndef") {
|
||||
skip_spaces();
|
||||
std::string name = next_token(true);
|
||||
if (ifdef_fail_level > 0 || defines_map.count(name) != 0)
|
||||
ifdef_fail_level++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ifdef_fail_level > 0) {
|
||||
if (tok == "\n")
|
||||
output_code.push_back(tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`include") {
|
||||
skip_spaces();
|
||||
std::string fn = next_token(true);
|
||||
while (1) {
|
||||
size_t pos = fn.find('"');
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
if (pos == 0)
|
||||
fn = fn.substr(1);
|
||||
else
|
||||
fn = fn.substr(0, pos) + fn.substr(pos+1);
|
||||
}
|
||||
FILE *fp = fopen(fn.c_str(), "r");
|
||||
if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
|
||||
std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn;
|
||||
fp = fopen(fn2.c_str(), "r");
|
||||
}
|
||||
if (fp != NULL) {
|
||||
input_file(fp, fn);
|
||||
fclose(fp);
|
||||
} else
|
||||
output_code.push_back("`file_notfound " + fn + "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`define") {
|
||||
std::string name, value;
|
||||
skip_spaces();
|
||||
name = next_token(true);
|
||||
if (!define_to_feature(name).empty())
|
||||
output_code.push_back("`yosys_enable_" + define_to_feature(name));
|
||||
skip_spaces();
|
||||
int newline_count = 0;
|
||||
while (!tok.empty()) {
|
||||
tok = next_token();
|
||||
if (tok == "\n") {
|
||||
return_char('\n');
|
||||
break;
|
||||
}
|
||||
if (tok == "\\") {
|
||||
char ch = next_char();
|
||||
if (ch == '\n') {
|
||||
value += " ";
|
||||
newline_count++;
|
||||
} else {
|
||||
value += std::string("\\");
|
||||
return_char(ch);
|
||||
}
|
||||
} else
|
||||
value += tok;
|
||||
}
|
||||
while (newline_count-- > 0)
|
||||
return_char('\n');
|
||||
// printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
|
||||
defines_map[name] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`undef") {
|
||||
std::string name;
|
||||
skip_spaces();
|
||||
name = next_token(true);
|
||||
if (!define_to_feature(name).empty())
|
||||
output_code.push_back("`yosys_disable_" + define_to_feature(name));
|
||||
// printf("undef: >>%s<<\n", name.c_str());
|
||||
defines_map.erase(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`timescale") {
|
||||
std::string name;
|
||||
skip_spaces();
|
||||
while (!tok.empty() && tok != "\n")
|
||||
tok = next_token(true);
|
||||
if (tok == "\n")
|
||||
return_char('\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
|
||||
// printf("expand: >>%s<< -> >>%s<<\n", tok.c_str(), defines_map[tok.substr(1)].c_str());
|
||||
insert_input(defines_map[tok.substr(1)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
output_code.push_back(tok);
|
||||
}
|
||||
|
||||
std::string output;
|
||||
for (auto &str : output_code)
|
||||
output += str;
|
||||
|
||||
output_code.clear();
|
||||
input_buffer.clear();
|
||||
input_buffer_charp = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/sha1.h"
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace VERILOG_FRONTEND;
|
||||
|
||||
// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL
|
||||
|
||||
struct VerilogFrontend : public Frontend {
|
||||
VerilogFrontend() : Frontend("verilog") { }
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool flag_dump_ast = false;
|
||||
bool flag_dump_ast_diff = false;
|
||||
bool flag_dump_vlog = false;
|
||||
bool flag_nolatches = false;
|
||||
bool flag_nomem2reg = false;
|
||||
bool flag_ppdump = false;
|
||||
bool flag_nopp = false;
|
||||
frontend_verilog_yydebug = false;
|
||||
|
||||
log_header("Executing Verilog-2005 frontend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-dump_ast") {
|
||||
flag_dump_ast = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_ast_diff") {
|
||||
flag_dump_ast = true;
|
||||
flag_dump_ast_diff = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_vlog") {
|
||||
flag_dump_vlog = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-yydebug") {
|
||||
frontend_verilog_yydebug = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nolatches") {
|
||||
flag_nolatches = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nomem2reg") {
|
||||
flag_nomem2reg = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ppdump") {
|
||||
flag_ppdump = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nopp") {
|
||||
flag_nopp = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log("Parsing Verilog input from `%s' to AST representation.\n", filename.c_str());
|
||||
|
||||
AST::current_filename = filename;
|
||||
AST::set_line_num = &frontend_verilog_yyset_lineno;
|
||||
AST::get_line_num = &frontend_verilog_yyget_lineno;
|
||||
|
||||
current_ast = new AST::AstNode(AST::AST_DESIGN);
|
||||
|
||||
FILE *fp = f;
|
||||
std::string code_after_preproc;
|
||||
|
||||
if (!flag_nopp) {
|
||||
code_after_preproc = frontend_verilog_preproc(f, filename);
|
||||
if (flag_ppdump)
|
||||
log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
|
||||
fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r");
|
||||
}
|
||||
|
||||
lexer_feature_defattr = false;
|
||||
|
||||
frontend_verilog_yyset_lineno(1);
|
||||
frontend_verilog_yyrestart(fp);
|
||||
frontend_verilog_yyparse();
|
||||
frontend_verilog_yylex_destroy();
|
||||
|
||||
AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg);
|
||||
|
||||
if (!flag_nopp)
|
||||
fclose(fp);
|
||||
|
||||
delete current_ast;
|
||||
current_ast = NULL;
|
||||
|
||||
log("Successfully finished Verilog frontend.\n");
|
||||
}
|
||||
} VerilogFrontend;
|
||||
|
||||
// the yyerror function used by bison to report parser errors
|
||||
void frontend_verilog_yyerror(char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buffer[1024];
|
||||
char *p = buffer;
|
||||
p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ",
|
||||
AST::current_filename.c_str(), frontend_verilog_yyget_lineno());
|
||||
va_start(ap, fmt);
|
||||
p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap);
|
||||
va_end(ap);
|
||||
p += snprintf(p, buffer + sizeof(buffer) - p, "\n");
|
||||
log_error("%s", buffer);
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VERILOG_FRONTEND_H
|
||||
#define VERILOG_FRONTEND_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace VERILOG_FRONTEND
|
||||
{
|
||||
// this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser
|
||||
extern struct AST::AstNode *current_ast;
|
||||
|
||||
// this function converts a Verilog constant to an AST_CONSTANT node
|
||||
AST::AstNode *const2ast(std::string code, char case_type = 0);
|
||||
|
||||
// lexer state variables
|
||||
extern bool lexer_feature_defattr;
|
||||
}
|
||||
|
||||
// the pre-processor
|
||||
std::string frontend_verilog_preproc(FILE *f, std::string filename);
|
||||
|
||||
// the usual bison/flex stuff
|
||||
extern int frontend_verilog_yydebug;
|
||||
int frontend_verilog_yylex(void);
|
||||
void frontend_verilog_yyerror(char const *fmt, ...);
|
||||
void frontend_verilog_yyrestart(FILE *f);
|
||||
int frontend_verilog_yyparse(void);
|
||||
int frontend_verilog_yylex_destroy(void);
|
||||
int frontend_verilog_yyget_lineno(void);
|
||||
void frontend_verilog_yyset_lineno (int);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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 BITPATTERN_H
|
||||
#define BITPATTERN_H
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/rtlil.h"
|
||||
|
||||
struct BitPatternPool
|
||||
{
|
||||
int width;
|
||||
typedef std::vector<RTLIL::State> bits_t;
|
||||
std::set<bits_t> pool;
|
||||
|
||||
BitPatternPool(RTLIL::SigSpec sig)
|
||||
{
|
||||
width = sig.width;
|
||||
if (width > 0) {
|
||||
std::vector<RTLIL::State> pattern(width);
|
||||
sig.optimize();
|
||||
for (int i = 0; i < width; i++) {
|
||||
RTLIL::SigSpec s = sig.extract(i, 1);
|
||||
s.optimize();
|
||||
assert(s.chunks.size() == 1);
|
||||
if (s.chunks[0].wire == NULL && s.chunks[0].data.bits[0] <= RTLIL::State::S1)
|
||||
pattern[i] = s.chunks[0].data.bits[0];
|
||||
else
|
||||
pattern[i] = RTLIL::State::Sa;
|
||||
}
|
||||
pool.insert(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
BitPatternPool(int width)
|
||||
{
|
||||
this->width = width;
|
||||
if (width > 0) {
|
||||
std::vector<RTLIL::State> pattern(width);
|
||||
for (int i = 0; i < width; i++)
|
||||
pattern[i] = RTLIL::State::Sa;
|
||||
pool.insert(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
bits_t sig2bits(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.optimize();
|
||||
assert(sig.is_fully_const());
|
||||
assert(sig.chunks.size() == 1);
|
||||
bits_t bits = sig.chunks[0].data.bits;
|
||||
for (auto &b : bits)
|
||||
if (b > RTLIL::State::S1)
|
||||
b = RTLIL::State::Sa;
|
||||
return bits;
|
||||
}
|
||||
|
||||
bool match(bits_t a, bits_t b)
|
||||
{
|
||||
assert(int(a.size()) == width);
|
||||
assert(int(b.size()) == width);
|
||||
for (int i = 0; i < width; i++)
|
||||
if (a[i] <= RTLIL::State::S1 && b[i] <= RTLIL::State::S1 && a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_any(RTLIL::SigSpec sig)
|
||||
{
|
||||
bits_t bits = sig2bits(sig);
|
||||
for (auto &it : pool)
|
||||
if (match(it, bits))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_all(RTLIL::SigSpec sig)
|
||||
{
|
||||
bits_t bits = sig2bits(sig);
|
||||
for (auto &it : pool)
|
||||
if (match(it, bits)) {
|
||||
for (int i = 0; i < width; i++)
|
||||
if (bits[i] > RTLIL::State::S1 && it[i] <= RTLIL::State::S1)
|
||||
goto next_pool_entry;
|
||||
return true;
|
||||
next_pool_entry:;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool take(RTLIL::SigSpec sig)
|
||||
{
|
||||
bool status = false;
|
||||
bits_t bits = sig2bits(sig);
|
||||
std::vector<bits_t> pattern_list;
|
||||
for (auto &it : pool)
|
||||
if (match(it, bits))
|
||||
pattern_list.push_back(it);
|
||||
for (auto pattern : pattern_list) {
|
||||
pool.erase(pattern);
|
||||
for (int i = 0; i < width; i++) {
|
||||
if (pattern[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa)
|
||||
continue;
|
||||
bits_t new_pattern = pattern;
|
||||
new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
pool.insert(new_pattern);
|
||||
}
|
||||
status = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool take_all()
|
||||
{
|
||||
if (pool.empty())
|
||||
return false;
|
||||
pool.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
return pool.empty();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* 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/rtlil.h"
|
||||
#include "bigint/BigIntegerLibrary.hh"
|
||||
#include <assert.h>
|
||||
|
||||
static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos)
|
||||
{
|
||||
BigInteger result = 0, this_bit = 1;
|
||||
for (size_t i = 0; i < val.bits.size(); i++) {
|
||||
if (val.bits[i] == RTLIL::State::S1) {
|
||||
if (as_signed && i+1 == val.bits.size())
|
||||
result -= this_bit;
|
||||
else
|
||||
result += this_bit;
|
||||
}
|
||||
else if (val.bits[i] != RTLIL::State::S0) {
|
||||
if (undef_bit_pos < 0)
|
||||
undef_bit_pos = i;
|
||||
}
|
||||
this_bit *= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_bit_pos)
|
||||
{
|
||||
BigUnsigned mag = val.getMagnitude();
|
||||
RTLIL::Const result(0, result_len);
|
||||
|
||||
if (!mag.isZero())
|
||||
{
|
||||
if (val.getSign() < 0)
|
||||
{
|
||||
mag--;
|
||||
for (int i = 0; i < result_len; i++)
|
||||
result.bits[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < result_len; i++)
|
||||
result.bits[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
if (undef_bit_pos >= 0)
|
||||
for (int i = undef_bit_pos; i < result_len; i++)
|
||||
result.bits[i] = RTLIL::State::Sx;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RTLIL::State logic_and(RTLIL::State a, RTLIL::State b)
|
||||
{
|
||||
if (a == RTLIL::State::S0) return RTLIL::State::S0;
|
||||
if (b == RTLIL::State::S0) return RTLIL::State::S0;
|
||||
if (a != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
if (b != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
return RTLIL::State::S1;
|
||||
}
|
||||
|
||||
static RTLIL::State logic_or(RTLIL::State a, RTLIL::State b)
|
||||
{
|
||||
if (a == RTLIL::State::S1) return RTLIL::State::S1;
|
||||
if (b == RTLIL::State::S1) return RTLIL::State::S1;
|
||||
if (a != RTLIL::State::S0) return RTLIL::State::Sx;
|
||||
if (b != RTLIL::State::S0) return RTLIL::State::Sx;
|
||||
return RTLIL::State::S0;
|
||||
}
|
||||
|
||||
static RTLIL::State logic_xor(RTLIL::State a, RTLIL::State b)
|
||||
{
|
||||
if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
return a != b ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
}
|
||||
|
||||
static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b)
|
||||
{
|
||||
if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx;
|
||||
return a == b ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
|
||||
{
|
||||
if (result_len < 0)
|
||||
result_len = arg1.bits.size();
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
for (size_t i = 0; i < size_t(result_len); i++) {
|
||||
if (i >= arg1.bits.size())
|
||||
result.bits[i] = RTLIL::State::S0;
|
||||
else if (arg1.bits[i] == RTLIL::State::S0)
|
||||
result.bits[i] = RTLIL::State::S1;
|
||||
else if (arg1.bits[i] == RTLIL::State::S1)
|
||||
result.bits[i] = RTLIL::State::S0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State),
|
||||
const RTLIL::Const &arg1, const RTLIL::Const &arg2, int result_len = -1)
|
||||
{
|
||||
if (result_len < 0)
|
||||
result_len = std::max(arg1.bits.size(), arg2.bits.size());
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
for (size_t i = 0; i < size_t(result_len); i++) {
|
||||
RTLIL::State a = i < arg1.bits.size() ? arg1.bits[i] : RTLIL::State::S0;
|
||||
RTLIL::State b = i < arg2.bits.size() ? arg2.bits[i] : RTLIL::State::S0;
|
||||
result.bits[i] = logic_func(a, b);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return logic_wrapper(logic_and, arg1, arg2, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return logic_wrapper(logic_or, arg1, arg2, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_xor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return logic_wrapper(logic_xor, arg1, arg2, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_xnor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return logic_wrapper(logic_xnor, arg1, arg2, result_len);
|
||||
}
|
||||
|
||||
static RTLIL::Const logic_reduce_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), const RTLIL::Const &arg1)
|
||||
{
|
||||
RTLIL::State temp = RTLIL::State::S0;
|
||||
|
||||
for (size_t i = 0; i < arg1.bits.size(); i++)
|
||||
temp = logic_func(temp, arg1.bits[i]);
|
||||
|
||||
return RTLIL::Const(temp);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_reduce_and(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int)
|
||||
{
|
||||
return logic_reduce_wrapper(logic_and, arg1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_reduce_or(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int)
|
||||
{
|
||||
return logic_reduce_wrapper(logic_or, arg1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int)
|
||||
{
|
||||
return logic_reduce_wrapper(logic_xor, arg1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int)
|
||||
{
|
||||
return logic_reduce_wrapper(logic_xnor, arg1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_reduce_bool(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int)
|
||||
{
|
||||
return logic_reduce_wrapper(logic_or, arg1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int)
|
||||
{
|
||||
int undef_bit_pos_a = -1;
|
||||
BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
|
||||
|
||||
if (a.isZero()) {
|
||||
if (undef_bit_pos_a >= 0)
|
||||
return RTLIL::Const(RTLIL::State::Sx);
|
||||
return RTLIL::Const(RTLIL::State::S1);
|
||||
}
|
||||
|
||||
return RTLIL::Const(RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos_a = -1, undef_bit_pos_b = -1;
|
||||
BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
|
||||
BigInteger b = const2big(arg2, signed2, undef_bit_pos_b);
|
||||
|
||||
if (a.isZero() || b.isZero()) {
|
||||
if (undef_bit_pos_a >= 0 && undef_bit_pos_b >= 0)
|
||||
return RTLIL::Const(RTLIL::State::Sx);
|
||||
return RTLIL::Const(RTLIL::State::S0);
|
||||
}
|
||||
|
||||
return RTLIL::Const(RTLIL::State::S1);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos_a = -1, undef_bit_pos_b = -1;
|
||||
BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
|
||||
BigInteger b = const2big(arg2, signed2, undef_bit_pos_b);
|
||||
|
||||
if (a.isZero() && b.isZero()) {
|
||||
if (undef_bit_pos_a >= 0 || undef_bit_pos_b >= 0)
|
||||
return RTLIL::Const(RTLIL::State::Sx);
|
||||
return RTLIL::Const(RTLIL::State::S0);
|
||||
}
|
||||
|
||||
return RTLIL::Const(RTLIL::State::S1);
|
||||
}
|
||||
|
||||
static RTLIL::Const const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, int direction, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger offset = const2big(arg2, false, undef_bit_pos) * direction;
|
||||
|
||||
if (result_len < 0)
|
||||
result_len = arg1.bits.size();
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
if (undef_bit_pos >= 0)
|
||||
return result;
|
||||
|
||||
for (int i = 0; i < result_len; i++) {
|
||||
BigInteger pos = BigInteger(i) + offset;
|
||||
if (pos < 0)
|
||||
result.bits[i] = RTLIL::State::S0;
|
||||
else if (pos >= arg1.bits.size())
|
||||
result.bits[i] = sign_ext ? arg1.bits.back() : RTLIL::State::S0;
|
||||
else
|
||||
result.bits[i] = arg1.bits[pos.toInt()];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return const_shift(arg1, arg2, false, -1, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return const_shift(arg1, arg2, false, +1, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return const_shift(arg1, arg2, true, -1, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len)
|
||||
{
|
||||
return const_shift(arg1, arg2, true, +1, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) == const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) != const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos);
|
||||
return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) / const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) % const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
|
||||
BigInteger a = const2big(arg1, signed1, undef_bit_pos);
|
||||
BigInteger b = const2big(arg2, signed2, undef_bit_pos);
|
||||
BigInteger y = 1;
|
||||
|
||||
if (b < 0 || a == 0) {
|
||||
y = 0;
|
||||
} else {
|
||||
while (b > 0) {
|
||||
y = y * a;
|
||||
if (y.getLength() > 0x10000) {
|
||||
undef_bit_pos = 0;
|
||||
break;
|
||||
}
|
||||
b--;
|
||||
}
|
||||
}
|
||||
|
||||
return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
{
|
||||
RTLIL::Const zero(RTLIL::State::S0, 1);
|
||||
return RTLIL::const_add(zero, arg1, false, signed1, result_len);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
{
|
||||
RTLIL::Const zero(RTLIL::State::S0, 1);
|
||||
return RTLIL::const_sub(zero, arg1, false, signed1, result_len);
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* 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 CELLTYPES_H
|
||||
#define CELLTYPES_H
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct CellTypes
|
||||
{
|
||||
std::set<std::string> cell_types;
|
||||
|
||||
void setup_internals()
|
||||
{
|
||||
cell_types.insert("$not");
|
||||
cell_types.insert("$pos");
|
||||
cell_types.insert("$neg");
|
||||
cell_types.insert("$and");
|
||||
cell_types.insert("$or");
|
||||
cell_types.insert("$xor");
|
||||
cell_types.insert("$xnor");
|
||||
cell_types.insert("$reduce_and");
|
||||
cell_types.insert("$reduce_or");
|
||||
cell_types.insert("$reduce_xor");
|
||||
cell_types.insert("$reduce_xnor");
|
||||
cell_types.insert("$reduce_bool");
|
||||
cell_types.insert("$shl");
|
||||
cell_types.insert("$shr");
|
||||
cell_types.insert("$sshl");
|
||||
cell_types.insert("$sshr");
|
||||
cell_types.insert("$lt");
|
||||
cell_types.insert("$le");
|
||||
cell_types.insert("$eq");
|
||||
cell_types.insert("$ne");
|
||||
cell_types.insert("$ge");
|
||||
cell_types.insert("$gt");
|
||||
cell_types.insert("$add");
|
||||
cell_types.insert("$sub");
|
||||
cell_types.insert("$mul");
|
||||
cell_types.insert("$div");
|
||||
cell_types.insert("$mod");
|
||||
cell_types.insert("$pow");
|
||||
cell_types.insert("$logic_not");
|
||||
cell_types.insert("$logic_and");
|
||||
cell_types.insert("$logic_or");
|
||||
cell_types.insert("$mux");
|
||||
cell_types.insert("$pmux");
|
||||
cell_types.insert("$safe_pmux");
|
||||
}
|
||||
|
||||
void setup_internals_mem()
|
||||
{
|
||||
cell_types.insert("$dff");
|
||||
cell_types.insert("$adff");
|
||||
cell_types.insert("$memrd");
|
||||
cell_types.insert("$memwr");
|
||||
cell_types.insert("$mem");
|
||||
cell_types.insert("$fsm");
|
||||
}
|
||||
|
||||
void setup_stdcells()
|
||||
{
|
||||
cell_types.insert("$_INV_");
|
||||
cell_types.insert("$_AND_");
|
||||
cell_types.insert("$_OR_");
|
||||
cell_types.insert("$_XOR_");
|
||||
cell_types.insert("$_MUX_");
|
||||
}
|
||||
|
||||
void setup_stdcells_mem()
|
||||
{
|
||||
cell_types.insert("$_DFF_N_");
|
||||
cell_types.insert("$_DFF_P_");
|
||||
cell_types.insert("$_DFF_NN0_");
|
||||
cell_types.insert("$_DFF_NN1_");
|
||||
cell_types.insert("$_DFF_NP0_");
|
||||
cell_types.insert("$_DFF_NP1_");
|
||||
cell_types.insert("$_DFF_PN0_");
|
||||
cell_types.insert("$_DFF_PN1_");
|
||||
cell_types.insert("$_DFF_PP0_");
|
||||
cell_types.insert("$_DFF_PP1_");
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
cell_types.clear();
|
||||
}
|
||||
|
||||
bool cell_known(std::string type)
|
||||
{
|
||||
return cell_types.count(type) > 0;
|
||||
}
|
||||
|
||||
bool cell_output(std::string type, std::string port)
|
||||
{
|
||||
if (!cell_known(type))
|
||||
return false;
|
||||
if (port == "\\Y" || port == "\\Q" || port == "\\RD_DATA")
|
||||
return true;
|
||||
if (type == "$memrd" && port == "\\DATA")
|
||||
return true;
|
||||
if (type == "$fsm" && port == "\\CTRL_OUT")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cell_input(std::string type, std::string port)
|
||||
{
|
||||
if (!cell_known(type))
|
||||
return false;
|
||||
return !cell_output(type, port);
|
||||
}
|
||||
|
||||
static RTLIL::Const eval(std::string type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
#define HANDLE_CELL_TYPE(_t) if (type == "$" #_t) return const_ ## _t(arg1, arg2, signed1, signed2, result_len);
|
||||
HANDLE_CELL_TYPE(not)
|
||||
HANDLE_CELL_TYPE(and)
|
||||
HANDLE_CELL_TYPE(or)
|
||||
HANDLE_CELL_TYPE(xor)
|
||||
HANDLE_CELL_TYPE(xnor)
|
||||
HANDLE_CELL_TYPE(reduce_and)
|
||||
HANDLE_CELL_TYPE(reduce_or)
|
||||
HANDLE_CELL_TYPE(reduce_xor)
|
||||
HANDLE_CELL_TYPE(reduce_xnor)
|
||||
HANDLE_CELL_TYPE(reduce_bool)
|
||||
HANDLE_CELL_TYPE(logic_not)
|
||||
HANDLE_CELL_TYPE(logic_and)
|
||||
HANDLE_CELL_TYPE(logic_or)
|
||||
HANDLE_CELL_TYPE(shl)
|
||||
HANDLE_CELL_TYPE(shr)
|
||||
HANDLE_CELL_TYPE(sshl)
|
||||
HANDLE_CELL_TYPE(sshr)
|
||||
HANDLE_CELL_TYPE(lt)
|
||||
HANDLE_CELL_TYPE(le)
|
||||
HANDLE_CELL_TYPE(eq)
|
||||
HANDLE_CELL_TYPE(ne)
|
||||
HANDLE_CELL_TYPE(ge)
|
||||
HANDLE_CELL_TYPE(gt)
|
||||
HANDLE_CELL_TYPE(add)
|
||||
HANDLE_CELL_TYPE(sub)
|
||||
HANDLE_CELL_TYPE(mul)
|
||||
HANDLE_CELL_TYPE(div)
|
||||
HANDLE_CELL_TYPE(mod)
|
||||
HANDLE_CELL_TYPE(pow)
|
||||
HANDLE_CELL_TYPE(pos)
|
||||
HANDLE_CELL_TYPE(neg)
|
||||
#undef HANDLE_CELL_TYPE
|
||||
|
||||
if (type == "$_INV_")
|
||||
return const_not(arg1, arg2, false, false, 1);
|
||||
if (type == "$_AND_")
|
||||
return const_and(arg1, arg2, false, false, 1);
|
||||
if (type == "$_OR_")
|
||||
return const_or(arg1, arg2, false, false, 1);
|
||||
if (type == "$_XOR_")
|
||||
return const_xor(arg1, arg2, false, false, 1);
|
||||
|
||||
assert(!"Called CellType.eval() with unsupported cell type!");
|
||||
abort();
|
||||
}
|
||||
|
||||
static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2)
|
||||
{
|
||||
bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool();
|
||||
bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool();
|
||||
int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1;
|
||||
return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len);
|
||||
}
|
||||
|
||||
static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &sel)
|
||||
{
|
||||
if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_") {
|
||||
RTLIL::Const ret = arg1;
|
||||
for (size_t i = 0; i < sel.bits.size(); i++)
|
||||
if (sel.bits[i] == RTLIL::State::S1) {
|
||||
std::vector<RTLIL::State> bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size());
|
||||
ret = RTLIL::Const(bits);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(sel.bits.size() == 0);
|
||||
bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool();
|
||||
bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool();
|
||||
int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1;
|
||||
return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONSTEVAL_H
|
||||
#define CONSTEVAL_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
|
||||
struct ConstEval
|
||||
{
|
||||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
SigMap values_map;
|
||||
SigPool stop_signals;
|
||||
SigSet<RTLIL::Cell*> sig2driver;
|
||||
std::set<RTLIL::Cell*> busy;
|
||||
std::vector<SigMap> stack;
|
||||
|
||||
ConstEval(RTLIL::Module *module) : module(module), assign_map(module)
|
||||
{
|
||||
CellTypes ct;
|
||||
ct.setup_internals();
|
||||
ct.setup_stdcells();
|
||||
|
||||
for (auto &it : module->cells) {
|
||||
if (!ct.cell_known(it.second->type))
|
||||
continue;
|
||||
for (auto &it2 : it.second->connections)
|
||||
if (ct.cell_output(it.second->type, it2.first))
|
||||
sig2driver.insert(assign_map(it2.second), it.second);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
values_map.clear();
|
||||
stop_signals.clear();
|
||||
}
|
||||
|
||||
void push()
|
||||
{
|
||||
stack.push_back(values_map);
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
values_map.swap(stack.back());
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
void set(RTLIL::SigSpec sig, RTLIL::Const value)
|
||||
{
|
||||
assign_map.apply(sig);
|
||||
#ifndef NDEBUG
|
||||
RTLIL::SigSpec current_val = values_map(sig);
|
||||
current_val.expand();
|
||||
for (size_t i = 0; i < current_val.chunks.size(); i++) {
|
||||
RTLIL::SigChunk &chunk = current_val.chunks[i];
|
||||
assert(chunk.wire != NULL || chunk.data.bits[0] == value.bits[i]);
|
||||
}
|
||||
#endif
|
||||
values_map.add(sig, RTLIL::SigSpec(value));
|
||||
}
|
||||
|
||||
void stop(RTLIL::SigSpec sig)
|
||||
{
|
||||
assign_map.apply(sig);
|
||||
stop_signals.add(sig);
|
||||
}
|
||||
|
||||
bool eval(RTLIL::Cell *cell, RTLIL::SigSpec &undef)
|
||||
{
|
||||
RTLIL::SigSpec sig_a, sig_b, sig_s, sig_y;
|
||||
bool ignore_sig_a = false, ignore_sig_b = false;
|
||||
int sig_b_shift = -1;
|
||||
|
||||
assert(cell->connections.count("\\Y") > 0);
|
||||
sig_y = values_map(assign_map(cell->connections["\\Y"]));
|
||||
if (sig_y.is_fully_const())
|
||||
return true;
|
||||
|
||||
if (cell->connections.count("\\S") > 0) {
|
||||
sig_s = cell->connections["\\S"];
|
||||
if (!eval(sig_s, undef, cell))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_") {
|
||||
bool found_collision = false;
|
||||
for (int i = 0; i < sig_s.width; i++)
|
||||
if (sig_s.extract(i, 1).as_bool()) {
|
||||
if (sig_b_shift >= 0)
|
||||
found_collision = true;
|
||||
sig_b_shift = i;
|
||||
ignore_sig_a = true;
|
||||
if (cell->type != "$safe_pmux")
|
||||
break;
|
||||
}
|
||||
if (found_collision) {
|
||||
sig_b_shift = -1;
|
||||
ignore_sig_a = false;
|
||||
}
|
||||
if (sig_b_shift < 0)
|
||||
ignore_sig_b = true;
|
||||
}
|
||||
|
||||
if (!ignore_sig_a && cell->connections.count("\\A") > 0) {
|
||||
sig_a = cell->connections["\\A"];
|
||||
if (!eval(sig_a, undef, cell))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ignore_sig_b && cell->connections.count("\\B") > 0) {
|
||||
sig_b = cell->connections["\\B"];
|
||||
if (sig_b_shift >= 0)
|
||||
sig_b = sig_b.extract(sig_y.width*sig_b_shift, sig_y.width);
|
||||
if (!eval(sig_b, undef, cell))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_")
|
||||
set(sig_y, sig_s.as_bool() ? sig_b.as_const() : sig_a.as_const());
|
||||
else
|
||||
set(sig_y, CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eval(RTLIL::SigSpec &sig, RTLIL::SigSpec &undef, RTLIL::Cell *busy_cell = NULL)
|
||||
{
|
||||
assign_map.apply(sig);
|
||||
values_map.apply(sig);
|
||||
|
||||
if (sig.is_fully_const())
|
||||
return true;
|
||||
|
||||
if (stop_signals.check_any(sig)) {
|
||||
undef = stop_signals.extract(sig);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (busy_cell) {
|
||||
if (busy.count(busy_cell) > 0) {
|
||||
undef = sig;
|
||||
return false;
|
||||
}
|
||||
busy.insert(busy_cell);
|
||||
}
|
||||
|
||||
std::set<RTLIL::Cell*> driver_cells;
|
||||
sig2driver.find(sig, driver_cells);
|
||||
for (auto cell : driver_cells) {
|
||||
if (!eval(cell, undef)) {
|
||||
if (busy_cell)
|
||||
busy.erase(busy_cell);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (busy_cell)
|
||||
busy.erase(busy_cell);
|
||||
|
||||
values_map.apply(sig);
|
||||
if (sig.is_fully_const())
|
||||
return true;
|
||||
|
||||
for (size_t i = 0; i < sig.chunks.size(); i++)
|
||||
if (sig.chunks[i].wire != NULL)
|
||||
undef.append(sig.chunks[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool eval(RTLIL::SigSpec &sig)
|
||||
{
|
||||
RTLIL::SigSpec undef;
|
||||
return eval(sig, undef);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command)
|
||||
{
|
||||
if (command == "auto") {
|
||||
if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
|
||||
command = "verilog";
|
||||
else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
|
||||
command = "ilang";
|
||||
else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys")
|
||||
command = "script";
|
||||
else if (filename == "-")
|
||||
command = "ilang";
|
||||
else
|
||||
log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str());
|
||||
}
|
||||
|
||||
if (command == "script") {
|
||||
log("\n-- Executing script file `%s' --\n", filename.c_str());
|
||||
FILE *f = fopen(filename.c_str(), "r");
|
||||
if (f == NULL)
|
||||
log_error("Can;t open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
|
||||
char buffer[4096];
|
||||
while (fgets(buffer, 4096, f) != NULL) {
|
||||
Pass::call(design, buffer);
|
||||
design->check();
|
||||
}
|
||||
fclose(f);
|
||||
if (backend_command != NULL && *backend_command == "auto")
|
||||
*backend_command = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (filename == "-") {
|
||||
log("\n-- Parsing stdin using frontend `%s' --\n", command.c_str());
|
||||
} else {
|
||||
log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str());
|
||||
}
|
||||
|
||||
Frontend::frontend_call(design, NULL, filename, command);
|
||||
design->check();
|
||||
}
|
||||
|
||||
static void run_pass(std::string command, RTLIL::Design *design)
|
||||
{
|
||||
log("\n-- Running pass `%s' --\n", command.c_str());
|
||||
|
||||
Pass::call(design, command);
|
||||
design->check();
|
||||
}
|
||||
|
||||
static void run_backend(std::string filename, std::string command, RTLIL::Design *design)
|
||||
{
|
||||
if (command == "auto") {
|
||||
if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
|
||||
command = "verilog";
|
||||
else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
|
||||
command = "ilang";
|
||||
else if (filename == "-")
|
||||
command = "ilang";
|
||||
else
|
||||
log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str());
|
||||
}
|
||||
|
||||
if (filename == "-") {
|
||||
log("\n-- Writing to stdout using backend `%s' --\n", command.c_str());
|
||||
} else {
|
||||
log("\n-- Writing to `%s' using backend `%s' --\n", filename.c_str(), command.c_str());
|
||||
}
|
||||
|
||||
Backend::backend_call(design, NULL, filename, command);
|
||||
design->check();
|
||||
}
|
||||
|
||||
static char *readline_cmd_generator(const char *text, int state)
|
||||
{
|
||||
static std::map<std::string, Pass*>::iterator it;
|
||||
static int len;
|
||||
|
||||
if (!state) {
|
||||
it = REGISTER_INTERN::pass_register.begin();
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
for (; it != REGISTER_INTERN::pass_register.end(); it++) {
|
||||
if (it->first.substr(0, len) == text)
|
||||
return strdup((it++)->first.c_str());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char **readline_completion(const char *text, int start, int)
|
||||
{
|
||||
if (start == 0)
|
||||
return rl_completion_matches(text, readline_cmd_generator);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *create_prompt(RTLIL::Design *design)
|
||||
{
|
||||
static char buffer[100];
|
||||
std::string str = "\nyosys";
|
||||
if (!design->selected_active_module.empty())
|
||||
str += stringf(" [%s]", design->selected_active_module.c_str());
|
||||
if (!design->selection_stack.back().full_selection) {
|
||||
if (design->selected_active_module.empty())
|
||||
str += "*";
|
||||
else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 ||
|
||||
design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0)
|
||||
str += "*";
|
||||
}
|
||||
snprintf(buffer, 100, "%s> ", str.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::string frontend_command = "auto";
|
||||
std::string backend_command = "auto";
|
||||
std::vector<std::string> passes_commands;
|
||||
std::string output_filename = "-";
|
||||
std::string scriptfile = "";
|
||||
bool got_output_filename = false;
|
||||
|
||||
RTLIL::Design *design = new RTLIL::Design;
|
||||
design->selection_stack.push_back(RTLIL::Selection());
|
||||
log_push();
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "f:b:o:p:l:qts:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'f':
|
||||
frontend_command = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
backend_command = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
passes_commands.push_back(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
output_filename = optarg;
|
||||
got_output_filename = true;
|
||||
break;
|
||||
case 'l':
|
||||
log_files.push_back(fopen(optarg, "wt"));
|
||||
if (log_files.back() == NULL) {
|
||||
fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
log_errfile = stderr;
|
||||
break;
|
||||
case 't':
|
||||
log_time = true;
|
||||
break;
|
||||
case 's':
|
||||
scriptfile = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s [-q] [-t] [-l logfile] [-o <outfile>] [-f <frontend>] [-s <scriptfile>] [-p <pass> [-p ..]] [-b <backend>] [<infile> [..]]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (log_errfile == NULL)
|
||||
log_files.push_back(stderr);
|
||||
|
||||
if (optind == argc && passes_commands.size() == 0 && scriptfile.empty())
|
||||
{
|
||||
log_cmd_error_throw = true;
|
||||
|
||||
rl_readline_name = "yosys";
|
||||
rl_attempted_completion_function = readline_completion;
|
||||
|
||||
char *command = NULL;
|
||||
while ((command = readline(create_prompt(design))) != NULL)
|
||||
{
|
||||
if (command[strspn(command, " \t\r\n")] == 0)
|
||||
continue;
|
||||
add_history(command);
|
||||
|
||||
try {
|
||||
assert(design->selection_stack.size() == 1);
|
||||
Pass::call(design, command);
|
||||
} catch (int) {
|
||||
while (design->selection_stack.size() > 1)
|
||||
design->selection_stack.pop_back();
|
||||
log_reset_stack();
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_output_filename)
|
||||
backend_command = "";
|
||||
log_cmd_error_throw = false;
|
||||
}
|
||||
|
||||
while (optind < argc)
|
||||
run_frontend(argv[optind++], frontend_command, design, output_filename == "-" ? &backend_command : NULL);
|
||||
|
||||
if (!scriptfile.empty())
|
||||
run_frontend(scriptfile, "script", design, output_filename == "-" ? &backend_command : NULL);
|
||||
|
||||
for (auto it = passes_commands.begin(); it != passes_commands.end(); it++)
|
||||
run_pass(*it, design);
|
||||
|
||||
if (!backend_command.empty())
|
||||
run_backend(output_filename, backend_command, design);
|
||||
|
||||
delete design;
|
||||
|
||||
log("\nREADY.\n");
|
||||
log_pop();
|
||||
|
||||
for (auto f : log_files)
|
||||
if (f != stderr)
|
||||
fclose(f);
|
||||
log_errfile = NULL;
|
||||
log_files.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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 "backends/ilang/ilang_backend.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
std::vector<FILE*> log_files;
|
||||
FILE *log_errfile = NULL;
|
||||
bool log_time = false;
|
||||
bool log_cmd_error_throw = false;
|
||||
|
||||
std::vector<int> header_count;
|
||||
std::list<std::string> string_buf;
|
||||
|
||||
static struct timeval initial_tv = { 0, 0 };
|
||||
static bool next_print_log = false;
|
||||
|
||||
std::string stringf(const char *fmt, ...)
|
||||
{
|
||||
std::string string;
|
||||
char *str = NULL;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&str, fmt, ap) < 0)
|
||||
str = NULL;
|
||||
va_end(ap);
|
||||
|
||||
if (str != NULL) {
|
||||
string = str;
|
||||
free(str);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void logv(const char *format, va_list ap)
|
||||
{
|
||||
if (log_time) {
|
||||
while (format[0] == '\n' && format[1] != 0) {
|
||||
format++;
|
||||
log("\n");
|
||||
}
|
||||
if (next_print_log || initial_tv.tv_sec == 0) {
|
||||
next_print_log = false;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
if (initial_tv.tv_sec == 0)
|
||||
initial_tv = tv;
|
||||
if (tv.tv_usec < initial_tv.tv_usec) {
|
||||
tv.tv_sec--;
|
||||
tv.tv_usec += 1000000;
|
||||
}
|
||||
tv.tv_sec -= initial_tv.tv_sec;
|
||||
tv.tv_usec -= initial_tv.tv_usec;
|
||||
log("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec));
|
||||
}
|
||||
if (format[0] && format[strlen(format)-1] == '\n')
|
||||
next_print_log = true;
|
||||
}
|
||||
|
||||
for (auto f : log_files) {
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
vfprintf(f, format, aq);
|
||||
va_end(aq);
|
||||
}
|
||||
}
|
||||
|
||||
void logv_header(const char *format, va_list ap)
|
||||
{
|
||||
log("\n");
|
||||
if (header_count.size() > 0)
|
||||
header_count.back()++;
|
||||
for (int c : header_count)
|
||||
log("%d.", c);
|
||||
log(" ");
|
||||
logv(format, ap);
|
||||
log_flush();
|
||||
}
|
||||
|
||||
void logv_error(const char *format, va_list ap)
|
||||
{
|
||||
log("ERROR: ");
|
||||
logv(format, ap);
|
||||
if (log_errfile != NULL) {
|
||||
fprintf(log_errfile, "ERROR: ");
|
||||
vfprintf(log_errfile, format, ap);
|
||||
}
|
||||
log_flush();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void log(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
logv(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void log_header(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
logv_header(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void log_error(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
logv_error(format, ap);
|
||||
}
|
||||
|
||||
void log_cmd_error(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
if (log_cmd_error_throw) {
|
||||
log("ERROR: ");
|
||||
logv(format, ap);
|
||||
log_flush();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
logv_error(format, ap);
|
||||
}
|
||||
|
||||
void log_push()
|
||||
{
|
||||
header_count.push_back(0);
|
||||
}
|
||||
|
||||
void log_pop()
|
||||
{
|
||||
header_count.pop_back();
|
||||
string_buf.clear();
|
||||
log_flush();
|
||||
}
|
||||
|
||||
void log_reset_stack()
|
||||
{
|
||||
while (header_count.size() > 1)
|
||||
header_count.pop_back();
|
||||
string_buf.clear();
|
||||
log_flush();
|
||||
}
|
||||
|
||||
void log_flush()
|
||||
{
|
||||
for (auto f : log_files)
|
||||
fflush(f);
|
||||
}
|
||||
|
||||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
||||
{
|
||||
char *ptr;
|
||||
size_t size;
|
||||
|
||||
FILE *f = open_memstream(&ptr, &size);
|
||||
ILANG_BACKEND::dump_sigspec(f, sig, autoint);
|
||||
fputc(0, f);
|
||||
fclose(f);
|
||||
|
||||
string_buf.push_back(ptr);
|
||||
free(ptr);
|
||||
|
||||
return string_buf.back().c_str();
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
extern std::vector<FILE*> log_files;
|
||||
extern FILE *log_errfile;
|
||||
extern bool log_time;
|
||||
extern bool log_cmd_error_throw;
|
||||
|
||||
std::string stringf(const char *fmt, ...);
|
||||
|
||||
void logv(const char *format, va_list ap);
|
||||
void logv_header(const char *format, va_list ap);
|
||||
void logv_error(const char *format, va_list ap) __attribute__ ((noreturn));
|
||||
|
||||
void log(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
void log_header(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
void log_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __attribute__ ((noreturn));
|
||||
void log_cmd_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __attribute__ ((noreturn));
|
||||
|
||||
void log_push();
|
||||
void log_pop();
|
||||
|
||||
void log_reset_stack();
|
||||
void log_flush();
|
||||
|
||||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* 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 "register.h"
|
||||
#include "log.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace REGISTER_INTERN;
|
||||
|
||||
namespace REGISTER_INTERN {
|
||||
std::map<std::string, Frontend*> frontend_register;
|
||||
std::map<std::string, Pass*> pass_register;
|
||||
std::map<std::string, Backend*> backend_register;
|
||||
}
|
||||
|
||||
std::vector<std::string> Frontend::next_args;
|
||||
|
||||
Pass::Pass(std::string name) : pass_name(name)
|
||||
{
|
||||
assert(pass_register.count(name) == 0);
|
||||
pass_register[name] = this;
|
||||
}
|
||||
|
||||
Pass::~Pass()
|
||||
{
|
||||
pass_register.erase(pass_name);
|
||||
}
|
||||
|
||||
void Pass::help()
|
||||
{
|
||||
log("No help message for this command.\n");
|
||||
}
|
||||
|
||||
void Pass::cmd_log_args(const std::vector<std::string> &args)
|
||||
{
|
||||
if (args.size() <= 1)
|
||||
return;
|
||||
log("Full command line:");
|
||||
for (size_t i = 0; i < args.size(); i++)
|
||||
log(" %s", args[i].c_str());
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
|
||||
{
|
||||
std::string command_text;
|
||||
int error_pos = 0;
|
||||
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (i < argidx)
|
||||
error_pos += args[i].size() + 1;
|
||||
command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
|
||||
}
|
||||
|
||||
log("\nSyntax error in command `%s':\n", command_text.c_str());
|
||||
help();
|
||||
|
||||
log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
|
||||
msg.c_str(), command_text.c_str(), error_pos, "");
|
||||
}
|
||||
|
||||
void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *)
|
||||
{
|
||||
for (; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
|
||||
if (arg.substr(0, 1) == "-")
|
||||
cmd_error(args, argidx, "Unkown option or option in arguments.");
|
||||
cmd_error(args, argidx, "Extra argument.");
|
||||
}
|
||||
cmd_log_args(args);
|
||||
}
|
||||
|
||||
void Pass::call(RTLIL::Design *design, std::string command)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
char *s = strdup(command.c_str());
|
||||
for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
|
||||
args.push_back(p);
|
||||
free(s);
|
||||
call(design, args);
|
||||
}
|
||||
|
||||
void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
|
||||
{
|
||||
if (args.size() == 0 || args[0][0] == '#')
|
||||
return;
|
||||
if (pass_register.count(args[0]) == 0)
|
||||
log_cmd_error("No such command: %s\n", args[0].c_str());
|
||||
|
||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||
pass_register[args[0]]->execute(args, design);
|
||||
while (design->selection_stack.size() > orig_sel_stack_pos)
|
||||
design->selection_stack.pop_back();
|
||||
}
|
||||
|
||||
Frontend::Frontend(std::string name) : Pass("read_"+name), frontend_name(name)
|
||||
{
|
||||
assert(frontend_register.count(name) == 0);
|
||||
frontend_register[name] = this;
|
||||
}
|
||||
|
||||
Frontend::~Frontend()
|
||||
{
|
||||
frontend_register.erase(frontend_name);
|
||||
}
|
||||
|
||||
void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
assert(next_args.empty());
|
||||
do {
|
||||
FILE *f = NULL;
|
||||
next_args.clear();
|
||||
execute(f, std::string(), args, design);
|
||||
args = next_args;
|
||||
fclose(f);
|
||||
} while (!args.empty());
|
||||
}
|
||||
|
||||
void Frontend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
||||
{
|
||||
bool called_with_fp = f != NULL;
|
||||
|
||||
next_args.clear();
|
||||
for (; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
|
||||
if (arg.substr(0, 1) == "-")
|
||||
cmd_error(args, argidx, "Unkown option or option in arguments.");
|
||||
if (f != NULL)
|
||||
cmd_error(args, argidx, "Extra filename argument in direct file mode.");
|
||||
|
||||
filename = arg;
|
||||
f = fopen(filename.c_str(), "r");
|
||||
if (f == NULL)
|
||||
log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
|
||||
|
||||
if (argidx+1 < args.size()) {
|
||||
next_args.insert(next_args.begin(), args.begin(), args.begin()+argidx);
|
||||
next_args.insert(next_args.begin()+argidx, args.begin()+argidx+1, args.end());
|
||||
args.erase(args.begin()+argidx+1, args.end());
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (f == NULL)
|
||||
cmd_error(args, argidx, "No filename given.");
|
||||
|
||||
if (called_with_fp)
|
||||
args.push_back(filename);
|
||||
args[0] = pass_name;
|
||||
cmd_log_args(args);
|
||||
}
|
||||
|
||||
void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
char *s = strdup(command.c_str());
|
||||
for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
|
||||
args.push_back(p);
|
||||
free(s);
|
||||
frontend_call(design, f, filename, args);
|
||||
}
|
||||
|
||||
void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
|
||||
{
|
||||
if (args.size() == 0)
|
||||
return;
|
||||
if (frontend_register.count(args[0]) == 0)
|
||||
log_cmd_error("No such frontend: %s\n", args[0].c_str());
|
||||
|
||||
if (f != NULL) {
|
||||
frontend_register[args[0]]->execute(f, filename, args, design);
|
||||
} else if (filename == "-") {
|
||||
frontend_register[args[0]]->execute(stdin, "<stdin>", args, design);
|
||||
} else {
|
||||
if (!filename.empty())
|
||||
args.push_back(filename);
|
||||
frontend_register[args[0]]->execute(args, design);
|
||||
}
|
||||
}
|
||||
|
||||
Backend::Backend(std::string name) : Pass("write_"+name), backend_name(name)
|
||||
{
|
||||
assert(backend_register.count(name) == 0);
|
||||
backend_register[name] = this;
|
||||
}
|
||||
|
||||
Backend::~Backend()
|
||||
{
|
||||
backend_register.erase(backend_name);
|
||||
}
|
||||
|
||||
void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
execute(f, std::string(), args, design);
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
||||
{
|
||||
bool called_with_fp = f != NULL;
|
||||
|
||||
for (; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
|
||||
if (arg.substr(0, 1) == "-" && arg != "-")
|
||||
cmd_error(args, argidx, "Unkown option or option in arguments.");
|
||||
if (f != NULL)
|
||||
cmd_error(args, argidx, "Extra filename argument in direct file mode.");
|
||||
|
||||
if (arg == "-") {
|
||||
filename = "<stdout>";
|
||||
f = stdout;
|
||||
continue;
|
||||
}
|
||||
|
||||
filename = arg;
|
||||
f = fopen(filename.c_str(), "w");
|
||||
if (f == NULL)
|
||||
log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
if (called_with_fp)
|
||||
args.push_back(filename);
|
||||
args[0] = pass_name;
|
||||
cmd_log_args(args);
|
||||
|
||||
if (f == NULL) {
|
||||
filename = "<stdout>";
|
||||
f = stdout;
|
||||
}
|
||||
}
|
||||
|
||||
void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
char *s = strdup(command.c_str());
|
||||
for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
|
||||
args.push_back(p);
|
||||
free(s);
|
||||
backend_call(design, f, filename, args);
|
||||
}
|
||||
|
||||
void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
|
||||
{
|
||||
if (args.size() == 0)
|
||||
return;
|
||||
if (backend_register.count(args[0]) == 0)
|
||||
log_cmd_error("No such backend: %s\n", args[0].c_str());
|
||||
|
||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||
|
||||
if (f != NULL) {
|
||||
backend_register[args[0]]->execute(f, filename, args, design);
|
||||
} else if (filename == "-") {
|
||||
backend_register[args[0]]->execute(stdout, "<stdout>", args, design);
|
||||
} else {
|
||||
if (!filename.empty())
|
||||
args.push_back(filename);
|
||||
backend_register[args[0]]->execute(args, design);
|
||||
}
|
||||
|
||||
while (design->selection_stack.size() > orig_sel_stack_pos)
|
||||
design->selection_stack.pop_back();
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 REGISTER_H
|
||||
#define REGISTER_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
struct Pass
|
||||
{
|
||||
std::string pass_name;
|
||||
Pass(std::string name);
|
||||
virtual ~Pass();
|
||||
virtual void help();
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
void cmd_log_args(const std::vector<std::string> &args);
|
||||
void cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg);
|
||||
void extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design);
|
||||
|
||||
static void call(RTLIL::Design *design, std::string command);
|
||||
static void call(RTLIL::Design *design, std::vector<std::string> args);
|
||||
};
|
||||
|
||||
struct Frontend : Pass
|
||||
{
|
||||
std::string frontend_name;
|
||||
Frontend(std::string name);
|
||||
virtual ~Frontend();
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design);
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
static std::vector<std::string> next_args;
|
||||
void extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
||||
|
||||
static void frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command);
|
||||
static void frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args);
|
||||
};
|
||||
|
||||
struct Backend : Pass
|
||||
{
|
||||
std::string backend_name;
|
||||
Backend(std::string name);
|
||||
virtual ~Backend();
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design);
|
||||
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
void extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
||||
|
||||
static void backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command);
|
||||
static void backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args);
|
||||
};
|
||||
|
||||
namespace REGISTER_INTERN {
|
||||
extern std::map<std::string, Pass*> pass_register;
|
||||
extern std::map<std::string, Frontend*> frontend_register;
|
||||
extern std::map<std::string, Backend*> backend_register;
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* 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 RTLIL_H
|
||||
#define RTLIL_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
std::string stringf(const char *fmt, ...);
|
||||
|
||||
namespace RTLIL
|
||||
{
|
||||
enum State {
|
||||
S0 = 0,
|
||||
S1 = 1,
|
||||
Sx = 2, // undefined value or conflict
|
||||
Sz = 3, // high-impedance / not-connected
|
||||
Sa = 4, // don't care (used only in cases)
|
||||
Sm = 5 // marker (used internally by some passes)
|
||||
};
|
||||
enum SyncType {
|
||||
ST0 = 0, // level sensitive: 0
|
||||
ST1 = 1, // level sensitive: 1
|
||||
STp = 2, // edge sensitive: posedge
|
||||
STn = 3, // edge sensitive: negedge
|
||||
STe = 4, // edge sensitive: both edges
|
||||
STa = 5 // always active
|
||||
};
|
||||
|
||||
extern int autoidx;
|
||||
|
||||
struct Const;
|
||||
struct Selection;
|
||||
struct Design;
|
||||
struct Module;
|
||||
struct Wire;
|
||||
struct Memory;
|
||||
struct Cell;
|
||||
struct SigChunk;
|
||||
struct SigSpec;
|
||||
struct CaseRule;
|
||||
struct SwitchRule;
|
||||
struct SyncRule;
|
||||
struct Process;
|
||||
|
||||
typedef std::pair<SigSpec, SigSpec> SigSig;
|
||||
|
||||
#ifdef NDEBUG
|
||||
typedef std::string IdString;
|
||||
#else
|
||||
struct IdString : public std::string {
|
||||
IdString() { }
|
||||
IdString(std::string str) : std::string(str) {
|
||||
check();
|
||||
}
|
||||
IdString(const char *s) : std::string(s) {
|
||||
check();
|
||||
}
|
||||
IdString &operator=(const std::string &str) {
|
||||
std::string::operator=(str);
|
||||
check();
|
||||
return *this;
|
||||
}
|
||||
IdString &operator=(const char *s) {
|
||||
std::string::operator=(s);
|
||||
check();
|
||||
return *this;
|
||||
}
|
||||
bool operator<(const IdString &rhs) {
|
||||
check(), rhs.check();
|
||||
return std::string(*this) < std::string(rhs);
|
||||
}
|
||||
void check() const {
|
||||
assert(empty() || (size() >= 2 && (at(0) == '$' || at(0) == '\\')));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static IdString escape_id(std::string str) __attribute__((unused));
|
||||
static IdString escape_id(std::string str) {
|
||||
if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
|
||||
return "\\" + str;
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::string unescape_id(std::string str) __attribute__((unused));
|
||||
static std::string unescape_id(std::string str) {
|
||||
if (str.size() > 0 && str[0] == '\\')
|
||||
return str.substr(1);
|
||||
return str;
|
||||
}
|
||||
|
||||
static IdString new_id(std::string file, int line, std::string func) __attribute__((unused));
|
||||
static IdString new_id(std::string file, int line, std::string func) {
|
||||
std::string str = "$auto$";
|
||||
size_t pos = file.find_last_of('/');
|
||||
str += pos != std::string::npos ? file.substr(pos+1) : file;
|
||||
str += stringf(":%d:%s$%d", line, func.c_str(), autoidx++);
|
||||
return str;
|
||||
}
|
||||
|
||||
#define NEW_ID \
|
||||
RTLIL::new_id(__FILE__, __LINE__, __FUNCTION__)
|
||||
|
||||
// see calc.cc for the implementation of this functions
|
||||
RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_reduce_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_reduce_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_reduce_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_reduce_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_reduce_bool (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_logic_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_logic_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_logic_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_shl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_shr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_sshl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_sshr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_lt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_le (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_eq (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_ne (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_ge (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_gt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_add (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_sub (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_mul (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_div (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_mod (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_pow (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
|
||||
RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||
};
|
||||
|
||||
struct RTLIL::Const {
|
||||
std::string str;
|
||||
std::vector<RTLIL::State> bits;
|
||||
Const(std::string str = std::string());
|
||||
Const(int val, int width = 32);
|
||||
Const(RTLIL::State bit, int width = 1);
|
||||
Const(std::vector<RTLIL::State> bits) : bits(bits) { };
|
||||
bool operator <(const RTLIL::Const &other) const;
|
||||
bool operator ==(const RTLIL::Const &other) const;
|
||||
bool operator !=(const RTLIL::Const &other) const;
|
||||
bool as_bool() const;
|
||||
int as_int() const;
|
||||
std::string as_string() const;
|
||||
};
|
||||
|
||||
struct RTLIL::Selection {
|
||||
bool full_selection;
|
||||
std::set<RTLIL::IdString> selected_modules;
|
||||
std::map<RTLIL::IdString, std::set<RTLIL::IdString>> selected_members;
|
||||
Selection(bool full = true) : full_selection(full) { }
|
||||
bool selected_module(RTLIL::IdString mod_name);
|
||||
bool selected_whole_module(RTLIL::IdString mod_name);
|
||||
bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name);
|
||||
void optimize(RTLIL::Design *design);
|
||||
};
|
||||
|
||||
struct RTLIL::Design {
|
||||
std::map<RTLIL::IdString, RTLIL::Module*> modules;
|
||||
std::vector<RTLIL::Selection> selection_stack;
|
||||
std::map<RTLIL::IdString, RTLIL::Selection> selection_vars;
|
||||
std::string selected_active_module;
|
||||
~Design();
|
||||
void check();
|
||||
void optimize();
|
||||
bool selected_module(RTLIL::IdString mod_name);
|
||||
bool selected_whole_module(RTLIL::IdString mod_name);
|
||||
bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name);
|
||||
template<typename T1> bool selected(T1 *module) {
|
||||
return selected_module(module->name);
|
||||
}
|
||||
template<typename T1, typename T2> bool selected(T1 *module, T2 *member) {
|
||||
return selected_member(module->name, member->name);
|
||||
}
|
||||
};
|
||||
|
||||
struct RTLIL::Module {
|
||||
RTLIL::IdString name;
|
||||
std::map<RTLIL::IdString, RTLIL::Wire*> wires;
|
||||
std::map<RTLIL::IdString, RTLIL::Memory*> memories;
|
||||
std::map<RTLIL::IdString, RTLIL::Cell*> cells;
|
||||
std::map<RTLIL::IdString, RTLIL::Process*> processes;
|
||||
std::vector<RTLIL::SigSig> connections;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
virtual ~Module();
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters);
|
||||
virtual void update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes);
|
||||
virtual size_t count_id(RTLIL::IdString id);
|
||||
virtual void check();
|
||||
virtual void optimize();
|
||||
void add(RTLIL::Wire *wire);
|
||||
void add(RTLIL::Cell *cell);
|
||||
};
|
||||
|
||||
struct RTLIL::Wire {
|
||||
RTLIL::IdString name;
|
||||
int width, start_offset, port_id;
|
||||
bool port_input, port_output, auto_width;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
Wire();
|
||||
};
|
||||
|
||||
struct RTLIL::Memory {
|
||||
RTLIL::IdString name;
|
||||
int width, start_offset, size;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
Memory();
|
||||
};
|
||||
|
||||
struct RTLIL::Cell {
|
||||
RTLIL::IdString name;
|
||||
RTLIL::IdString type;
|
||||
std::map<RTLIL::IdString, RTLIL::SigSpec> connections;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> parameters;
|
||||
void optimize();
|
||||
};
|
||||
|
||||
struct RTLIL::SigChunk {
|
||||
RTLIL::Wire *wire;
|
||||
RTLIL::Const data; // only used if wire == NULL, LSB at index 0
|
||||
int width, offset;
|
||||
SigChunk();
|
||||
SigChunk(const RTLIL::Const &data);
|
||||
SigChunk(RTLIL::Wire *wire, int width, int offset);
|
||||
SigChunk(const std::string &str);
|
||||
SigChunk(int val, int width = 32);
|
||||
SigChunk(RTLIL::State bit, int width = 1);
|
||||
RTLIL::SigChunk extract(int offset, int length) const;
|
||||
bool operator <(const RTLIL::SigChunk &other) const;
|
||||
bool operator ==(const RTLIL::SigChunk &other) const;
|
||||
bool operator !=(const RTLIL::SigChunk &other) const;
|
||||
};
|
||||
|
||||
struct RTLIL::SigSpec {
|
||||
std::vector<RTLIL::SigChunk> chunks; // LSB at index 0
|
||||
int width;
|
||||
SigSpec();
|
||||
SigSpec(const RTLIL::Const &data);
|
||||
SigSpec(const RTLIL::SigChunk &chunk);
|
||||
SigSpec(RTLIL::Wire *wire, int width = -1, int offset = 0);
|
||||
SigSpec(const std::string &str);
|
||||
SigSpec(int val, int width = 32);
|
||||
SigSpec(RTLIL::State bit, int width = 1);
|
||||
void expand();
|
||||
void optimize();
|
||||
void sort_and_unify();
|
||||
void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with);
|
||||
void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const;
|
||||
void remove(const RTLIL::SigSpec &pattern);
|
||||
void remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const;
|
||||
void remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other);
|
||||
RTLIL::SigSpec extract(RTLIL::SigSpec pattern, RTLIL::SigSpec *other = NULL) const;
|
||||
void replace(int offset, const RTLIL::SigSpec &with);
|
||||
void remove_const();
|
||||
void remove(int offset, int length);
|
||||
RTLIL::SigSpec extract(int offset, int length) const;
|
||||
void append(const RTLIL::SigSpec &signal);
|
||||
bool combine(RTLIL::SigSpec signal, RTLIL::State freeState = RTLIL::State::Sz, bool override = false);
|
||||
void extend(int width, bool is_signed = false);
|
||||
void check() const;
|
||||
bool operator <(const RTLIL::SigSpec &other) const;
|
||||
bool operator ==(const RTLIL::SigSpec &other) const;
|
||||
bool operator !=(const RTLIL::SigSpec &other) const;
|
||||
bool is_fully_const() const;
|
||||
bool is_fully_def() const;
|
||||
bool is_fully_undef() const;
|
||||
bool has_marked_bits() const;
|
||||
bool as_bool() const;
|
||||
int as_int() const;
|
||||
std::string as_string() const;
|
||||
RTLIL::Const as_const() const;
|
||||
bool match(std::string pattern) const;
|
||||
};
|
||||
|
||||
struct RTLIL::CaseRule {
|
||||
std::vector<RTLIL::SigSpec> compare;
|
||||
std::vector<RTLIL::SigSig> actions;
|
||||
std::vector<RTLIL::SwitchRule*> switches;
|
||||
~CaseRule();
|
||||
void optimize();
|
||||
};
|
||||
|
||||
struct RTLIL::SwitchRule {
|
||||
RTLIL::SigSpec signal;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
std::vector<RTLIL::CaseRule*> cases;
|
||||
~SwitchRule();
|
||||
void optimize();
|
||||
};
|
||||
|
||||
struct RTLIL::SyncRule {
|
||||
RTLIL::SyncType type;
|
||||
RTLIL::SigSpec signal;
|
||||
std::vector<RTLIL::SigSig> actions;
|
||||
void optimize();
|
||||
};
|
||||
|
||||
struct RTLIL::Process {
|
||||
RTLIL::IdString name;
|
||||
std::map<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
RTLIL::CaseRule root_case;
|
||||
std::vector<RTLIL::SyncRule*> syncs;
|
||||
~Process();
|
||||
void optimize();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,476 @@
|
|||
/*
|
||||
* 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 <string.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
static std::vector<RTLIL::Selection> work_stack;
|
||||
|
||||
static bool match_ids(RTLIL::IdString id, std::string pattern)
|
||||
{
|
||||
if (!fnmatch(pattern.c_str(), id.c_str(), FNM_NOESCAPE))
|
||||
return true;
|
||||
if (id.size() > 0 && id[0] == '\\' && !fnmatch(pattern.c_str(), id.substr(1).c_str(), FNM_NOESCAPE))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs)
|
||||
{
|
||||
if (lhs.full_selection) {
|
||||
lhs.full_selection = false;
|
||||
lhs.selected_modules.clear();
|
||||
lhs.selected_members.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lhs.selected_modules.size() == 0 && lhs.selected_members.size() == 0) {
|
||||
lhs.full_selection = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RTLIL::Selection new_sel(false);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
if (lhs.selected_whole_module(mod_it.first))
|
||||
continue;
|
||||
if (!lhs.selected_module(mod_it.first)) {
|
||||
new_sel.selected_modules.insert(mod_it.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Module *mod = mod_it.second;
|
||||
for (auto &it : mod->wires)
|
||||
if (!lhs.selected_member(mod_it.first, it.first))
|
||||
new_sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->memories)
|
||||
if (!lhs.selected_member(mod_it.first, it.first))
|
||||
new_sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->cells)
|
||||
if (!lhs.selected_member(mod_it.first, it.first))
|
||||
new_sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->processes)
|
||||
if (!lhs.selected_member(mod_it.first, it.first))
|
||||
new_sel.selected_members[mod->name].insert(it.first);
|
||||
}
|
||||
|
||||
lhs.selected_modules.swap(new_sel.selected_modules);
|
||||
lhs.selected_members.swap(new_sel.selected_members);
|
||||
}
|
||||
|
||||
static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
|
||||
{
|
||||
if (rhs.full_selection) {
|
||||
lhs.full_selection = true;
|
||||
lhs.selected_modules.clear();
|
||||
lhs.selected_members.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lhs.full_selection)
|
||||
return;
|
||||
|
||||
for (auto &it : rhs.selected_members)
|
||||
for (auto &it2 : it.second)
|
||||
lhs.selected_members[it.first].insert(it2);
|
||||
|
||||
for (auto &it : rhs.selected_modules) {
|
||||
lhs.selected_modules.insert(it);
|
||||
lhs.selected_members.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
|
||||
{
|
||||
if (rhs.full_selection) {
|
||||
lhs.full_selection = false;
|
||||
lhs.selected_modules.clear();
|
||||
lhs.selected_members.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lhs.full_selection) {
|
||||
if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0)
|
||||
return;
|
||||
lhs.full_selection = false;
|
||||
for (auto &it : design->modules)
|
||||
lhs.selected_modules.insert(it.first);
|
||||
}
|
||||
|
||||
for (auto &it : rhs.selected_modules) {
|
||||
lhs.selected_modules.erase(it);
|
||||
lhs.selected_members.erase(it);
|
||||
}
|
||||
|
||||
for (auto &it : rhs.selected_members)
|
||||
{
|
||||
if (design->modules.count(it.first) == 0)
|
||||
continue;
|
||||
|
||||
RTLIL::Module *mod = design->modules[it.first];
|
||||
|
||||
if (lhs.selected_modules.count(mod->name) > 0)
|
||||
{
|
||||
for (auto &it : mod->wires)
|
||||
lhs.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->memories)
|
||||
lhs.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->cells)
|
||||
lhs.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->processes)
|
||||
lhs.selected_members[mod->name].insert(it.first);
|
||||
lhs.selected_modules.erase(mod->name);
|
||||
}
|
||||
|
||||
if (lhs.selected_members.count(mod->name) == 0)
|
||||
continue;
|
||||
|
||||
for (auto &it2 : it.second)
|
||||
lhs.selected_members[mod->name].erase(it2);
|
||||
}
|
||||
}
|
||||
|
||||
static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
|
||||
{
|
||||
if (rhs.full_selection)
|
||||
return;
|
||||
|
||||
if (lhs.full_selection) {
|
||||
lhs.full_selection = false;
|
||||
for (auto &it : design->modules)
|
||||
lhs.selected_modules.insert(it.first);
|
||||
}
|
||||
|
||||
std::vector<RTLIL::IdString> del_list;
|
||||
|
||||
for (auto &it : lhs.selected_modules)
|
||||
if (rhs.selected_modules.count(it) == 0) {
|
||||
if (rhs.selected_members.count(it) > 0)
|
||||
for (auto &it2 : rhs.selected_members.at(it))
|
||||
lhs.selected_members[it].insert(it2);
|
||||
del_list.push_back(it);
|
||||
}
|
||||
for (auto &it : del_list)
|
||||
lhs.selected_modules.erase(it);
|
||||
|
||||
del_list.clear();
|
||||
for (auto &it : lhs.selected_members) {
|
||||
if (rhs.selected_modules.count(it.first) > 0)
|
||||
continue;
|
||||
if (rhs.selected_members.count(it.first) == 0) {
|
||||
del_list.push_back(it.first);
|
||||
continue;
|
||||
}
|
||||
std::vector<RTLIL::IdString> del_list2;
|
||||
for (auto &it2 : it.second)
|
||||
if (rhs.selected_members.at(it.first).count(it2) == 0)
|
||||
del_list2.push_back(it2);
|
||||
for (auto &it2 : del_list2)
|
||||
it.second.erase(it2);
|
||||
if (it.second.size() == 0)
|
||||
del_list.push_back(it.first);
|
||||
}
|
||||
for (auto &it : del_list)
|
||||
lhs.selected_members.erase(it);
|
||||
}
|
||||
|
||||
static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &sel)
|
||||
{
|
||||
if (design->selected_active_module.empty())
|
||||
return;
|
||||
|
||||
if (sel.full_selection) {
|
||||
sel.full_selection = false;
|
||||
sel.selected_modules.clear();
|
||||
sel.selected_members.clear();
|
||||
sel.selected_modules.insert(design->selected_active_module);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> del_list;
|
||||
for (auto mod_name : sel.selected_modules)
|
||||
if (mod_name != design->selected_active_module)
|
||||
del_list.push_back(mod_name);
|
||||
for (auto &it : sel.selected_members)
|
||||
if (it.first != design->selected_active_module)
|
||||
del_list.push_back(it.first);
|
||||
for (auto mod_name : del_list) {
|
||||
sel.selected_modules.erase(mod_name);
|
||||
sel.selected_members.erase(mod_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void select_stmt(RTLIL::Design *design, std::string arg)
|
||||
{
|
||||
std::string arg_mod, arg_memb;
|
||||
|
||||
if (arg.size() == 0)
|
||||
return;
|
||||
|
||||
if (arg[0] == '#') {
|
||||
if (arg == "#") {
|
||||
if (design->selection_stack.size() > 0)
|
||||
work_stack.push_back(design->selection_stack.back());
|
||||
} else
|
||||
if (arg == "#n") {
|
||||
if (work_stack.size() < 1)
|
||||
log_cmd_error("Must have at least one element on stack for operator #n.\n");
|
||||
select_op_neg(design, work_stack[work_stack.size()-1]);
|
||||
} else
|
||||
if (arg == "#u") {
|
||||
if (work_stack.size() < 2)
|
||||
log_cmd_error("Must have at least two elements on stack for operator #u.\n");
|
||||
select_op_union(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
|
||||
work_stack.pop_back();
|
||||
} else
|
||||
if (arg == "#d") {
|
||||
if (work_stack.size() < 2)
|
||||
log_cmd_error("Must have at least two elements on stack for operator #d.\n");
|
||||
select_op_diff(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
|
||||
work_stack.pop_back();
|
||||
} else
|
||||
if (arg == "#i") {
|
||||
if (work_stack.size() < 2)
|
||||
log_cmd_error("Must have at least two elements on stack for operator #i.\n");
|
||||
select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
|
||||
work_stack.pop_back();
|
||||
} else
|
||||
log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str());
|
||||
select_filter_active_mod(design, work_stack.back());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!design->selected_active_module.empty()) {
|
||||
arg_mod = design->selected_active_module;
|
||||
arg_memb = arg;
|
||||
} else {
|
||||
size_t pos = arg.find('/');
|
||||
if (pos == std::string::npos) {
|
||||
arg_mod = arg;
|
||||
} else {
|
||||
arg_mod = arg.substr(0, pos);
|
||||
arg_memb = arg.substr(pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
work_stack.push_back(RTLIL::Selection());
|
||||
RTLIL::Selection &sel = work_stack.back();
|
||||
|
||||
if (arg == "*" && arg_mod == "*") {
|
||||
select_filter_active_mod(design, work_stack.back());
|
||||
return;
|
||||
}
|
||||
|
||||
sel.full_selection = false;
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
if (!match_ids(mod_it.first, arg_mod))
|
||||
continue;
|
||||
|
||||
if (arg_memb == "") {
|
||||
sel.selected_modules.insert(mod_it.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Module *mod = mod_it.second;
|
||||
if (arg_memb.substr(0, 2) == "w:") {
|
||||
for (auto &it : mod->wires)
|
||||
if (match_ids(it.first, arg_memb.substr(2)))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
} else
|
||||
if (arg_memb.substr(0, 2) == "m:") {
|
||||
for (auto &it : mod->memories)
|
||||
if (match_ids(it.first, arg_memb.substr(2)))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
} else
|
||||
if (arg_memb.substr(0, 2) == "c:") {
|
||||
for (auto &it : mod->cells)
|
||||
if (match_ids(it.first, arg_memb.substr(2)))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
} else
|
||||
if (arg_memb.substr(0, 2) == "t:") {
|
||||
for (auto &it : mod->cells)
|
||||
if (match_ids(it.second->type, arg_memb.substr(2)))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
} else
|
||||
if (arg_memb.substr(0, 2) == "p:") {
|
||||
for (auto &it : mod->processes)
|
||||
if (match_ids(it.first, arg_memb.substr(2)))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
} else {
|
||||
if (arg_memb.substr(0, 2) == "n:")
|
||||
arg_memb = arg_memb.substr(2);
|
||||
for (auto &it : mod->wires)
|
||||
if (match_ids(it.first, arg_memb))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->memories)
|
||||
if (match_ids(it.first, arg_memb))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->cells)
|
||||
if (match_ids(it.first, arg_memb))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
for (auto &it : mod->processes)
|
||||
if (match_ids(it.first, arg_memb))
|
||||
sel.selected_members[mod->name].insert(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
select_filter_active_mod(design, work_stack.back());
|
||||
}
|
||||
|
||||
struct SelectPass : public Pass {
|
||||
SelectPass() : Pass("select") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool add_mode = false;
|
||||
bool del_mode = false;
|
||||
bool clear_mode = false;
|
||||
bool list_mode = false;
|
||||
bool got_module = false;
|
||||
|
||||
work_stack.clear();
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-add") {
|
||||
add_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-del") {
|
||||
del_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-clear") {
|
||||
clear_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-list") {
|
||||
list_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-module" && argidx+1 < args.size()) {
|
||||
RTLIL::IdString mod_name = RTLIL::escape_id(args[++argidx]);
|
||||
if (design->modules.count(mod_name) == 0)
|
||||
log_cmd_error("No such module: %s\n", mod_name.c_str());
|
||||
design->selected_active_module = mod_name;
|
||||
got_module = true;
|
||||
continue;
|
||||
}
|
||||
if (arg.size() > 0 && arg[0] == '-')
|
||||
log_cmd_error("Unkown option %s.\n", arg.c_str());
|
||||
select_stmt(design, arg);
|
||||
}
|
||||
|
||||
if (clear_mode && args.size() != 2)
|
||||
log_cmd_error("Option -clear can not be combined with other options.\n");
|
||||
|
||||
if (add_mode && del_mode)
|
||||
log_cmd_error("Options -add and -del can not be combined.\n");
|
||||
|
||||
if (list_mode && (add_mode || del_mode))
|
||||
log_cmd_error("Option -list can not be combined with -add or -del.\n");
|
||||
|
||||
if (work_stack.size() == 0 && got_module) {
|
||||
RTLIL::Selection sel;
|
||||
select_filter_active_mod(design, sel);
|
||||
work_stack.push_back(sel);
|
||||
}
|
||||
|
||||
while (work_stack.size() > 1) {
|
||||
select_op_union(design, work_stack.front(), work_stack.back());
|
||||
work_stack.pop_back();
|
||||
}
|
||||
|
||||
assert(design->selection_stack.size() > 0);
|
||||
|
||||
if (clear_mode)
|
||||
{
|
||||
design->selection_stack.back() = RTLIL::Selection(true);
|
||||
design->selected_active_module = std::string();
|
||||
return;
|
||||
}
|
||||
|
||||
if (list_mode)
|
||||
{
|
||||
RTLIL::Selection *sel = &design->selection_stack.back();
|
||||
if (work_stack.size() > 0)
|
||||
sel = &work_stack.back();
|
||||
sel->optimize(design);
|
||||
for (auto mod_it : design->modules)
|
||||
{
|
||||
if (design->selected_whole_module(mod_it.first))
|
||||
log("%s\n", mod_it.first.c_str());
|
||||
if (design->selected_module(mod_it.first)) {
|
||||
for (auto &it : mod_it.second->wires)
|
||||
if (design->selected_member(mod_it.first, it.first))
|
||||
log("%s/%s\n", mod_it.first.c_str(), it.first.c_str());
|
||||
for (auto &it : mod_it.second->memories)
|
||||
if (design->selected_member(mod_it.first, it.first))
|
||||
log("%s/%s\n", mod_it.first.c_str(), it.first.c_str());
|
||||
for (auto &it : mod_it.second->cells)
|
||||
if (design->selected_member(mod_it.first, it.first))
|
||||
log("%s/%s\n", mod_it.first.c_str(), it.first.c_str());
|
||||
for (auto &it : mod_it.second->processes)
|
||||
if (design->selected_member(mod_it.first, it.first))
|
||||
log("%s/%s\n", mod_it.first.c_str(), it.first.c_str());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (add_mode)
|
||||
{
|
||||
if (work_stack.size() == 0)
|
||||
log_cmd_error("Nothing to add to selection.\n");
|
||||
select_op_union(design, design->selection_stack.back(), work_stack.back());
|
||||
design->selection_stack.back().optimize(design);
|
||||
return;
|
||||
}
|
||||
|
||||
if (del_mode)
|
||||
{
|
||||
if (work_stack.size() == 0)
|
||||
log_cmd_error("Nothing to delete from selection.\n");
|
||||
select_op_diff(design, design->selection_stack.back(), work_stack.back());
|
||||
design->selection_stack.back().optimize(design);
|
||||
return;
|
||||
}
|
||||
|
||||
if (work_stack.size() == 0) {
|
||||
RTLIL::Selection &sel = design->selection_stack.back();
|
||||
if (sel.full_selection)
|
||||
log("*\n");
|
||||
for (auto &it : sel.selected_modules)
|
||||
log("%s\n", it.c_str());
|
||||
for (auto &it : sel.selected_members)
|
||||
for (auto &it2 : it.second)
|
||||
log("%s/%s\n", it.first.c_str(), it2.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
design->selection_stack.back() = work_stack.back();
|
||||
design->selection_stack.back().optimize(design);
|
||||
}
|
||||
} SelectPass;
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
Copyright (c) 2011, Micael Hildenborg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Micael Hildenborg nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Contributors:
|
||||
Gustav
|
||||
Several members in the gamedev.se forum.
|
||||
Gregory Petrosyan
|
||||
*/
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
namespace sha1
|
||||
{
|
||||
namespace // local
|
||||
{
|
||||
// Rotate an integer value to left.
|
||||
inline unsigned int rol(const unsigned int value,
|
||||
const unsigned int steps)
|
||||
{
|
||||
return ((value << steps) | (value >> (32 - steps)));
|
||||
}
|
||||
|
||||
// Sets the first 16 integers in the buffert to zero.
|
||||
// Used for clearing the W buffert.
|
||||
inline void clearWBuffert(unsigned int* buffert)
|
||||
{
|
||||
for (int pos = 16; --pos >= 0;)
|
||||
{
|
||||
buffert[pos] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void innerHash(unsigned int* result, unsigned int* w)
|
||||
{
|
||||
unsigned int a = result[0];
|
||||
unsigned int b = result[1];
|
||||
unsigned int c = result[2];
|
||||
unsigned int d = result[3];
|
||||
unsigned int e = result[4];
|
||||
|
||||
int round = 0;
|
||||
|
||||
#define sha1macro(func,val) \
|
||||
{ \
|
||||
const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
|
||||
e = d; \
|
||||
d = c; \
|
||||
c = rol(b, 30); \
|
||||
b = a; \
|
||||
a = t; \
|
||||
}
|
||||
|
||||
while (round < 16)
|
||||
{
|
||||
sha1macro((b & c) | (~b & d), 0x5a827999)
|
||||
++round;
|
||||
}
|
||||
while (round < 20)
|
||||
{
|
||||
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||
sha1macro((b & c) | (~b & d), 0x5a827999)
|
||||
++round;
|
||||
}
|
||||
while (round < 40)
|
||||
{
|
||||
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||
sha1macro(b ^ c ^ d, 0x6ed9eba1)
|
||||
++round;
|
||||
}
|
||||
while (round < 60)
|
||||
{
|
||||
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||
sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
|
||||
++round;
|
||||
}
|
||||
while (round < 80)
|
||||
{
|
||||
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||
sha1macro(b ^ c ^ d, 0xca62c1d6)
|
||||
++round;
|
||||
}
|
||||
|
||||
#undef sha1macro
|
||||
|
||||
result[0] += a;
|
||||
result[1] += b;
|
||||
result[2] += c;
|
||||
result[3] += d;
|
||||
result[4] += e;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void calc(const void* src, const int bytelength, unsigned char* hash)
|
||||
{
|
||||
// Init the result array.
|
||||
unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
|
||||
|
||||
// Cast the void src pointer to be the byte array we can work with.
|
||||
const unsigned char* sarray = (const unsigned char*) src;
|
||||
|
||||
// The reusable round buffer
|
||||
unsigned int w[80];
|
||||
|
||||
// Loop through all complete 64byte blocks.
|
||||
const int endOfFullBlocks = bytelength - 64;
|
||||
int endCurrentBlock;
|
||||
int currentBlock = 0;
|
||||
|
||||
while (currentBlock <= endOfFullBlocks)
|
||||
{
|
||||
endCurrentBlock = currentBlock + 64;
|
||||
|
||||
// Init the round buffer with the 64 byte block data.
|
||||
for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
|
||||
{
|
||||
// This line will swap endian on big endian and keep endian on little endian.
|
||||
w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
|
||||
| (((unsigned int) sarray[currentBlock + 2]) << 8)
|
||||
| (((unsigned int) sarray[currentBlock + 1]) << 16)
|
||||
| (((unsigned int) sarray[currentBlock]) << 24);
|
||||
}
|
||||
innerHash(result, w);
|
||||
}
|
||||
|
||||
// Handle the last and not full 64 byte block if existing.
|
||||
endCurrentBlock = bytelength - currentBlock;
|
||||
clearWBuffert(w);
|
||||
int lastBlockBytes = 0;
|
||||
for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes)
|
||||
{
|
||||
w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
|
||||
}
|
||||
w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
|
||||
if (endCurrentBlock >= 56)
|
||||
{
|
||||
innerHash(result, w);
|
||||
clearWBuffert(w);
|
||||
}
|
||||
w[15] = bytelength << 3;
|
||||
innerHash(result, w);
|
||||
|
||||
// Store hash in result pointer, and make sure we get in in the correct order on both endian models.
|
||||
for (int hashByte = 20; --hashByte >= 0;)
|
||||
{
|
||||
hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void toHexString(const unsigned char* hash, char* hexstring)
|
||||
{
|
||||
const char hexDigits[] = { "0123456789abcdef" };
|
||||
|
||||
for (int hashByte = 20; --hashByte >= 0;)
|
||||
{
|
||||
hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf];
|
||||
hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf];
|
||||
}
|
||||
hexstring[40] = 0;
|
||||
}
|
||||
} // namespace sha1
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright (c) 2011, Micael Hildenborg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Micael Hildenborg nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SHA1_DEFINED
|
||||
#define SHA1_DEFINED
|
||||
|
||||
namespace sha1
|
||||
{
|
||||
|
||||
/**
|
||||
@param src points to any kind of data to be hashed.
|
||||
@param bytelength the number of bytes to hash from the src pointer.
|
||||
@param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.
|
||||
*/
|
||||
void calc(const void* src, const int bytelength, unsigned char* hash);
|
||||
|
||||
/**
|
||||
@param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function.
|
||||
@param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string.
|
||||
*/
|
||||
void toHexString(const unsigned char* hash, char* hexstring);
|
||||
|
||||
} // namespace sha1
|
||||
|
||||
#endif // SHA1_DEFINED
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* 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 <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#undef CLUSTER_CELLS_AND_PORTBOXES
|
||||
|
||||
struct ShowWorker
|
||||
{
|
||||
CellTypes ct;
|
||||
|
||||
std::vector<std::string> dot_escape_store;
|
||||
std::map<RTLIL::IdString, int> dot_id2num_store;
|
||||
std::map<RTLIL::IdString, int> autonames;
|
||||
int single_idx_count;
|
||||
|
||||
struct net_conn { std::set<std::string> in, out; };
|
||||
std::map<std::string, net_conn> net_conn_map;
|
||||
|
||||
FILE *f;
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
int page_counter;
|
||||
|
||||
const char *escape(std::string id, bool is_name = false)
|
||||
{
|
||||
if (id.size() == 0)
|
||||
return "";
|
||||
|
||||
if (id[0] == '$' && is_name) {
|
||||
if (autonames.count(id) == 0) {
|
||||
autonames[id] = autonames.size() + 1;
|
||||
log("Generated short name for internal identifier: _%d_ -> %s\n", autonames[id], id.c_str());
|
||||
}
|
||||
id = stringf("_%d_", autonames[id]);
|
||||
}
|
||||
|
||||
if (id[0] == '\\')
|
||||
id = id.substr(1);
|
||||
|
||||
std::string str;
|
||||
for (char ch : id) {
|
||||
if (ch == '\\' || ch == '"')
|
||||
str += "\\";
|
||||
str += ch;
|
||||
}
|
||||
|
||||
dot_escape_store.push_back(str);
|
||||
return dot_escape_store.back().c_str();
|
||||
}
|
||||
|
||||
int id2num(RTLIL::IdString id)
|
||||
{
|
||||
if (dot_id2num_store.count(id) > 0)
|
||||
return dot_id2num_store[id];
|
||||
return dot_id2num_store[id] = dot_id2num_store.size() + 1;
|
||||
}
|
||||
|
||||
std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true)
|
||||
{
|
||||
sig.optimize();
|
||||
|
||||
if (sig.chunks.size() == 0) {
|
||||
fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count);
|
||||
return stringf("v%d", single_idx_count++);
|
||||
}
|
||||
|
||||
if (sig.chunks.size() == 1) {
|
||||
RTLIL::SigChunk &c = sig.chunks[0];
|
||||
if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) {
|
||||
if (!range_check || c.wire->width == c.width)
|
||||
return stringf("n%d", id2num(c.wire->name));
|
||||
} else {
|
||||
fprintf(f, "v%d [ label=\"%s\" ];\n", single_idx_count, escape(log_signal(c), true));
|
||||
return stringf("v%d", single_idx_count++);
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string gen_portbox(std::string port, RTLIL::SigSpec sig, bool driver, std::string *node = NULL)
|
||||
{
|
||||
std::string code;
|
||||
std::string net = gen_signode_simple(sig);
|
||||
if (net.empty())
|
||||
{
|
||||
std::string label_string;
|
||||
sig.optimize();
|
||||
int pos = sig.width-1;
|
||||
int idx = single_idx_count++;
|
||||
for (int i = int(sig.chunks.size())-1; i >= 0; i--) {
|
||||
RTLIL::SigChunk &c = sig.chunks[i];
|
||||
net = gen_signode_simple(c, false);
|
||||
assert(!net.empty());
|
||||
if (driver) {
|
||||
label_string += stringf("<s%d> %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset);
|
||||
net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i));
|
||||
} else {
|
||||
label_string += stringf("<s%d> %d:%d - %d:%d |", i, c.offset+c.width-1, c.offset, pos, pos-c.width+1);
|
||||
net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i));
|
||||
}
|
||||
pos -= c.width;
|
||||
}
|
||||
if (label_string[label_string.size()-1] == '|')
|
||||
label_string = label_string.substr(0, label_string.size()-1);
|
||||
code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str());
|
||||
if (!port.empty()) {
|
||||
if (driver)
|
||||
code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", port.c_str(), idx);
|
||||
else
|
||||
code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", idx, port.c_str());
|
||||
}
|
||||
if (node != NULL)
|
||||
*node = stringf("x%d", idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!port.empty()) {
|
||||
if (driver)
|
||||
net_conn_map[net].in.insert(port);
|
||||
else
|
||||
net_conn_map[net].out.insert(port);
|
||||
}
|
||||
if (node != NULL)
|
||||
*node = net;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void handle_module()
|
||||
{
|
||||
single_idx_count = 0;
|
||||
dot_escape_store.clear();
|
||||
dot_id2num_store.clear();
|
||||
net_conn_map.clear();
|
||||
|
||||
fprintf(f, "digraph \"%s\" {\n", escape(module->name));
|
||||
fprintf(f, "rankdir=\"LR\";\n");
|
||||
fprintf(f, "remincross=true;\n");
|
||||
|
||||
std::map<std::string, std::string> wires_on_demand;
|
||||
for (auto &it : module->wires) {
|
||||
if (!design->selected_member(module->name, it.first))
|
||||
continue;
|
||||
const char *shape = "diamond";
|
||||
if (it.second->port_input || it.second->port_output)
|
||||
shape = "octagon";
|
||||
if (it.first[0] == '\\')
|
||||
fprintf(f, "n%d [ shape=%s, label=\"%s\" ];\n",
|
||||
id2num(it.first), shape, escape(it.first));
|
||||
else {
|
||||
wires_on_demand[stringf("n%d", id2num(it.first))] = it.first;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->cells)
|
||||
{
|
||||
if (!design->selected_member(module->name, it.first))
|
||||
continue;
|
||||
|
||||
std::vector<RTLIL::IdString> in_ports, out_ports;
|
||||
|
||||
for (auto &conn : it.second->connections) {
|
||||
if (ct.cell_input(it.second->type, conn.first))
|
||||
in_ports.push_back(conn.first);
|
||||
else
|
||||
out_ports.push_back(conn.first);
|
||||
}
|
||||
|
||||
std::string label_string = "{{";
|
||||
|
||||
for (auto &p : in_ports)
|
||||
label_string += stringf("<p%d> %s|", id2num(p), escape(p));
|
||||
if (label_string[label_string.size()-1] == '|')
|
||||
label_string = label_string.substr(0, label_string.size()-1);
|
||||
|
||||
label_string += stringf("}|%s\\n%s|{", escape(it.first, true), escape(it.second->type));
|
||||
|
||||
for (auto &p : out_ports)
|
||||
label_string += stringf("<p%d> %s|", id2num(p), escape(p));
|
||||
if (label_string[label_string.size()-1] == '|')
|
||||
label_string = label_string.substr(0, label_string.size()-1);
|
||||
|
||||
label_string += "}}";
|
||||
|
||||
std::string code;
|
||||
for (auto &conn : it.second->connections) {
|
||||
code += gen_portbox(stringf("c%d:p%d", id2num(it.first), id2num(conn.first)),
|
||||
conn.second, !ct.cell_input(it.second->type, conn.first));
|
||||
}
|
||||
|
||||
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
||||
if (!code.empty())
|
||||
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\" ];\n%s}\n",
|
||||
id2num(it.first), id2num(it.first), label_string.c_str(), code.c_str());
|
||||
else
|
||||
#endif
|
||||
fprintf(f, "c%d [ shape=record, label=\"%s\" ];\n%s",
|
||||
id2num(it.first), label_string.c_str(), code.c_str());
|
||||
}
|
||||
|
||||
for (auto &conn : module->connections)
|
||||
{
|
||||
bool found_lhs_wire = false;
|
||||
for (auto &c : conn.first.chunks) {
|
||||
if (c.wire != NULL && design->selected_member(module->name, c.wire->name))
|
||||
found_lhs_wire = true;
|
||||
}
|
||||
bool found_rhs_wire = false;
|
||||
for (auto &c : conn.second.chunks) {
|
||||
if (c.wire != NULL && design->selected_member(module->name, c.wire->name))
|
||||
found_rhs_wire = true;
|
||||
}
|
||||
if (!found_lhs_wire || !found_rhs_wire)
|
||||
continue;
|
||||
|
||||
std::string code, left_node, right_node;
|
||||
code += gen_portbox("", conn.second, false, &left_node);
|
||||
code += gen_portbox("", conn.first, true, &right_node);
|
||||
fprintf(f, "%s", code.c_str());
|
||||
|
||||
if (left_node[0] == 'x' && right_node[0] == 'x')
|
||||
fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", left_node.c_str(), right_node.c_str());
|
||||
else if (left_node[0] == 'x')
|
||||
net_conn_map[right_node].in.insert(left_node);
|
||||
else if (right_node[0] == 'x')
|
||||
net_conn_map[left_node].out.insert(right_node);
|
||||
else {
|
||||
net_conn_map[right_node].in.insert(stringf("x%d:e", single_idx_count));
|
||||
net_conn_map[left_node].out.insert(stringf("x%d:w", single_idx_count));
|
||||
fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : net_conn_map)
|
||||
{
|
||||
if (wires_on_demand.count(it.first) > 0) {
|
||||
if (it.second.in.size() == 1 && it.second.out.size() == 1) {
|
||||
fprintf(f, "%s:e -> %s:w;\n", it.second.in.begin()->c_str(), it.second.out.begin()->c_str());
|
||||
continue;
|
||||
}
|
||||
if (it.second.in.size() == 0 || it.second.out.size() == 0)
|
||||
fprintf(f, "%s [ shape=diamond, label=\"%s\" ];\n", it.first.c_str(), escape(wires_on_demand[it.first], true));
|
||||
else
|
||||
fprintf(f, "%s [ shape=point ];\n", it.first.c_str());
|
||||
}
|
||||
for (auto &it2 : it.second.in)
|
||||
fprintf(f, "%s:e -> %s:w;\n", it2.c_str(), it.first.c_str());
|
||||
for (auto &it2 : it.second.out)
|
||||
fprintf(f, "%s:e -> %s:w;\n", it.first.c_str(), it2.c_str());
|
||||
}
|
||||
|
||||
fprintf(f, "};\n");
|
||||
}
|
||||
|
||||
ShowWorker(FILE *f, RTLIL::Design *design) : f(f), design(design)
|
||||
{
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
|
||||
design->optimize();
|
||||
page_counter = 0;
|
||||
for (auto &mod_it : design->modules)
|
||||
{
|
||||
module = mod_it.second;
|
||||
if (!design->selected_module(module->name))
|
||||
continue;
|
||||
if (design->selected_whole_module(module->name))
|
||||
log("Dumping module %s to page %d.\n", module->name.c_str(), ++page_counter);
|
||||
else
|
||||
log("Dumping selected parts of module %s to page %d.\n", module->name.c_str(), ++page_counter);
|
||||
handle_module();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ShowPass : public Pass {
|
||||
ShowPass() : Pass("show") { }
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Generating Graphviz representation of design.\n");
|
||||
|
||||
std::string viewer_exe;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-viewer" && argidx+1 < args.size()) {
|
||||
viewer_exe = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
log("Writing dot description to `yosys-show.dot'.\n");
|
||||
FILE *f = fopen("yosys-show.dot", "w");
|
||||
if (f == NULL)
|
||||
log_cmd_error("Can't open dot file `yosys-show.dot' for writing.\n");
|
||||
ShowWorker worker(f, design);
|
||||
fclose(f);
|
||||
|
||||
if (worker.page_counter == 0)
|
||||
log_cmd_error("Nothing there to show.\n");
|
||||
|
||||
std::string cmd = stringf("dot -Tps -o yosys-show.ps yosys-show.dot");
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (system(cmd.c_str()) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
|
||||
if (!viewer_exe.empty()) {
|
||||
cmd = stringf("%s yosys-show.ps &", viewer_exe.c_str());
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (system(cmd.c_str()) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
}
|
||||
}
|
||||
} ShowPass;
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* 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 SIGTOOLS_H
|
||||
#define SIGTOOLS_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
#include <assert.h>
|
||||
#include <set>
|
||||
|
||||
struct SigPool
|
||||
{
|
||||
typedef std::pair<RTLIL::Wire*,int> bitDef_t;
|
||||
std::set<bitDef_t> bits;
|
||||
|
||||
void clear()
|
||||
{
|
||||
bits.clear();
|
||||
}
|
||||
|
||||
void add(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
bits.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void add(const SigPool &other)
|
||||
{
|
||||
for (auto &bit : other.bits)
|
||||
bits.insert(bit);
|
||||
}
|
||||
|
||||
void del(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
bits.erase(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void del(const SigPool &other)
|
||||
{
|
||||
for (auto &bit : other.bits)
|
||||
bits.insert(bit);
|
||||
}
|
||||
|
||||
void expand(RTLIL::SigSpec from, RTLIL::SigSpec to)
|
||||
{
|
||||
from.expand();
|
||||
to.expand();
|
||||
assert(from.chunks.size() == to.chunks.size());
|
||||
for (size_t i = 0; i < from.chunks.size(); i++) {
|
||||
bitDef_t bit_from(from.chunks[i].wire, from.chunks[i].offset);
|
||||
bitDef_t bit_to(to.chunks[i].wire, to.chunks[i].offset);
|
||||
if (bit_from.first == NULL || bit_to.first == NULL)
|
||||
continue;
|
||||
if (bits.count(bit_from) > 0)
|
||||
bits.insert(bit_to);
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::SigSpec extract(RTLIL::SigSpec sig)
|
||||
{
|
||||
RTLIL::SigSpec result;
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (bits.count(bit) > 0)
|
||||
result.append(c);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec remove(RTLIL::SigSpec sig)
|
||||
{
|
||||
RTLIL::SigSpec result;
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (bits.count(bit) == 0)
|
||||
result.append(c);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool check_any(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (bits.count(bit) != 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_all(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (bits.count(bit) == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct SigSet
|
||||
{
|
||||
typedef std::pair<RTLIL::Wire*,int> bitDef_t;
|
||||
std::map<bitDef_t, std::set<T>> bits;
|
||||
|
||||
void clear()
|
||||
{
|
||||
bits.clear();
|
||||
}
|
||||
|
||||
void insert(RTLIL::SigSpec sig, T data)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
bits[bit].insert(data);
|
||||
}
|
||||
}
|
||||
|
||||
void erase(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
bits[bit].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void erase(RTLIL::SigSpec sig, T data)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
bits[bit].erase(data);
|
||||
}
|
||||
}
|
||||
|
||||
void find(RTLIL::SigSpec sig, std::set<T> &result)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks) {
|
||||
if (c.wire == NULL)
|
||||
continue;
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
for (auto &data : bits[bit])
|
||||
result.insert(data);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<T> find(RTLIL::SigSpec sig)
|
||||
{
|
||||
std::set<T> result;
|
||||
find(sig, result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct SigMap
|
||||
{
|
||||
typedef std::pair<RTLIL::Wire*,int> bitDef_t;
|
||||
|
||||
struct shared_bit_data_t {
|
||||
RTLIL::SigChunk chunk;
|
||||
std::set<bitDef_t> bits;
|
||||
};
|
||||
|
||||
std::map<bitDef_t, shared_bit_data_t*> bits;
|
||||
|
||||
SigMap(RTLIL::Module *module = NULL)
|
||||
{
|
||||
if (module != NULL)
|
||||
set(module);
|
||||
}
|
||||
|
||||
SigMap(const SigMap &other)
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
|
||||
const SigMap &operator=(const SigMap &other)
|
||||
{
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void copy(const SigMap &other)
|
||||
{
|
||||
clear();
|
||||
for (auto &bit : other.bits) {
|
||||
bits[bit.first] = new shared_bit_data_t;
|
||||
bits[bit.first]->chunk = bit.second->chunk;
|
||||
bits[bit.first]->bits = bit.second->bits;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(SigMap &other)
|
||||
{
|
||||
bits.swap(other.bits);
|
||||
}
|
||||
|
||||
~SigMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::set<shared_bit_data_t*> all_bd_ptr;
|
||||
for (auto &it : bits)
|
||||
all_bd_ptr.insert(it.second);
|
||||
for (auto bd_ptr : all_bd_ptr)
|
||||
delete bd_ptr;
|
||||
bits.clear();
|
||||
}
|
||||
|
||||
void set(RTLIL::Module *module)
|
||||
{
|
||||
clear();
|
||||
for (auto &it : module->connections)
|
||||
add(it.first, it.second);
|
||||
}
|
||||
|
||||
// internal helper function
|
||||
void register_bit(const RTLIL::SigChunk &c)
|
||||
{
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (c.wire && bits.count(bit) == 0) {
|
||||
shared_bit_data_t *bd = new shared_bit_data_t;
|
||||
bd->chunk = c;
|
||||
bd->bits.insert(bit);
|
||||
bits[bit] = bd;
|
||||
}
|
||||
}
|
||||
|
||||
// internal helper function
|
||||
void unregister_bit(const RTLIL::SigChunk &c)
|
||||
{
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (c.wire && bits.count(bit) > 0) {
|
||||
shared_bit_data_t *bd = bits[bit];
|
||||
bd->bits.erase(bit);
|
||||
if (bd->bits.size() == 0)
|
||||
delete bd;
|
||||
bits.erase(bit);
|
||||
}
|
||||
}
|
||||
|
||||
// internal helper function
|
||||
void merge_bit(const RTLIL::SigChunk &c1, const RTLIL::SigChunk &c2)
|
||||
{
|
||||
assert(c1.wire != NULL && c2.wire != NULL);
|
||||
assert(c1.width == 1 && c2.width == 1);
|
||||
|
||||
bitDef_t b1(c1.wire, c1.offset);
|
||||
bitDef_t b2(c2.wire, c2.offset);
|
||||
|
||||
shared_bit_data_t *bd1 = bits[b1];
|
||||
shared_bit_data_t *bd2 = bits[b2];
|
||||
assert(bd1 != NULL && bd2 != NULL);
|
||||
|
||||
if (bd1 == bd2)
|
||||
return;
|
||||
|
||||
if (bd1->bits.size() < bd2->bits.size())
|
||||
{
|
||||
for (auto &bit : bd1->bits)
|
||||
bits[bit] = bd2;
|
||||
bd2->bits.insert(bd1->bits.begin(), bd1->bits.end());
|
||||
delete bd1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bd1->chunk = bd2->chunk;
|
||||
for (auto &bit : bd2->bits)
|
||||
bits[bit] = bd1;
|
||||
bd1->bits.insert(bd2->bits.begin(), bd2->bits.end());
|
||||
delete bd2;
|
||||
}
|
||||
}
|
||||
|
||||
// internal helper function
|
||||
void set_bit(const RTLIL::SigChunk &c1, const RTLIL::SigChunk &c2)
|
||||
{
|
||||
assert(c1.wire != NULL);
|
||||
assert(c1.width == 1 && c2.width == 1);
|
||||
bitDef_t bit(c1.wire, c1.offset);
|
||||
assert(bits.count(bit) > 0);
|
||||
bits[bit]->chunk = c2;
|
||||
}
|
||||
|
||||
// internal helper function
|
||||
void map_bit(RTLIL::SigChunk &c)
|
||||
{
|
||||
assert(c.width == 1);
|
||||
bitDef_t bit(c.wire, c.offset);
|
||||
if (c.wire && bits.count(bit) > 0)
|
||||
c = bits[bit]->chunk;
|
||||
}
|
||||
|
||||
void add(RTLIL::SigSpec from, RTLIL::SigSpec to)
|
||||
{
|
||||
from.expand();
|
||||
to.expand();
|
||||
|
||||
assert(from.chunks.size() == to.chunks.size());
|
||||
for (size_t i = 0; i < from.chunks.size(); i++)
|
||||
{
|
||||
RTLIL::SigChunk &cf = from.chunks[i];
|
||||
RTLIL::SigChunk &ct = to.chunks[i];
|
||||
|
||||
if (cf.wire == NULL)
|
||||
continue;
|
||||
|
||||
register_bit(cf);
|
||||
register_bit(ct);
|
||||
|
||||
if (ct.wire != NULL)
|
||||
merge_bit(cf, ct);
|
||||
else
|
||||
set_bit(cf, ct);
|
||||
}
|
||||
}
|
||||
|
||||
void add(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (size_t i = 0; i < sig.chunks.size(); i++)
|
||||
{
|
||||
RTLIL::SigChunk &c = sig.chunks[i];
|
||||
if (c.wire != NULL) {
|
||||
register_bit(c);
|
||||
set_bit(c, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void del(RTLIL::SigSpec sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks)
|
||||
unregister_bit(c);
|
||||
}
|
||||
|
||||
void apply(RTLIL::SigSpec &sig)
|
||||
{
|
||||
sig.expand();
|
||||
for (auto &c : sig.chunks)
|
||||
map_bit(c);
|
||||
sig.optimize();
|
||||
}
|
||||
|
||||
RTLIL::SigSpec operator()(RTLIL::SigSpec sig)
|
||||
{
|
||||
apply(sig);
|
||||
return sig;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SIGTOOLS_H */
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
OBJS += passes/abc/abc.o
|
||||
OBJS += passes/abc/vlparse.o
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
#define FILTERLIB
|
||||
#include "libparse.cc"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += passes/hierarchy/hierarchy.o
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue