Compare commits

...

21 Commits

Author SHA1 Message Date
andy fox 0792267009
Merge e63a0685ab into 1717a0b9c0 2024-11-28 00:11:25 +01:00
Martin Povišer 1717a0b9c0
Merge pull request #4721 from ldoolitt/main
kernel/drivertools.h: avoid maybe-uninitialized compile warnings
2024-11-28 00:09:43 +01:00
Martin Povišer 956313efe8
Merge pull request #4742 from YosysHQ/hierarchy_notify_top_attr
Print a note about finding attribute (* top *) in hierarchy
2024-11-28 00:07:18 +01:00
Martin Povišer 3bab837bc9
Merge pull request #4765 from georgerennie/george/rtlil_case_rule
read_rtlil: Warn on assigns after switches in case rules
2024-11-28 00:01:21 +01:00
KrystalDelusion 698c464109
Merge pull request #4767 from YosysHQ/krys/latest-compilers
test-compile: Use newer clang and gcc versions
2024-11-28 11:51:38 +13:00
github-actions[bot] 98b4affc4a Bump version 2024-11-26 01:25:27 +00:00
KrystalDelusion 1e0e367aed
test-compile: Drop back to gcc-13 2024-11-26 10:18:09 +13:00
KrystalDelusion 6ff5823d6a
test-compile: Use clang-18 and gcc-14
The 'newest' compilers are actually not all that new, they're just the default for the image.  Instead provide explicit versions.
2024-11-26 09:59:52 +13:00
Miodrag Milanović 29e8812bab
Merge pull request #4724 from YosysHQ/micko/blackbox_verific
verific: fix blackbox regression and add test case
2024-11-25 15:06:54 +01:00
Miodrag Milanović 9512ec4bbc
Merge pull request #4764 from YosysHQ/micko/verific_vhdl_assert
verific : VHDL assert DFF initial value set on Verific library patch
2024-11-25 15:06:36 +01:00
George Rennie 8148ebd1ad docs: document that assigns must come before switches in case rules 2024-11-21 22:41:13 +01:00
George Rennie 4a057b3c44 read_rtlil: warn on assigns after switches in case rules 2024-11-21 22:41:13 +01:00
Miodrag Milanovic d6bd521487 verific : VHDL assert DFF initial value set on Verific library patch side 2024-11-21 13:43:26 +01:00
N. Engelhardt 96c526d1ba Print a note about finding attribute (* top *) in hierarchy 2024-11-13 10:21:44 +01:00
Larry Doolittle 3ae9ca7c2b drivertools.h: switch from log_assert(0) to log_abort() for new feature 2024-11-08 10:30:11 -08:00
Miodrag Milanovic df391f5816 verific: fix blackbox regression and add test case 2024-11-08 14:57:04 +01:00
Larry Doolittle d36a387aca kernel/drivertools.h: avoid maybe-uninitialized compile warnings
Initialize "unsigned int inner" in hash() functions
Includes a log_assert() that might help catch corrupted data structures
or future incomplete modification of DriveType definition
2024-11-07 19:49:25 -08:00
andyfox-rushc e63a0685ab
Parallel wallace tree
Signed-off-by: andyfox-rushc <andy@rushc.com>
2024-10-20 16:14:53 -07:00
andyfox-rushc d3fc63f694
Parallel Wallace Tree Generation
Signed-off-by: andyfox-rushc <andy@rushc.com>
2024-05-28 15:42:06 -07:00
andy fox 542660246f
Merge branch 'YosysHQ:main' into master 2024-05-28 14:30:06 -07:00
andyfox-rushc 9a0b60a66f
parallel reduction in booth.cc 2024-04-04 00:15:51 -07:00
12 changed files with 464 additions and 185 deletions

View File

@ -32,9 +32,9 @@ jobs:
# oldest supported # oldest supported
- 'clang-14' - 'clang-14'
- 'gcc-10' - 'gcc-10'
# newest # newest, make sure to update maximum standard step to match
- 'clang' - 'clang-18'
- 'gcc' - 'gcc-13'
include: include:
# macOS # macOS
- os: macos-13 - os: macos-13
@ -72,7 +72,7 @@ jobs:
# maximum standard, only on newest compilers # maximum standard, only on newest compilers
- name: Build C++20 - name: Build C++20
if: ${{ matrix.compiler == 'clang' || matrix.compiler == 'gcc'}} if: ${{ matrix.compiler == 'clang-18' || matrix.compiler == 'gcc-13' }}
shell: bash shell: bash
run: | run: |
make config-$CC_SHORT make config-$CC_SHORT

View File

@ -155,7 +155,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE CXXFLAGS += -D_DEFAULT_SOURCE
endif endif
YOSYS_VER := 0.47+116 YOSYS_VER := 0.47+121
# Note: We arrange for .gitcommit to contain the (short) commit hash in # Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo # tarballs generated with git-archive(1) using .gitattributes. The git repo

View File

@ -242,7 +242,7 @@ Processes
Declares a process, with zero or more attributes, with the given identifier in Declares a process, with zero or more attributes, with the given identifier in
the enclosing module. The body of a process consists of zero or more the enclosing module. The body of a process consists of zero or more
assignments, exactly one switch, and zero or more syncs. assignments followed by zero or more switches and zero or more syncs.
See :ref:`sec:rtlil_process` for an overview of processes. See :ref:`sec:rtlil_process` for an overview of processes.
@ -250,7 +250,7 @@ See :ref:`sec:rtlil_process` for an overview of processes.
<process> ::= <attr-stmt>* <proc-stmt> <process-body> <proc-end-stmt> <process> ::= <attr-stmt>* <proc-stmt> <process-body> <proc-end-stmt>
<proc-stmt> ::= process <id> <eol> <proc-stmt> ::= process <id> <eol>
<process-body> ::= <assign-stmt>* <switch>? <assign-stmt>* <sync>* <process-body> ::= <assign-stmt>* <switch>* <sync>*
<assign-stmt> ::= assign <dest-sigspec> <src-sigspec> <eol> <assign-stmt> ::= assign <dest-sigspec> <src-sigspec> <eol>
<dest-sigspec> ::= <sigspec> <dest-sigspec> ::= <sigspec>
<src-sigspec> ::= <sigspec> <src-sigspec> ::= <sigspec>
@ -262,8 +262,8 @@ Switches
Switches test a signal for equality against a list of cases. Each case specifies Switches test a signal for equality against a list of cases. Each case specifies
a comma-separated list of signals to check against. If there are no signals in a comma-separated list of signals to check against. If there are no signals in
the list, then the case is the default case. The body of a case consists of zero the list, then the case is the default case. The body of a case consists of zero
or more switches and assignments. Both switches and cases may have zero or more or more assignments followed by zero or more switches. Both switches and cases
attributes. may have zero or more attributes.
.. code:: BNF .. code:: BNF
@ -272,7 +272,7 @@ attributes.
<case> ::= <attr-stmt>* <case-stmt> <case-body> <case> ::= <attr-stmt>* <case-stmt> <case-body>
<case-stmt> ::= case <compare>? <eol> <case-stmt> ::= case <compare>? <eol>
<compare> ::= <sigspec> (, <sigspec>)* <compare> ::= <sigspec> (, <sigspec>)*
<case-body> ::= (<switch> | <assign-stmt>)* <case-body> ::= <assign-stmt>* <switch>*
<switch-end-stmt> ::= end <eol> <switch-end-stmt> ::= end <eol>
Syncs Syncs

View File

@ -31,6 +31,11 @@ void rtlil_frontend_yyerror(char const *s)
YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s); YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s);
} }
void rtlil_frontend_yywarning(char const *s)
{
YOSYS_NAMESPACE_PREFIX log_warning("In line %d: %s\n", rtlil_frontend_yyget_lineno(), s);
}
YOSYS_NAMESPACE_BEGIN YOSYS_NAMESPACE_BEGIN
struct RTLILFrontend : public Frontend { struct RTLILFrontend : public Frontend {

View File

@ -42,6 +42,7 @@ YOSYS_NAMESPACE_END
extern int rtlil_frontend_yydebug; extern int rtlil_frontend_yydebug;
int rtlil_frontend_yylex(void); int rtlil_frontend_yylex(void);
void rtlil_frontend_yyerror(char const *s); void rtlil_frontend_yyerror(char const *s);
void rtlil_frontend_yywarning(char const *s);
void rtlil_frontend_yyrestart(FILE *f); void rtlil_frontend_yyrestart(FILE *f);
int rtlil_frontend_yyparse(void); int rtlil_frontend_yyparse(void);
int rtlil_frontend_yylex_destroy(void); int rtlil_frontend_yylex_destroy(void);

View File

@ -344,6 +344,16 @@ assign_stmt:
TOK_ASSIGN sigspec sigspec EOL { TOK_ASSIGN sigspec sigspec EOL {
if (attrbuf.size() != 0) if (attrbuf.size() != 0)
rtlil_frontend_yyerror("dangling attribute"); rtlil_frontend_yyerror("dangling attribute");
// See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this
// warning
if (!switch_stack.back()->empty()) {
rtlil_frontend_yywarning(
"case rule assign statements after switch statements may cause unexpected behaviour. "
"The assign statement is reordered to come before all switch statements."
);
}
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
delete $2; delete $2;
delete $3; delete $3;

View File

@ -2126,12 +2126,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log(" assert condition %s.\n", log_signal(cond)); log(" assert condition %s.\n", log_signal(cond));
Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1); Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1);
// Initialize FF feeding condition to 1, in case it is not
// used by rest of design logic, to prevent failing on
// initial uninitialized state
if (cond.is_wire() && !cond.wire->name.isPublic())
cond.wire->attributes[ID::init] = Const(1,1);
import_attributes(cell->attributes, inst); import_attributes(cell->attributes, inst);
continue; continue;
} }
@ -3428,6 +3422,7 @@ struct VerificPass : public Pass {
RuntimeFlags::SetVar("veri_preserve_assignments", 1); RuntimeFlags::SetVar("veri_preserve_assignments", 1);
RuntimeFlags::SetVar("veri_preserve_comments", 1); RuntimeFlags::SetVar("veri_preserve_comments", 1);
RuntimeFlags::SetVar("veri_preserve_drivers", 1); RuntimeFlags::SetVar("veri_preserve_drivers", 1);
RuntimeFlags::SetVar("veri_create_empty_box", 1);
// Workaround for VIPER #13851 // Workaround for VIPER #13851
RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);

View File

@ -364,7 +364,7 @@ public:
unsigned int hash() const unsigned int hash() const
{ {
unsigned int inner; unsigned int inner = 0;
switch (type_) switch (type_)
{ {
case DriveType::NONE: case DriveType::NONE:
@ -385,6 +385,9 @@ public:
case DriveType::MULTIPLE: case DriveType::MULTIPLE:
inner = multiple_.hash(); inner = multiple_.hash();
break; break;
default:
log_abort();
break;
} }
return mkhash((unsigned int)type_, inner); return mkhash((unsigned int)type_, inner);
} }
@ -912,7 +915,7 @@ public:
unsigned int hash() const unsigned int hash() const
{ {
unsigned int inner; unsigned int inner = 0;
switch (type_) switch (type_)
{ {
case DriveType::NONE: case DriveType::NONE:
@ -933,6 +936,9 @@ public:
case DriveType::MULTIPLE: case DriveType::MULTIPLE:
inner = multiple_.hash(); inner = multiple_.hash();
break; break;
default:
log_abort();
break;
} }
return mkhash((unsigned int)type_, inner); return mkhash((unsigned int)type_, inner);
} }

View File

@ -1003,8 +1003,10 @@ struct HierarchyPass : public Pass {
if (top_mod == nullptr) if (top_mod == nullptr)
for (auto mod : design->modules()) for (auto mod : design->modules())
if (mod->get_bool_attribute(ID::top)) if (mod->get_bool_attribute(ID::top)) {
log("Attribute `top' found on module `%s'. Setting top module to %s.\n", log_id(mod), log_id(mod));
top_mod = mod; top_mod = mod;
}
if (top_mod == nullptr) if (top_mod == nullptr)
{ {

View File

@ -52,8 +52,8 @@ or in generic synthesis call with -booth argument:
synth -top my_design -booth synth -top my_design -booth
*/ */
//FIXME: These debug prints are broken now, should be fixed or removed.
//#define DEBUG_CPA //#define DEBUG_CPA
//#define DEBUG_CSA 1
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "kernel/yosys.h" #include "kernel/yosys.h"
@ -104,8 +104,7 @@ struct BoothPassWorker {
} }
// Booth unsigned radix 4 encoder // Booth unsigned radix 4 encoder
void BuildBur4e(std::string name, SigBit y0_i, SigBit y1_i, SigBit y2_i, void BuildBur4e(std::string name, SigBit y0_i, SigBit y1_i, SigBit y2_i, SigBit &one_o, SigBit &two_o, SigBit &s_o, SigBit &sb_o)
SigBit &one_o, SigBit &two_o, SigBit &s_o, SigBit &sb_o)
{ {
one_o = module->XorGate(NEW_ID_SUFFIX(name), y0_i, y1_i); one_o = module->XorGate(NEW_ID_SUFFIX(name), y0_i, y1_i);
s_o = y2_i; s_o = y2_i;
@ -116,8 +115,7 @@ struct BoothPassWorker {
void BuildBr4e(std::string name, SigBit y2_m1_i, void BuildBr4e(std::string name, SigBit y2_m1_i,
SigBit y2_i, // y2i SigBit y2_i, // y2i
SigBit y2_p1_i, SigBit y2_p1_i, SigBit &negi_o, SigBit &twoi_n_o, SigBit &onei_n_o, SigBit &cori_o)
SigBit &negi_o, SigBit &twoi_n_o, SigBit &onei_n_o, SigBit &cori_o)
{ {
auto y2_p1_n = module->NotGate(NEW_ID_SUFFIX(name), y2_p1_i); auto y2_p1_n = module->NotGate(NEW_ID_SUFFIX(name), y2_p1_i);
auto y2_n = module->NotGate(NEW_ID_SUFFIX(name), y2_i); auto y2_n = module->NotGate(NEW_ID_SUFFIX(name), y2_i);
@ -130,9 +128,8 @@ struct BoothPassWorker {
// (y2_p1 & y2_n & y2_m1_n) // (y2_p1 & y2_n & y2_m1_n)
// ) // )
twoi_n_o = module->NorGate(NEW_ID_SUFFIX(name), twoi_n_o = module->NorGate(NEW_ID_SUFFIX(name),
module->AndGate(NEW_ID_SUFFIX(name), y2_p1_n, module->AndGate(NEW_ID_SUFFIX(name), y2_i, y2_m1_i)), module->AndGate(NEW_ID_SUFFIX(name), y2_p1_n, module->AndGate(NEW_ID_SUFFIX(name), y2_i, y2_m1_i)),
module->AndGate(NEW_ID_SUFFIX(name), y2_p1_i, module->AndGate(NEW_ID_SUFFIX(name), y2_n, y2_m1_n)) module->AndGate(NEW_ID_SUFFIX(name), y2_p1_i, module->AndGate(NEW_ID_SUFFIX(name), y2_n, y2_m1_n)));
);
// onei_n = ~(y2_m1_i ^ y2_i); // onei_n = ~(y2_m1_i ^ y2_i);
onei_n_o = module->XnorGate(NEW_ID_SUFFIX(name), y2_m1_i, y2_i); onei_n_o = module->XnorGate(NEW_ID_SUFFIX(name), y2_m1_i, y2_i);
@ -143,17 +140,14 @@ struct BoothPassWorker {
// //
// signed booth radix 4 decoder // signed booth radix 4 decoder
// //
void BuildBr4d(std::string name, SigBit nxj_m1_i, SigBit twoi_n_i, SigBit xj_i, SigBit negi_i, SigBit onei_n_i, void BuildBr4d(std::string name, SigBit nxj_m1_i, SigBit twoi_n_i, SigBit xj_i, SigBit negi_i, SigBit onei_n_i, SigBit &ppij_o, SigBit &nxj_o)
SigBit &ppij_o, SigBit &nxj_o)
{ {
// nxj_in = xnor(xj,negi) // nxj_in = xnor(xj,negi)
// nxj_o = xnj_in, // nxj_o = xnj_in,
// ppij = ~( (nxj_m1_i | twoi_n_i) & (nxj_int | onei_n_i)); // ppij = ~( (nxj_m1_i | twoi_n_i) & (nxj_int | onei_n_i));
nxj_o = module->XnorGate(NEW_ID_SUFFIX(name), xj_i, negi_i); nxj_o = module->XnorGate(NEW_ID_SUFFIX(name), xj_i, negi_i);
ppij_o = module->NandGate(NEW_ID_SUFFIX(name), ppij_o = module->NandGate(NEW_ID_SUFFIX(name), module->OrGate(NEW_ID_SUFFIX(name), nxj_m1_i, twoi_n_i),
module->OrGate(NEW_ID_SUFFIX(name), nxj_m1_i, twoi_n_i), module->OrGate(NEW_ID_SUFFIX(name), nxj_o, onei_n_i));
module->OrGate(NEW_ID_SUFFIX(name), nxj_o, onei_n_i)
);
} }
/* /*
@ -161,9 +155,8 @@ struct BoothPassWorker {
using non-booth encoded logic. We can save a booth using non-booth encoded logic. We can save a booth
encoder for the first couple of bits. encoder for the first couple of bits.
*/ */
void BuildBoothQ1(std::string name, SigBit negi_i, SigBit cori_i, SigBit x0_i, SigBit x1_i, SigBit y0_i, void BuildBoothQ1(std::string name, SigBit negi_i, SigBit cori_i, SigBit x0_i, SigBit x1_i, SigBit y0_i, SigBit y1_i, SigBit &nxj_o,
SigBit y1_i, SigBit &cor_o, SigBit &pp0_o, SigBit &pp1_o)
SigBit &nxj_o, SigBit &cor_o, SigBit &pp0_o, SigBit &pp1_o)
{ {
/* /*
assign NXJO = ~(X1 ^ NEGI); assign NXJO = ~(X1 ^ NEGI);
@ -186,9 +179,8 @@ struct BoothPassWorker {
cor_o = module->AndGate(NEW_ID_SUFFIX(name), pp1_nor_pp0, cori_i); cor_o = module->AndGate(NEW_ID_SUFFIX(name), pp1_nor_pp0, cori_i);
} }
void BuildBitwiseFa(Module *mod, std::string name, const SigSpec &sig_a, const SigSpec &sig_b, void BuildBitwiseFa(Module *mod, std::string name, const SigSpec &sig_a, const SigSpec &sig_b, const SigSpec &sig_c, const SigSpec &sig_x,
const SigSpec &sig_c, const SigSpec &sig_x, const SigSpec &sig_y, const SigSpec &sig_y, const std::string &src = "")
const std::string &src = "")
{ {
// We can't emit a single wide full-adder cell here since // We can't emit a single wide full-adder cell here since
// there would typically be feedback loops involving the cells' // there would typically be feedback loops involving the cells'
@ -200,8 +192,7 @@ struct BoothPassWorker {
log_assert(sig_a.size() == sig_y.size()); log_assert(sig_a.size() == sig_y.size());
for (int i = 0; i < sig_a.size(); i++) for (int i = 0; i < sig_a.size(); i++)
mod->addFa(stringf("%s[%d]", name.c_str(), i), sig_a[i], sig_b[i], mod->addFa(stringf("%s[%d]", name.c_str(), i), sig_a[i], sig_b[i], sig_c[i], sig_x[i], sig_y[i], src);
sig_c[i], sig_x[i], sig_y[i], src);
} }
void run() void run()
@ -216,8 +207,7 @@ struct BoothPassWorker {
int x_sz = GetSize(A), y_sz = GetSize(B), z_sz = GetSize(Y); int x_sz = GetSize(A), y_sz = GetSize(B), z_sz = GetSize(Y);
if (x_sz < 4 || y_sz < 4 || z_sz < 8) { if (x_sz < 4 || y_sz < 4 || z_sz < 8) {
log_debug("Not mapping cell %s sized at %dx%x, %x: size below threshold\n", log_debug("Not mapping cell %s sized at %dx%x, %x: size below threshold\n", log_id(cell), x_sz, y_sz, z_sz);
log_id(cell), x_sz, y_sz, z_sz);
continue; continue;
} }
@ -240,7 +230,7 @@ struct BoothPassWorker {
y_sz_revised = y_sz + 1; y_sz_revised = y_sz + 1;
} else { } else {
x_sz_revised = y_sz; x_sz_revised = y_sz;
} }
} else { } else {
if (x_sz % 2 != 0) { if (x_sz % 2 != 0) {
y_sz_revised = x_sz + 1; y_sz_revised = x_sz + 1;
@ -258,7 +248,6 @@ struct BoothPassWorker {
log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0));
A.extend_u0(x_sz_revised, is_signed); A.extend_u0(x_sz_revised, is_signed);
B.extend_u0(y_sz_revised, is_signed); B.extend_u0(y_sz_revised, is_signed);
@ -280,18 +269,16 @@ struct BoothPassWorker {
if (!lowpower) if (!lowpower)
CreateBoothMult(module, CreateBoothMult(module,
A, // multiplicand A, // multiplicand
B, // multiplier(scanned) B, // multiplier(scanned)
Y, // result Y, // result
is_signed is_signed);
);
else else
CreateBoothLowpowerMult(module, CreateBoothLowpowerMult(module,
A, // multiplicand A, // multiplicand
B, // multiplier(scanned) B, // multiplier(scanned)
Y, // result Y, // result
is_signed is_signed);
);
module->remove(cell); module->remove(cell);
booth_counter++; booth_counter++;
@ -306,11 +293,10 @@ struct BoothPassWorker {
while (summands.size() > 2) { while (summands.size() > 2) {
std::vector<SigSpec> new_summands; std::vector<SigSpec> new_summands;
int i; int i;
for (i = 0; i < (int) summands.size() - 2; i += 3) { for (i = 0; i < (int)summands.size() - 2; i += 3) {
SigSpec x = module->addWire(NEW_ID, width); SigSpec x = module->addWire(NEW_ID, width);
SigSpec y = module->addWire(NEW_ID, width); SigSpec y = module->addWire(NEW_ID, width);
BuildBitwiseFa(module, NEW_ID.str(), summands[i], summands[i + 1], BuildBitwiseFa(module, NEW_ID.str(), summands[i], summands[i + 1], summands[i + 2], x, y);
summands[i + 2], x, y);
new_summands.push_back(y); new_summands.push_back(y);
new_summands.push_back({x.extract(0, width - 1), State::S0}); new_summands.push_back({x.extract(0, width - 1), State::S0});
} }
@ -335,10 +321,9 @@ struct BoothPassWorker {
*/ */
void CreateBoothMult(RTLIL::Module *module, void CreateBoothMult(RTLIL::Module *module,
SigSpec X, // multiplicand SigSpec X, // multiplicand
SigSpec Y, // multiplier SigSpec Y, // multiplier
SigSpec Z, SigSpec Z, bool is_signed)
bool is_signed)
{ // result { // result
int z_sz = Z.size(); int z_sz = Z.size();
@ -369,15 +354,12 @@ struct BoothPassWorker {
SigSpec ppij_row_n; SigSpec ppij_row_n;
BuildBoothMultDecoderRowN(module, BuildBoothMultDecoderRowN(module,
X, // multiplicand X, // multiplicand
one_int[i], two_int[i], s_int[i], sb_int[i], ppij_row_n, i, one_int[i], two_int[i], s_int[i], sb_int[i], ppij_row_n, i, is_signed);
is_signed
);
// data, shift, sign // data, shift, sign
ppij_int.push_back(std::make_tuple(ppij_row_n, i * 2, s_int[i])); ppij_int.push_back(std::make_tuple(ppij_row_n, i * 2, s_int[i]));
} }
// Debug dump out partial products // Debug dump out partial products
// DebugDumpPP(ppij_int); // DebugDumpPP(ppij_int);
@ -394,18 +376,50 @@ struct BoothPassWorker {
// Debug: dump out aligned partial products. // Debug: dump out aligned partial products.
// Later on yosys will clean up unused constants // Later on yosys will clean up unused constants
// DebugDumpAlignPP(aligned_pp);
SigSig wtree_sum = WallaceSum(z_sz, aligned_pp); #ifdef DEBUG_CSA
DebugDumpAlignPP(aligned_pp);
#endif
SigSpec s_vec;
SigSpec c_vec;
std::vector<std::vector<RTLIL::Cell *>> debug_csa_trees;
BuildCSATree(module, aligned_pp, s_vec, c_vec, debug_csa_trees);
#ifdef DEBUG_CSA
printf("Size of Sum Vec %d Size of Carry Vec %d\n", s_vec.size(), c_vec.size());
printf("Sum Vec %s \n", s_vec.as_string().c_str());
printf("Carry Vec %s \n", c_vec.as_string().c_str());
printf("Size of Sum %d Size of Result %d\n", s_vec.size(), Z.size());
#endif
// Debug code: Dump out the csa trees // Debug code: Dump out the csa trees
// DumpCSATrees(debug_csa_trees); // DumpCSATrees(debug_csa_trees);
// BuildCPA(module, s_vec, c_vec, Z);
// Build the CPA to do the final accumulation. // Build the CPA to do the final accumulation.
log_assert(wtree_sum.second[0] == State::S0); // log_assert(wtree_sum.second[0] == State::S0);
if (mapped_cpa) // wtree_sum.first, {State::S0, wtree_sum.second.extract_end(1)}, Z);
BuildCPA(module, wtree_sum.first, {State::S0, wtree_sum.second.extract_end(1)}, Z);
else // if (mapped_cpa)
module->addAdd(NEW_ID, wtree_sum.first, {wtree_sum.second.extract_end(1), State::S0}, Z); // BuildCPA(module, wtree_sum.first, {State::S0, wtree_sum.second.extract_end(1)}, Z);
// else
// printf("Svec size %d c_vec size %d\n", s_vec.size(), c_vec.size());
// module->addAdd(NEW_ID, s_vec,
// {c_vec.extract(0,c_vec.size()-1),State::S0}, Z);
// Brent Kung adder
SigSpec g = module->addWire(NEW_ID, s_vec.size());
SigSpec p = module->addWire(NEW_ID, s_vec.size());
SigSpec co = module->addWire(NEW_ID, s_vec.size());
module->addAnd(NEW_ID, s_vec, {c_vec.extract(0, c_vec.size() - 1), State::S0}, g); // generate
module->addXor(NEW_ID, s_vec, {c_vec.extract(0, c_vec.size() - 1), State::S0}, p); // propagate
auto lcu = module->addCell(NEW_ID, ID($lcu));
auto lcu_int = module->addWire(NEW_ID, s_vec.size());
lcu->setParam(ID::WIDTH, s_vec.size());
lcu->setPort(ID::G, g);
lcu->setPort(ID::P, p);
lcu->setPort(ID::CI, State::S0);
lcu->setPort(ID::CO, lcu_int);
module->addXor(NEW_ID, p, {lcu_int, State::S0}, Z);
} }
/* /*
@ -413,9 +427,8 @@ struct BoothPassWorker {
*/ */
void BuildBoothMultDecoderRow0(RTLIL::Module *module, void BuildBoothMultDecoderRow0(RTLIL::Module *module,
SigSpec X, // multiplicand SigSpec X, // multiplicand
SigSpec s_int, SigSpec sb_int, SigSpec one_int, SigSpec s_int, SigSpec sb_int, SigSpec one_int, SigSpec two_int, SigSpec &ppij_vec, bool is_signed)
SigSpec two_int, SigSpec &ppij_vec, bool is_signed)
{ {
(void)sb_int; (void)sb_int;
(void)module; (void)module;
@ -427,21 +440,19 @@ struct BoothPassWorker {
// 1..xsize -1 // 1..xsize -1
for (int i = 1; i < x_sz; i++) for (int i = 1; i < x_sz; i++)
ppij_vec.append(Bur4d_n(stringf("row0_dec_%d", i), X[i], X[i - 1], ppij_vec.append(Bur4d_n(stringf("row0_dec_%d", i), X[i], X[i - 1], one_int[0], two_int[0], s_int[0]));
one_int[0], two_int[0], s_int[0]));
// The redundant bit. Duplicate decoding of last bit. // The redundant bit. Duplicate decoding of last bit.
if (!is_signed) { if (!is_signed) {
ppij_vec.append(Bur4d_msb("row0_dec_msb", X.msb(), two_int[0], s_int[0])); ppij_vec.append(Bur4d_msb("row0_dec_msb", X.msb(), two_int[0], s_int[0]));
} else { } else {
ppij_vec.append(Bur4d_n("row0_dec_msb", X.msb(), X.msb(), ppij_vec.append(Bur4d_n("row0_dec_msb", X.msb(), X.msb(), one_int[0], two_int[0], s_int[0]));
one_int[0], two_int[0], s_int[0]));
} }
// append the sign bits // append the sign bits
if (is_signed) { if (is_signed) {
SigBit e = module->XorGate(NEW_ID, s_int[0], module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int[0], one_int[0]))); SigBit e =
module->XorGate(NEW_ID, s_int[0], module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int[0], one_int[0])));
ppij_vec.append({module->NotGate(NEW_ID, e), e, e}); ppij_vec.append({module->NotGate(NEW_ID, e), e, e});
} else { } else {
// append the sign bits // append the sign bits
@ -452,10 +463,8 @@ struct BoothPassWorker {
// Build a generic row of decoders. // Build a generic row of decoders.
void BuildBoothMultDecoderRowN(RTLIL::Module *module, void BuildBoothMultDecoderRowN(RTLIL::Module *module,
SigSpec X, // multiplicand SigSpec X, // multiplicand
SigSpec one_int, SigSpec two_int, SigSpec s_int, SigSpec sb_int, SigSpec one_int, SigSpec two_int, SigSpec s_int, SigSpec sb_int, SigSpec &ppij_vec, int row_ix, bool is_signed)
SigSpec &ppij_vec, int row_ix,
bool is_signed)
{ {
(void)module; (void)module;
int x_sz = GetSize(X); int x_sz = GetSize(X);
@ -465,28 +474,124 @@ struct BoothPassWorker {
// core bits // core bits
for (int i = 1; i < x_sz; i++) for (int i = 1; i < x_sz; i++)
ppij_vec.append(Bur4d_n(stringf("row_%d_dec_%d", row_ix, i), X[i], X[i - 1], ppij_vec.append(Bur4d_n(stringf("row_%d_dec_%d", row_ix, i), X[i], X[i - 1], one_int, two_int, s_int));
one_int, two_int, s_int));
if (!is_signed) { // redundant bit if (!is_signed) { // redundant bit
ppij_vec.append(Bur4d_msb("row_dec_red", X[x_sz - 1], two_int, s_int)); ppij_vec.append(Bur4d_msb("row_dec_red", X[x_sz - 1], two_int, s_int));
} else { } else {
ppij_vec.append(Bur4d_n(stringf("row_%d_dec_msb", row_ix), X[x_sz - 1], X[x_sz - 1], ppij_vec.append(Bur4d_n(stringf("row_%d_dec_msb", row_ix), X[x_sz - 1], X[x_sz - 1], one_int, two_int, s_int));
one_int, two_int, s_int));
} }
ppij_vec.append(!is_signed ? sb_int[0] : module->XorGate(NEW_ID, sb_int, module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int, one_int)))); ppij_vec.append(!is_signed
? sb_int[0]
: module->XorGate(NEW_ID, sb_int, module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int, one_int))));
ppij_vec.append(State::S1); ppij_vec.append(State::S1);
} }
void DebugDumpAlignPP(std::vector<std::vector<RTLIL::Wire *>> &aligned_pp) void DumpModule()
{
printf("Cells\n");
for (auto cell : module->cells_) {
printf("+Cell %s type %s\n", cell.first.c_str(), cell.second->type.c_str());
printf("Connections on cell\n");
for (auto c : cell.second->connections()) {
printf("Connection %s\n", c.first.c_str());
DumpSigSpec(1, c.second);
}
printf("-\n");
}
printf("Wires\n");
for (auto w : module->wires_) {
printf("Wire %s (%p) width %d port_id %d dir %s\n", w.first.c_str(), w.second, w.second->width, w.second->port_id,
w.second->port_input ? "input" : "output");
}
printf("Connections\n");
for (auto con : module->connections_) {
SigSpec from = con.first;
SigSpec to = con.second;
printf("\t From connection %s width %d bit lengtht %d", from.is_wire() ? "wire" : "?", from.size(), from.bits().size());
printf("\tTo connection %s ", to.is_wire() ? "wire" : to.is_bit() ? "bit" : to.is_chunk() ? "chunk" : "?");
if (to.is_bit()) {
RTLIL::SigBit sb = to.as_bit();
if (to.as_bit().wire) {
printf("Sig bit wire offset %d dir %s\n", to.as_bit().offset, to.as_bit().wire->port_input ? "ip" : "op");
} else
printf("Sig bit value is state\n");
}
printf("\n");
}
}
std::string Indent(int indent)
{
std::string ret = "";
for (int i = 0; i < indent; i++)
ret = ret + '\t';
return ret;
}
void DumpSigSpec(int ident, RTLIL::SigSpec sp, std::string hdr = "")
{
printf("%s Sig spec %s\n", Indent(ident).c_str(), hdr.c_str());
printf("%s Width %d\n", Indent(ident + 1).c_str(), sp.size());
printf("%s Type: %s\n", Indent(ident + 1).c_str(),
sp.is_wire()
? "wire"
: sp.is_chunk() ? "chunk"
: sp.is_bit() ? "bit"
: sp.is_fully_const()
? "fully const"
: sp.is_fully_def()
? "fully def"
: sp.is_fully_undef() ? "fully undef" : sp.has_marked_bits() ? "marked bits" : "unk");
printf("%s Number of bits %d Number of chunks %d\n", Indent(ident + 1).c_str(), sp.bits().size(), sp.chunks().size());
printf("%s Bits:\n", Indent(ident + 1).c_str());
int count = 0;
// b is SigBit
for (auto b : sp.bits()) {
if (b.wire) {
printf("%s [%d] Bit wire %s (%p)\n", Indent(ident + 1).c_str(), count, b.wire->name.c_str(), b.wire);
} else {
if (b.data == State::S0)
printf("%s Bit constant 0\n", Indent(ident + 1).c_str());
else if (b.data == State::S1)
printf("%s Bit constant 1\n", Indent(ident + 1).c_str());
else
printf("%s Unknown constant\n", Indent(ident + 1).c_str());
}
count++;
}
/*
count=0;
printf("%s Chunks:\n",Indent(ident).c_str());
if (sp.chunks().size() >0){
for (auto sc: sp.chunks()){
printf("%s [%d] Chunk wire %s (%p)width %d offset %d\n",
Indent(ident).c_str(),
count,
sc.wire ? sc.wire -> name.c_str(): "non-wire chunk: empty",
sc.wire,
sc.width,
sc.offset);
count++;
}
}
*/
}
void DebugDumpAlignPP(std::vector<SigSpec> &aligned_pp)
{ {
printf("Aligned & Padded Partial products\n"); printf("Aligned & Padded Partial products\n");
int pp_ix = 0; int pp_ix = 0;
for (auto pp_row : aligned_pp) { for (auto pp_row : aligned_pp) {
printf("PP_%d \t", pp_ix); printf("PP_%d \t", pp_ix);
for (unsigned i = 0; i < pp_row.size(); i++) for (int i = 0; i < pp_row.size(); i++) {
printf("[%d] %s ", i, pp_row[i] == nullptr ? " 0 " : pp_row[i]->name.c_str()); RTLIL::SigSpec sb_el = pp_row.extract(i);
std::string col_ix = "PP_" + std::to_string(pp_ix) + " col" + std::to_string(i);
DumpSigSpec(1, sb_el, col_ix);
}
printf("\n"); printf("\n");
pp_ix++; pp_ix++;
} }
@ -558,8 +663,144 @@ struct BoothPassWorker {
} }
} }
void BuildCSATree(RTLIL::Module *module, std::vector<SigSpec> &bits_to_reduce, SigSpec &s_vec, /*
SigSpec &c_vec, std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees) Decompose the bits for pp[x] 2**n to reduce into groups of 3. Each group
will ultimately feed a csa. Where we have odd bits
we put them in A, B.
*/
void makeCSAGroups(SigSpec bits_to_reduce, std::vector<std::tuple<SigBit, SigBit, SigBit>> &groups, SigBit &A, SigBit &B)
{
int bit_ix = 0;
A = State::S0;
B = State::S0;
while (bit_ix < bits_to_reduce.size()) {
if (bit_ix == (bits_to_reduce.size() - 1)) {
A = bits_to_reduce.extract(bit_ix);
bit_ix++;
} else if (bit_ix == bits_to_reduce.size() - 2) {
A = bits_to_reduce.extract(bit_ix);
B = bits_to_reduce.extract(bit_ix + 1);
bit_ix = bit_ix + 2;
} else {
groups.push_back(std::make_tuple(bits_to_reduce.extract(bit_ix), bits_to_reduce.extract(bit_ix + 1),
bits_to_reduce.extract(bit_ix + 2)));
bit_ix = bit_ix + 3;
}
}
}
void makeCSARow(int row_ix, std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees, RTLIL::Module *module,
std::vector<std::tuple<SigBit, SigBit, SigBit>> &groups, SigBit &A, SigBit &B, SigSpec &carry_bits_to_sum,
SigSpec &next_bits_to_reduce)
{
int col_ix = 0;
std::vector<RTLIL::Cell *> csa_row;
for (auto g : groups) {
std::string csa_name = "csa_" + std::to_string(row_ix) + "_" + std::to_string(col_ix);
auto csa = module->addCell(new_id(csa_name, __LINE__, ""), ID($fa));
csa_row.push_back(csa);
csa->setParam(ID::WIDTH, 1);
col_ix++;
csa->setPort(ID::A, get<0>(g));
csa->setPort(ID::B, get<1>(g));
csa->setPort(ID::C, get<2>(g));
std::string s_name = "s_" + std::to_string(row_ix) + "_" + std::to_string(col_ix);
std::string c_name = "c_" + std::to_string(row_ix) + "_" + std::to_string(col_ix);
SigBit sum = module->addWire(new_id(s_name, __LINE__, ""), 1);
SigBit carry = module->addWire(new_id(c_name, __LINE__, ""), 1);
csa->setPort(ID::Y, sum);
csa->setPort(ID::X, carry);
carry_bits_to_sum.append(carry);
next_bits_to_reduce.append(sum);
#ifdef DEBUG_CSA
printf("CSA Appending carry bit %s\n", carry.is_wire() ? carry.wire->name.c_str() : "unk");
printf("New number of carry bits %d \n", carry_bits_to_sum.size());
DumpSigSpec(1, carry_bits_to_sum);
#endif
}
if (A != State::S0 && B != State::S0) {
SigBit sum;
SigBit carry;
std::string ha_name = "ha_" + std::to_string(row_ix) + "_" + std::to_string(col_ix);
BuildHa(ha_name, A, B, sum, carry);
carry_bits_to_sum.append(carry);
next_bits_to_reduce.append(sum);
#ifdef DEBUG_CSA
printf("HA Appending carry bit %s\n", carry.is_wire() ? carry.wire->name.c_str() : "unk");
printf("New number of carry bits %d \n", carry_bits_to_sum.size());
DumpSigSpec(1, carry_bits_to_sum);
#endif
} else if (A != State::S0) {
next_bits_to_reduce.append(A);
col_ix++;
}
debug_csa_trees.push_back(csa_row);
}
void ReduceBitsParallel(RTLIL::Module *module, int column_ix, SigSpec column_bits, SigBit &s_result, SigBit &c_result,
SigSpec &carry_bits_to_sum, std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees)
{
(void)column_ix;
#ifdef DEBUG_CSA
if (column_bits.size() > 0)
printf("Parallel Column %d reduce bits parallel given %d bits (%s) to reduce\n", column_ix, column_bits.size(),
column_bits.as_string().c_str());
else
printf("Column %d given %d bits\n", column_ix, 0);
#endif
SigSpec next_bits_to_reduce = column_bits;
std::vector<std::tuple<SigBit, SigBit, SigBit>> groups;
SigBit A;
SigBit B;
int row_ix = 0;
while (next_bits_to_reduce.size() > 1) {
#ifdef DEBUG_CSA
printf("Row %d Column %d Reducing %d bits\n", row_ix, column_ix, next_bits_to_reduce.size());
#endif
groups.clear();
makeCSAGroups(next_bits_to_reduce, groups, A, B);
#ifdef DEBUG_CSA
printf("Row %d Got %ld groups to reduce + A %d + B %d\n", row_ix, groups.size(), A == State::S0 ? 0 : 1,
B == State::S0 ? 0 : 1);
#endif
SigSpec csa_row_bits;
int csa_count_before = debug_csa_trees.size();
makeCSARow(row_ix, debug_csa_trees, module, groups, A, B, carry_bits_to_sum, csa_row_bits);
int csa_count_after = debug_csa_trees.size();
#ifdef DEBUG_CSA
printf("Reduced %d bits to %d bits using %d csa components\n", next_bits_to_reduce.size(), csa_row_bits.size(),
csa_count_after - csa_count_before);
#endif
next_bits_to_reduce = csa_row_bits;
row_ix++;
}
s_result = State::S0;
c_result = State::S0;
if (next_bits_to_reduce.size() == 1)
s_result = next_bits_to_reduce.extract(0);
if (carry_bits_to_sum.size() > 0) {
c_result = carry_bits_to_sum.extract(carry_bits_to_sum.size() - 1);
carry_bits_to_sum.remove(carry_bits_to_sum.size() - 1);
}
}
/*
Build a parallel CSA tree
*/
void BuildCSATree(RTLIL::Module *module, std::vector<SigSpec> &bits_to_reduce, SigSpec &s_vec, SigSpec &c_vec,
std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees)
{ {
if (!(bits_to_reduce.size() > 0)) if (!(bits_to_reduce.size() > 0))
@ -569,6 +810,10 @@ struct BoothPassWorker {
int row_size = bits_to_reduce.size(); int row_size = bits_to_reduce.size();
SigSpec carry_bits_to_add_to_next_column; SigSpec carry_bits_to_add_to_next_column;
#ifdef DEBUG_CSA
printf("Reducing %d partial products. Each with %d columns\n", row_size, column_size);
#endif
for (int column_ix = 0; column_ix < column_size; column_ix++) { for (int column_ix = 0; column_ix < column_size; column_ix++) {
// get the bits in this column. // get the bits in this column.
@ -578,8 +823,9 @@ struct BoothPassWorker {
column_bits.append(bits_to_reduce[row_ix][column_ix]); column_bits.append(bits_to_reduce[row_ix][column_ix]);
} }
for (auto c : carry_bits_to_add_to_next_column) { for (auto c : carry_bits_to_add_to_next_column) {
#ifdef DEBUG_CSA #ifdef DEBUG_CSA
printf("\t Propagating column bit %s to column %d from column %d\n", c->name.c_str(), column_ix, column_ix - 1); printf("\t Propagating column bit %s to column %d from column %d\n", c.wire->name.c_str(), column_ix, column_ix - 1);
#endif #endif
column_bits.append(c); column_bits.append(c);
} }
@ -589,25 +835,25 @@ struct BoothPassWorker {
#ifdef DEBUG_CSA #ifdef DEBUG_CSA
printf("Column %d Reducing %d bits\n", column_ix, column_bits.size()); printf("Column %d Reducing %d bits\n", column_ix, column_bits.size());
for (auto b : column_bits) { for (auto b : column_bits) {
printf("\t %s\n", b->name.c_str()); printf("\t %s\n", b.wire ? b.wire->name.c_str()
: b.data == State::S0 ? "Const 0" : (b.data == State::S1 ? "Const 1" : " Unk constant"));
} }
printf("\n"); printf("\n");
#endif #endif
// Build parallel reduction tree
SigBit s, c; SigBit s, c;
#ifdef DEBUG_CSA ReduceBitsParallel(module, column_ix, column_bits, s, c, carry_bits_to_add_to_next_column, debug_csa_trees);
int csa_count_before = debug_csa_trees[column_ix].size();
#endif
ReduceBits(module, column_ix, column_bits, s, c, carry_bits_to_add_to_next_column, debug_csa_trees);
s_vec.append(s); s_vec.append(s);
c_vec.append(c); c_vec.append(c);
#ifdef DEBUG_CSA #ifdef DEBUG_CSA
int csa_count_after = debug_csa_trees[column_ix].size(); printf("#Carry bits to next column: %d (%s)\n", carry_bits_to_add_to_next_column.size(),
carry_bits_to_add_to_next_column.as_string().c_str());
printf("Column %d Created %d csa tree elements\n", column_ix, csa_count_after - csa_count_before); for (auto b : carry_bits_to_add_to_next_column) {
printf("\t %s\n", b.wire ? b.wire->name.c_str()
: b.data == State::S0 ? "Const 0" : (b.data == State::S1 ? "Const 1" : " Unk constant"));
}
printf("\n");
#endif #endif
} }
} }
@ -632,14 +878,12 @@ struct BoothPassWorker {
Pad out rows with zeros and left the opt pass clean them up. Pad out rows with zeros and left the opt pass clean them up.
*/ */
void AlignPP(int z_sz, std::vector<std::tuple<SigSpec, int, SigBit>> &ppij_int, void AlignPP(int z_sz, std::vector<std::tuple<SigSpec, int, SigBit>> &ppij_int, std::vector<SigSpec> &aligned_pp)
std::vector<SigSpec> &aligned_pp)
{ {
unsigned aligned_pp_ix = aligned_pp.size() - 1; unsigned aligned_pp_ix = aligned_pp.size() - 1;
// default is zero for everything (so don't have to think to hard // default is zero for everything (so don't have to think to hard
// about padding). // about padding).
for (unsigned i = 0; i < aligned_pp.size(); i++) { for (unsigned i = 0; i < aligned_pp.size(); i++) {
for (int j = 0; j < z_sz; j++) { for (int j = 0; j < z_sz; j++) {
aligned_pp[i][j] = State::S0; aligned_pp[i][j] = State::S0;
@ -730,12 +974,11 @@ struct BoothPassWorker {
// Make the carry results.. Two extra bits after fa. // Make the carry results.. Two extra bits after fa.
SigBit carry_out = module->addWire(NEW_ID, 1); SigBit carry_out = module->addWire(NEW_ID, 1);
module->addFa(NEW_ID_SUFFIX(stringf("cpa_%d_fa_%d", cpa_id, n)), module->addFa(NEW_ID_SUFFIX(stringf("cpa_%d_fa_%d", cpa_id, n)),
/* A */ s_vec[n], /* A */ s_vec[n],
/* B */ c_vec[n - 1], /* B */ c_vec[n - 1],
/* C */ carry, /* C */ carry,
/* X */ carry_out, /* X */ carry_out,
/* Y */ result[n] /* Y */ result[n]);
);
carry = carry_out; carry = carry_out;
#ifdef DEBUG_CPA #ifdef DEBUG_CPA
@ -758,12 +1001,11 @@ struct BoothPassWorker {
else { else {
SigBit carry_out = module->addWire(NEW_ID_SUFFIX(stringf("cpa_%d_carry_%d", cpa_id, n)), 1); SigBit carry_out = module->addWire(NEW_ID_SUFFIX(stringf("cpa_%d_carry_%d", cpa_id, n)), 1);
module->addFa(NEW_ID_SUFFIX(stringf("cpa_%d_fa_%d", cpa_id, n)), module->addFa(NEW_ID_SUFFIX(stringf("cpa_%d_fa_%d", cpa_id, n)),
/* A */ s_vec[n], /* A */ s_vec[n],
/* B */ c_vec[n - 1], /* B */ c_vec[n - 1],
/* C */ carry, /* C */ carry,
/* X */ carry_out, /* X */ carry_out,
/* Y */ result[n] /* Y */ result[n]);
);
carry = carry_out; carry = carry_out;
#ifdef DEBUG_CPA #ifdef DEBUG_CPA
printf("CPA bit [%d] Cell %s IPs [%s] [%s] [%s]\n", n, fa_cell->name.c_str(), s_vec[n]->name.c_str(), printf("CPA bit [%d] Cell %s IPs [%s] [%s] [%s]\n", n, fa_cell->name.c_str(), s_vec[n]->name.c_str(),
@ -775,12 +1017,20 @@ struct BoothPassWorker {
// Sum the bits in the current column // Sum the bits in the current column
// Pass the carry bits from each csa to the next // Pass the carry bits from each csa to the next
// column for summation. // column for summation. Build serial tree
void ReduceBits(RTLIL::Module *module, int column_ix, SigSpec column_bits, SigBit &s_result, SigBit &c_result, void ReduceBits(RTLIL::Module *module, int column_ix, SigSpec column_bits, SigBit &s_result, SigBit &c_result, SigSpec &carry_bits_to_sum,
SigSpec &carry_bits_to_sum, std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees) std::vector<std::vector<RTLIL::Cell *>> &debug_csa_trees)
{ {
#ifdef DEBUG_CSA
if (column_bits.size() > 0)
printf("Serial reduce Column %d reduce bits serial given %d bits (%s) to reduce\n", column_ix, column_bits.size(),
column_bits.as_string().c_str());
else
printf("Serial reduce given 0 bits to reduce\n");
#endif
int csa_ix = 0; int csa_ix = 0;
int column_size = column_bits.size(); int column_size = column_bits.size();
@ -799,18 +1049,17 @@ struct BoothPassWorker {
auto c_wire = module->addWire(NEW_ID_SUFFIX(stringf("csa_%d_%d_c", column_ix, csa_ix + 1)), 1); auto c_wire = module->addWire(NEW_ID_SUFFIX(stringf("csa_%d_%d_c", column_ix, csa_ix + 1)), 1);
auto csa = module->addFa(NEW_ID_SUFFIX(stringf("csa_%d_%d", column_ix, csa_ix)), auto csa = module->addFa(NEW_ID_SUFFIX(stringf("csa_%d_%d", column_ix, csa_ix)),
/* A */ first_csa_ips[0], /* A */ first_csa_ips[0],
/* B */ first_csa_ips.size() > 1 ? first_csa_ips[1] : State::S0, /* B */ first_csa_ips.size() > 1 ? first_csa_ips[1] : State::S0,
/* C */ first_csa_ips.size() > 2 ? first_csa_ips[2] : State::S0, /* C */ first_csa_ips.size() > 2 ? first_csa_ips[2] : State::S0,
/* X */ c_wire, /* X */ c_wire,
/* Y */ s_wire /* Y */ s_wire);
);
s_result = s_wire; s_result = s_wire;
c_result = c_wire; c_result = c_wire;
debug_csa_trees[column_ix].push_back(csa); // debug_csa_trees[column_ix].push_back(csa);
csa_ix++; csa_ix++;
if (var_ix <= column_bits.size() - 1) if (var_ix <= column_bits.size() - 1)
carry_bits_to_sum.append(c_wire); carry_bits_to_sum.append(c_wire);
@ -831,14 +1080,13 @@ struct BoothPassWorker {
auto s_wire = module->addWire(NEW_ID_SUFFIX(stringf("csa_%d_%d_s", column_ix, csa_ix + 1)), 1); auto s_wire = module->addWire(NEW_ID_SUFFIX(stringf("csa_%d_%d_s", column_ix, csa_ix + 1)), 1);
auto csa = module->addFa(NEW_ID_SUFFIX(stringf("csa_%d_%d", column_ix, csa_ix)), auto csa = module->addFa(NEW_ID_SUFFIX(stringf("csa_%d_%d", column_ix, csa_ix)),
/* A */ s_result, /* A */ s_result,
/* B */ csa_ips[0], /* B */ csa_ips[0],
/* C */ csa_ips.size() > 1 ? csa_ips[1] : State::S0, /* C */ csa_ips.size() > 1 ? csa_ips[1] : State::S0,
/* X */ c_wire, /* X */ c_wire,
/* Y */ s_wire /* Y */ s_wire);
);
debug_csa_trees[column_ix].push_back(csa); // debug_csa_trees[column_ix].push_back(csa);
csa_ix++; csa_ix++;
if (var_ix <= column_bits.size() - 1) if (var_ix <= column_bits.size() - 1)
@ -852,8 +1100,8 @@ struct BoothPassWorker {
} }
} }
void BuildBoothMultEncoders(SigSpec Y, SigSpec &one_int, SigSpec &two_int, void BuildBoothMultEncoders(SigSpec Y, SigSpec &one_int, SigSpec &two_int, SigSpec &s_int, SigSpec &sb_int, RTLIL::Module *module,
SigSpec &s_int, SigSpec &sb_int, RTLIL::Module *module, int &encoder_ix, bool is_signed) int &encoder_ix, bool is_signed)
{ {
int y_sz = GetSize(Y); int y_sz = GetSize(Y);
@ -866,8 +1114,7 @@ struct BoothPassWorker {
sb_int.append(module->addWire(NEW_ID_SUFFIX(stringf("sb_int_%d", encoder_ix)), 1)); sb_int.append(module->addWire(NEW_ID_SUFFIX(stringf("sb_int_%d", encoder_ix)), 1));
if (y_ix == 0) { if (y_ix == 0) {
BuildBur4e(enc_name, State::S0, Y[y_ix], BuildBur4e(enc_name, State::S0, Y[y_ix], Y[y_ix + 1], one_int[encoder_ix], two_int[encoder_ix], s_int[encoder_ix],
Y[y_ix + 1], one_int[encoder_ix], two_int[encoder_ix], s_int[encoder_ix],
sb_int[encoder_ix]); sb_int[encoder_ix]);
y_ix = y_ix + 1; y_ix = y_ix + 1;
@ -926,8 +1173,7 @@ struct BoothPassWorker {
sb_int.append(module->addWire(NEW_ID_SUFFIX(stringf("sb_int_%d", encoder_ix)), 1)); sb_int.append(module->addWire(NEW_ID_SUFFIX(stringf("sb_int_%d", encoder_ix)), 1));
SigBit one_o_int, two_o_int, s_o_int, sb_o_int; SigBit one_o_int, two_o_int, s_o_int, sb_o_int;
BuildBur4e(enc_name, Y[y_ix], State::S0, BuildBur4e(enc_name, Y[y_ix], State::S0, State::S0, one_o_int, two_o_int, s_o_int, sb_o_int);
State::S0, one_o_int, two_o_int, s_o_int, sb_o_int);
module->connect(one_int[encoder_ix], one_o_int); module->connect(one_int[encoder_ix], one_o_int);
module->connect(two_int[encoder_ix], two_o_int); module->connect(two_int[encoder_ix], two_o_int);
@ -973,9 +1219,8 @@ struct BoothPassWorker {
cori_n_int[encoder_ix - 1] = module->addWire(NEW_ID_SUFFIX(stringf("cori_n_int_%d", encoder_ix)), 1); cori_n_int[encoder_ix - 1] = module->addWire(NEW_ID_SUFFIX(stringf("cori_n_int_%d", encoder_ix)), 1);
if (encoder_ix == 1) { if (encoder_ix == 1) {
BuildBr4e(enc_name, State::S0, Y[0], Y[1], BuildBr4e(enc_name, State::S0, Y[0], Y[1], negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1],
negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], cori_n_int[encoder_ix - 1]);
cori_n_int[encoder_ix - 1]);
} else { } else {
SigBit y1, y2, y3; SigBit y1, y2, y3;
@ -992,8 +1237,7 @@ struct BoothPassWorker {
else else
y3 = Y[(encoder_ix - 1) * 2 + 1]; //+1 y3 = Y[(encoder_ix - 1) * 2 + 1]; //+1
BuildBr4e(enc_name, y1, y2, y3, BuildBr4e(enc_name, y1, y2, y3, negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1],
negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1],
cori_n_int[encoder_ix - 1]); cori_n_int[encoder_ix - 1]);
} }
} }
@ -1005,11 +1249,10 @@ struct BoothPassWorker {
for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) { for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) {
for (int decoder_ix = 1; decoder_ix <= dec_count; decoder_ix++) { for (int decoder_ix = 1; decoder_ix <= dec_count; decoder_ix++) {
PPij[((encoder_ix - 1) * dec_count) + decoder_ix - 1] = PPij[((encoder_ix - 1) * dec_count) + decoder_ix - 1] =
module->addWire(NEW_ID_SUFFIX(stringf("ppij_%d_%d", encoder_ix, decoder_ix)), 1); module->addWire(NEW_ID_SUFFIX(stringf("ppij_%d_%d", encoder_ix, decoder_ix)), 1);
nxj[((encoder_ix - 1) * dec_count) + decoder_ix - 1] = nxj[((encoder_ix - 1) * dec_count) + decoder_ix - 1] = module->addWire(
module->addWire(NEW_ID_SUFFIX(stringf("nxj_%s%d_%d", decoder_ix == 1 ? "pre_dec_" : "", NEW_ID_SUFFIX(stringf("nxj_%s%d_%d", decoder_ix == 1 ? "pre_dec_" : "", encoder_ix, decoder_ix)), 1);
encoder_ix, decoder_ix)), 1);
} }
} }
@ -1023,10 +1266,8 @@ struct BoothPassWorker {
if (encoder_ix == 1) { if (encoder_ix == 1) {
// quadrant 1 optimization // quadrant 1 optimization
} else { } else {
module->addNotGate(NEW_ID_SUFFIX(stringf("pre_dec_%d", encoder_ix)), module->addNotGate(NEW_ID_SUFFIX(stringf("pre_dec_%d", encoder_ix)), negi_n_int[encoder_ix - 1],
negi_n_int[encoder_ix - 1], nxj[(encoder_ix - 1) * dec_count]);
nxj[(encoder_ix - 1) * dec_count]
);
} }
for (int decoder_ix = 1; decoder_ix < dec_count; decoder_ix++) { for (int decoder_ix = 1; decoder_ix < dec_count; decoder_ix++) {
@ -1046,9 +1287,9 @@ struct BoothPassWorker {
// applies to 9th decoder (xsz+1 decoder). // applies to 9th decoder (xsz+1 decoder).
std::string dec_name = stringf("dec_%d_%d", encoder_ix, x_sz + 1); std::string dec_name = stringf("dec_%d_%d", encoder_ix, x_sz + 1);
SigBit unused_op; SigBit unused_op;
BuildBr4d(dec_name, nxj[((encoder_ix - 1) * dec_count) + dec_count - 1], twoi_n_int[encoder_ix - 1], BuildBr4d(dec_name, nxj[((encoder_ix - 1) * dec_count) + dec_count - 1], twoi_n_int[encoder_ix - 1], X[dec_count - 2],
X[dec_count - 2], negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], PPij[((encoder_ix - 1) * dec_count) + dec_count - 1],
PPij[((encoder_ix - 1) * dec_count) + dec_count - 1], unused_op); unused_op);
} }
// //
@ -1060,8 +1301,7 @@ struct BoothPassWorker {
BuildBoothQ1("icb_booth_q1_", BuildBoothQ1("icb_booth_q1_",
negi_n_int[0], // negi negi_n_int[0], // negi
cori_n_int[0], // cori cori_n_int[0], // cori
X[0], X[1], Y[0], Y[1], X[0], X[1], Y[0], Y[1], nxj_o_int, q1_carry_out, pp0_o_int, pp1_o_int);
nxj_o_int, q1_carry_out, pp0_o_int, pp1_o_int);
module->connect(Z[0], pp0_o_int); module->connect(Z[0], pp0_o_int);
module->connect(Z[1], pp1_o_int); module->connect(Z[1], pp1_o_int);
@ -1085,29 +1325,27 @@ struct BoothPassWorker {
SigBit d08_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv1"), PPij[(0 * dec_count) + dec_count - 1]); SigBit d08_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv1"), PPij[(0 * dec_count) + dec_count - 1]);
SigBit d18_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv2"), PPij[(1 * dec_count) + dec_count - 1]); SigBit d18_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv2"), PPij[(1 * dec_count) + dec_count - 1]);
BuildBitwiseFa(module, NEW_ID_SUFFIX("fa_row_0").str(), BuildBitwiseFa(module, NEW_ID_SUFFIX("fa_row_0").str(),
/* A */ {State::S0, d08_inv, PPij[(0 * dec_count) + x_sz], PPij.extract((0 * dec_count) + 2, x_sz - 1)}, /* A */ {State::S0, d08_inv, PPij[(0 * dec_count) + x_sz], PPij.extract((0 * dec_count) + 2, x_sz - 1)},
/* B */ {State::S1, d18_inv, PPij.extract((1 * dec_count), x_sz)}, /* B */ {State::S1, d18_inv, PPij.extract((1 * dec_count), x_sz)},
/* C */ fa_carry[0].extract(1, x_sz + 2), /* C */ fa_carry[0].extract(1, x_sz + 2),
/* X */ fa_carry[0].extract(2, x_sz + 2), /* X */ fa_carry[0].extract(2, x_sz + 2),
/* Y */ fa_sum[0].extract(2, x_sz + 2) /* Y */ fa_sum[0].extract(2, x_sz + 2));
);
module->connect(fa_carry[0][1], q1_carry_out); module->connect(fa_carry[0][1], q1_carry_out);
// step case: 2nd and rest of rows. (fa_row_ix == 1...n) // step case: 2nd and rest of rows. (fa_row_ix == 1...n)
// special because these are driven by a decoder and prior fa. // special because these are driven by a decoder and prior fa.
for (fa_row_ix = 1; fa_row_ix < fa_row_count; fa_row_ix++) { for (fa_row_ix = 1; fa_row_ix < fa_row_count; fa_row_ix++) {
// end two bits: sign extension // end two bits: sign extension
SigBit d_inv = module->NotGate(NEW_ID_SUFFIX(stringf("bfa_se_inv_%d_L", fa_row_ix)), SigBit d_inv =
PPij[((fa_row_ix + 1) * dec_count) + dec_count - 1]); module->NotGate(NEW_ID_SUFFIX(stringf("bfa_se_inv_%d_L", fa_row_ix)), PPij[((fa_row_ix + 1) * dec_count) + dec_count - 1]);
BuildBitwiseFa(module, NEW_ID_SUFFIX(stringf("fa_row_%d", fa_row_ix)).str(), BuildBitwiseFa(module, NEW_ID_SUFFIX(stringf("fa_row_%d", fa_row_ix)).str(),
/* A */ {State::S0, fa_carry[fa_row_ix - 1][fa_count - 1], fa_sum[fa_row_ix - 1].extract(2, x_sz + 2)}, /* A */ {State::S0, fa_carry[fa_row_ix - 1][fa_count - 1], fa_sum[fa_row_ix - 1].extract(2, x_sz + 2)},
/* B */ {State::S1, d_inv, PPij.extract((fa_row_ix + 1) * dec_count, x_sz), State::S0, State::S0}, /* B */ {State::S1, d_inv, PPij.extract((fa_row_ix + 1) * dec_count, x_sz), State::S0, State::S0},
/* C */ {fa_carry[fa_row_ix].extract(0, x_sz + 3), cori_n_int[fa_row_ix]}, /* C */ {fa_carry[fa_row_ix].extract(0, x_sz + 3), cori_n_int[fa_row_ix]},
/* X */ fa_carry[fa_row_ix], /* X */ fa_carry[fa_row_ix],
/* Y */ fa_sum[fa_row_ix] /* Y */ fa_sum[fa_row_ix]);
);
} }
// instantiate the cpa // instantiate the cpa
@ -1155,8 +1393,7 @@ struct BoothPass : public Pass {
} }
void execute(vector<string> args, RTLIL::Design *design) override void execute(vector<string> args, RTLIL::Design *design) override
{ {
log_header(design, "Executing BOOTH pass (map to Booth multipliers).\n"); log_header(design, "**Executing BOOTH pass (map to Booth multipliers).\n");
size_t argidx; size_t argidx;
bool mapped_cpa = false; bool mapped_cpa = false;
bool lowpower = false; bool lowpower = false;
@ -1183,8 +1420,7 @@ struct BoothPass : public Pass {
total += worker.booth_counter; total += worker.booth_counter;
} }
} }
log_header(design, "Mapped %d multipliers.\n", total);
log("Mapped %d multipliers.\n", total);
} }
} MultPass; } MultPass;

0
tests/techmap/booth.cc Normal file
View File

24
tests/verific/blackbox.ys Normal file
View File

@ -0,0 +1,24 @@
verific -sv -lib <<EOF
module TEST_CELL(input clk, input a, input b, output reg c);
parameter PATH = "DEFAULT";
always @(posedge clk) begin
if (PATH=="DEFAULT")
c <= a;
else
c <= b;
end
endmodule
EOF
verific -sv <<EOF
module top(input clk, input a, input b, output c, output d);
TEST_CELL #(.PATH("TEST")) test1(.clk(clk),.a(a),.b(1'b1),.c(c));
TEST_CELL #(.PATH("DEFAULT")) test2(.clk(clk),.a(a),.b(1'bx),.c(d));
endmodule
EOF
verific -import top
hierarchy -top top
stat
select -assert-count 2 t:TEST_CELL