mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #786 from YosysHQ/pmgen
Pattern Matcher Generator and iCE40 DSP Mapper
This commit is contained in:
commit
03aa3541ae
|
@ -2410,6 +2410,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
||||||
if (connections_.count("\\Y"))
|
if (connections_.count("\\Y"))
|
||||||
parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
|
parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
|
||||||
|
|
||||||
|
if (connections_.count("\\Q"))
|
||||||
|
parameters["\\WIDTH"] = GetSize(connections_["\\Q"]);
|
||||||
|
|
||||||
check();
|
check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ struct WreduceConfig
|
||||||
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
||||||
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
|
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
|
||||||
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
|
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
|
||||||
"$mux", "$pmux"
|
"$mux", "$pmux",
|
||||||
|
"$dff", "$adff"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -134,6 +135,71 @@ struct WreduceWorker
|
||||||
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
|
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run_cell_dff(Cell *cell)
|
||||||
|
{
|
||||||
|
// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
|
||||||
|
|
||||||
|
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
|
||||||
|
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
|
||||||
|
|
||||||
|
int width_before = GetSize(sig_q);
|
||||||
|
|
||||||
|
if (width_before == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||||
|
bool sign_ext = !zero_ext;
|
||||||
|
|
||||||
|
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (zero_ext && sig_d[i] == State::S0) {
|
||||||
|
module->connect(sig_q[i], State::S0);
|
||||||
|
sig_d.remove(i);
|
||||||
|
sig_q.remove(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
|
||||||
|
module->connect(sig_q[i], sig_q[i-1]);
|
||||||
|
sig_d.remove(i);
|
||||||
|
sig_q.remove(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto info = mi.query(sig_q[i]);
|
||||||
|
if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||||
|
sig_d.remove(i);
|
||||||
|
sig_q.remove(i);
|
||||||
|
zero_ext = false;
|
||||||
|
sign_ext = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width_before == GetSize(sig_q))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GetSize(sig_q) == 0) {
|
||||||
|
log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||||
|
log_id(module), log_id(cell), log_id(cell->type));
|
||||||
|
|
||||||
|
for (auto bit : sig_d)
|
||||||
|
work_queue_bits.insert(bit);
|
||||||
|
|
||||||
|
for (auto bit : sig_q)
|
||||||
|
work_queue_bits.insert(bit);
|
||||||
|
|
||||||
|
cell->setPort("\\D", sig_d);
|
||||||
|
cell->setPort("\\Q", sig_q);
|
||||||
|
cell->fixup_parameters();
|
||||||
|
}
|
||||||
|
|
||||||
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
|
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
|
||||||
{
|
{
|
||||||
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
|
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
|
||||||
|
@ -176,6 +242,9 @@ struct WreduceWorker
|
||||||
if (cell->type.in("$mux", "$pmux"))
|
if (cell->type.in("$mux", "$pmux"))
|
||||||
return run_cell_mux(cell);
|
return run_cell_mux(cell);
|
||||||
|
|
||||||
|
if (cell->type.in("$dff", "$adff"))
|
||||||
|
return run_cell_dff(cell);
|
||||||
|
|
||||||
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
|
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
|
||||||
|
|
||||||
if (sig.has_const())
|
if (sig.has_const())
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/ice40_dsp_pm.h
|
|
@ -0,0 +1,8 @@
|
||||||
|
OBJS += passes/pmgen/ice40_dsp.o
|
||||||
|
|
||||||
|
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
||||||
|
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
|
||||||
|
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
|
||||||
|
|
||||||
|
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
|
||||||
|
$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
|
|
@ -0,0 +1,224 @@
|
||||||
|
Pattern Matcher Generator
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
|
||||||
|
writes a header-only C++ library that implements that pattern matcher.
|
||||||
|
|
||||||
|
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
|
||||||
|
|
||||||
|
The algorithm used in the generated pattern matcher is a simple recursive
|
||||||
|
search with backtracking. It is left to the author of the `.pmg` file to
|
||||||
|
determine an efficient cell order for the search that allows for maximum
|
||||||
|
use of indices and early backtracking.
|
||||||
|
|
||||||
|
|
||||||
|
API of Generated Matcher
|
||||||
|
========================
|
||||||
|
|
||||||
|
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
|
||||||
|
a class `foobar_pm`. That class is instanciated with an RTLIL module and a
|
||||||
|
list of cells from that module:
|
||||||
|
|
||||||
|
foobar_pm pm(module, module->selected_cells());
|
||||||
|
|
||||||
|
The caller must make sure that none of the cells in the 2nd argument are
|
||||||
|
deleted for as long as the patter matcher instance is used.
|
||||||
|
|
||||||
|
At any time it is possible to disable cells, preventing them from showing
|
||||||
|
up in any future matches:
|
||||||
|
|
||||||
|
pm.blacklist(some_cell);
|
||||||
|
|
||||||
|
The `.run(callback_function)` method searches for all matches and calls the
|
||||||
|
callback function for each found match:
|
||||||
|
|
||||||
|
pm.run([&](){
|
||||||
|
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
|
||||||
|
log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
|
||||||
|
});
|
||||||
|
|
||||||
|
The `.pmg` file declares matcher state variables that are accessible via the
|
||||||
|
`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
|
||||||
|
|
||||||
|
Similarly the `.pmg` file declares user data variables that become members of
|
||||||
|
`.ud`, a struct of type `foobar_pm::udata_t`.
|
||||||
|
|
||||||
|
|
||||||
|
The .pmg File Format
|
||||||
|
====================
|
||||||
|
|
||||||
|
The `.pmg` file format is a simple line-based file format. For the most part
|
||||||
|
lines consist of whitespace-separated tokens.
|
||||||
|
|
||||||
|
Lines in `.pmg` files starting with `//` are comments.
|
||||||
|
|
||||||
|
Declaring state variables
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
One or more state variables can be declared using the `state` statement,
|
||||||
|
followed by a C++ type in angle brackets, followed by a whitespace separated
|
||||||
|
list of variable names. For example:
|
||||||
|
|
||||||
|
state <bool> flag1 flag2 happy big
|
||||||
|
state <SigSpec> sigA sigB sigY
|
||||||
|
|
||||||
|
State variables are automatically managed by the generated backtracking algorithm
|
||||||
|
and saved and restored as needed.
|
||||||
|
|
||||||
|
They are automatically initialized to the default constructed value of their type
|
||||||
|
when `.run(callback_function)` is called.
|
||||||
|
|
||||||
|
Declaring udata variables
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Udata (user-data) variables can be used for example to configure the matcher or
|
||||||
|
the callback function used to perform actions on found matches.
|
||||||
|
|
||||||
|
There is no automatic management of udata variables. For this reason it is
|
||||||
|
recommended that the user-supplied matcher code treats them as read-only
|
||||||
|
variables.
|
||||||
|
|
||||||
|
They are declared like state variables, just using the `udata` statement:
|
||||||
|
|
||||||
|
udata <int> min_data_width max_data_width
|
||||||
|
udata <IdString> data_port_name
|
||||||
|
|
||||||
|
They are atomatically initialzed to the default constructed value of their type
|
||||||
|
when ther pattern matcher object is constructed.
|
||||||
|
|
||||||
|
Embedded C++ code
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Many statements in a `.pmg` file contain C++ code. However, there are some
|
||||||
|
slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
|
||||||
|
write matchers:
|
||||||
|
|
||||||
|
- Identifiers starting with a dollar sign or backslash are automatically
|
||||||
|
converted to special IdString variables that are initialized when the
|
||||||
|
matcher object is constructed.
|
||||||
|
|
||||||
|
- The `port(<cell>, <portname>)` function is a handy alias for
|
||||||
|
`sigmap(<cell>->getPort(<portname>))`.
|
||||||
|
|
||||||
|
- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
|
||||||
|
|
||||||
|
- The function `nusers(<sigspec>)` returns the number of different cells
|
||||||
|
connected to any of the given signal bits, plus one if any of the signal
|
||||||
|
bits is also a primary input or primary output.
|
||||||
|
|
||||||
|
- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
|
||||||
|
statements.
|
||||||
|
|
||||||
|
- In `index` statements there is a special `===` operator for the index
|
||||||
|
lookup.
|
||||||
|
|
||||||
|
Matching cells
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Cells are matched using `match..endmatch` blocks. For example:
|
||||||
|
|
||||||
|
match mul
|
||||||
|
if ff
|
||||||
|
select mul->type == $mul
|
||||||
|
select nusers(port(mul, \Y) == 2
|
||||||
|
index <SigSpec> port(mul, \Y) === port(ff, \D)
|
||||||
|
filter some_weird_function(mul) < other_weird_function(ff)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
A `match` block starts with `match <statevar>` and implicitly generates
|
||||||
|
a state variable `<statevar>` of type `RTLIL::Cell*`.
|
||||||
|
|
||||||
|
All statements in the match block are optional. (An empty match block
|
||||||
|
would simply match each and every cell in the module.)
|
||||||
|
|
||||||
|
The `if <expression>` statement makes the match block conditional. If
|
||||||
|
`<expression>` evaluates to `false` then the match block will be ignored
|
||||||
|
and the corresponding state variable is set to `nullptr`. In our example
|
||||||
|
we only try to match the `mul` cell if the `ff` state variable points
|
||||||
|
to a cell. (Presumably `ff` is provided by a prior `match` block.)
|
||||||
|
|
||||||
|
The `select` lines are evaluated once for each cell when the matcher is
|
||||||
|
initialized. A `match` block will only consider cells for which all `select`
|
||||||
|
expressions evaluated to `true`. Note that the state variable corresponding to
|
||||||
|
the match (in the example `mul`) is the only state variable that may be used
|
||||||
|
`select` lines.
|
||||||
|
|
||||||
|
Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
|
||||||
|
evaluated during matcher initialization and the same restrictions apply as for
|
||||||
|
`select` expressions. `expr2` is evaluated when the match is calulated. It is a
|
||||||
|
function of any state variables assigned to by previous blocks. Both expression
|
||||||
|
are converted to the given type and compared for equality. Only cells for which
|
||||||
|
all `index` statements in the block pass are considered by the match.
|
||||||
|
|
||||||
|
Note that `select` and `index` are fast operations. Thus `select` and `index`
|
||||||
|
should be used whenever possible to create efficient matchers.
|
||||||
|
|
||||||
|
Finally, `filter <expression>` narrows down the remaining list of cells. For
|
||||||
|
performance reasons `filter` statements should only be used for things that
|
||||||
|
can't be done using `select` and `index`.
|
||||||
|
|
||||||
|
The `optional` statement marks optional matches. I.e. the matcher will also
|
||||||
|
explore the case where `mul` is set to `nullptr`. Without the `optional`
|
||||||
|
statement a match may only be assigned nullptr when one of the `if` expressions
|
||||||
|
evaluates to `false`.
|
||||||
|
|
||||||
|
Additional code
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
|
||||||
|
Such a block starts with the keyword `code` followed by a list of state variables
|
||||||
|
that the block may modify. For example:
|
||||||
|
|
||||||
|
code addAB sigS
|
||||||
|
if (addA) {
|
||||||
|
addAB = addA;
|
||||||
|
sigS = port(addA, \B);
|
||||||
|
}
|
||||||
|
if (addB) {
|
||||||
|
addAB = addB;
|
||||||
|
sigS = port(addB, \A);
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
The special keyword `reject` can be used to reject the current state and
|
||||||
|
backtrack. For example:
|
||||||
|
|
||||||
|
code
|
||||||
|
if (ffA && ffB) {
|
||||||
|
if (port(ffA, \CLK) != port(ffB, \CLK))
|
||||||
|
reject;
|
||||||
|
if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
Similarly, the special keyword `accept` can be used to accept the current
|
||||||
|
state. (`accept` will not backtrack. This means it continues with the current
|
||||||
|
branch and may accept a larger match later.)
|
||||||
|
|
||||||
|
The special keyword `branch` can be used to explore different cases. Note that
|
||||||
|
each code block has an implicit `branch` at the end. So most use-cases of the
|
||||||
|
`branch` keyword need to end the block with `reject` to avoid the implicit
|
||||||
|
branch at the end. For example:
|
||||||
|
|
||||||
|
state <int> mode
|
||||||
|
|
||||||
|
code mode
|
||||||
|
for (mode = 0; mode < 8; mode++)
|
||||||
|
branch;
|
||||||
|
reject;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
But in some cases it is more natural to utilize the implicit branch statement:
|
||||||
|
|
||||||
|
state <IdString> portAB
|
||||||
|
|
||||||
|
code portAB
|
||||||
|
portAB = \A;
|
||||||
|
branch;
|
||||||
|
portAB = \B;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
There is an implicit `code..endcode` block at the end of each `.pgm` file
|
||||||
|
that just accepts everything that gets all the way there.
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
log("\n");
|
||||||
|
log("ffA: %s\n", log_id(pm.st.ffA, "--"));
|
||||||
|
log("ffB: %s\n", log_id(pm.st.ffB, "--"));
|
||||||
|
log("mul: %s\n", log_id(pm.st.mul, "--"));
|
||||||
|
log("ffY: %s\n", log_id(pm.st.ffY, "--"));
|
||||||
|
log("addAB: %s\n", log_id(pm.st.addAB, "--"));
|
||||||
|
log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
|
||||||
|
log("ffS: %s\n", log_id(pm.st.ffS, "--"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
|
||||||
|
|
||||||
|
if (GetSize(pm.st.sigA) > 16) {
|
||||||
|
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSize(pm.st.sigB) > 16) {
|
||||||
|
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSize(pm.st.sigS) > 32) {
|
||||||
|
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSize(pm.st.sigY) > 32) {
|
||||||
|
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
|
||||||
|
|
||||||
|
if (mul_signed) {
|
||||||
|
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(" replacing $mul with SB_MAC16 cell.\n");
|
||||||
|
|
||||||
|
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
||||||
|
pm.module->swap_names(cell, pm.st.mul);
|
||||||
|
|
||||||
|
// SB_MAC16 Input Interface
|
||||||
|
|
||||||
|
SigSpec A = pm.st.sigA;
|
||||||
|
A.extend_u0(16, mul_signed);
|
||||||
|
|
||||||
|
SigSpec B = pm.st.sigB;
|
||||||
|
B.extend_u0(16, mul_signed);
|
||||||
|
|
||||||
|
SigSpec CD;
|
||||||
|
if (pm.st.muxA)
|
||||||
|
CD = pm.st.muxA->getPort("\\B");
|
||||||
|
if (pm.st.muxB)
|
||||||
|
CD = pm.st.muxB->getPort("\\A");
|
||||||
|
CD.extend_u0(32, mul_signed);
|
||||||
|
|
||||||
|
cell->setPort("\\A", A);
|
||||||
|
cell->setPort("\\B", B);
|
||||||
|
cell->setPort("\\C", CD.extract(0, 16));
|
||||||
|
cell->setPort("\\D", CD.extract(16, 16));
|
||||||
|
|
||||||
|
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
|
||||||
|
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
|
||||||
|
|
||||||
|
cell->setPort("\\AHOLD", State::S0);
|
||||||
|
cell->setPort("\\BHOLD", State::S0);
|
||||||
|
cell->setPort("\\CHOLD", State::S0);
|
||||||
|
cell->setPort("\\DHOLD", State::S0);
|
||||||
|
|
||||||
|
cell->setPort("\\IRSTTOP", State::S0);
|
||||||
|
cell->setPort("\\IRSTBOT", State::S0);
|
||||||
|
|
||||||
|
if (pm.st.clock_vld)
|
||||||
|
{
|
||||||
|
cell->setPort("\\CLK", pm.st.clock);
|
||||||
|
cell->setPort("\\CE", State::S1);
|
||||||
|
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
|
||||||
|
|
||||||
|
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
|
||||||
|
|
||||||
|
if (pm.st.ffA)
|
||||||
|
log(" ffA:%s", log_id(pm.st.ffA));
|
||||||
|
|
||||||
|
if (pm.st.ffB)
|
||||||
|
log(" ffB:%s", log_id(pm.st.ffB));
|
||||||
|
|
||||||
|
if (pm.st.ffY)
|
||||||
|
log(" ffY:%s", log_id(pm.st.ffY));
|
||||||
|
|
||||||
|
if (pm.st.ffS)
|
||||||
|
log(" ffS:%s", log_id(pm.st.ffS));
|
||||||
|
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell->setPort("\\CLK", State::S0);
|
||||||
|
cell->setPort("\\CE", State::S0);
|
||||||
|
cell->setParam("\\NEG_TRIGGER", State::S0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SB_MAC16 Cascade Interface
|
||||||
|
|
||||||
|
cell->setPort("\\SIGNEXTIN", State::Sx);
|
||||||
|
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
||||||
|
|
||||||
|
cell->setPort("\\CI", State::Sx);
|
||||||
|
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
||||||
|
|
||||||
|
cell->setPort("\\ACCUMCI", State::Sx);
|
||||||
|
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
||||||
|
|
||||||
|
// SB_MAC16 Output Interface
|
||||||
|
|
||||||
|
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
|
||||||
|
if (GetSize(O) < 32)
|
||||||
|
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||||
|
|
||||||
|
cell->setPort("\\O", O);
|
||||||
|
|
||||||
|
if (pm.st.addAB) {
|
||||||
|
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
|
||||||
|
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||||
|
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||||
|
} else {
|
||||||
|
cell->setPort("\\ADDSUBTOP", State::S0);
|
||||||
|
cell->setPort("\\ADDSUBBOT", State::S0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell->setPort("\\ORSTTOP", State::S0);
|
||||||
|
cell->setPort("\\ORSTBOT", State::S0);
|
||||||
|
|
||||||
|
cell->setPort("\\OHOLDTOP", State::S0);
|
||||||
|
cell->setPort("\\OHOLDBOT", State::S0);
|
||||||
|
|
||||||
|
SigSpec acc_reset = State::S0;
|
||||||
|
if (pm.st.muxA)
|
||||||
|
acc_reset = pm.st.muxA->getPort("\\S");
|
||||||
|
if (pm.st.muxB)
|
||||||
|
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
|
||||||
|
|
||||||
|
cell->setPort("\\OLOADTOP", acc_reset);
|
||||||
|
cell->setPort("\\OLOADBOT", acc_reset);
|
||||||
|
|
||||||
|
// SB_MAC16 Remaining Parameters
|
||||||
|
|
||||||
|
cell->setParam("\\C_REG", State::S0);
|
||||||
|
cell->setParam("\\D_REG", State::S0);
|
||||||
|
|
||||||
|
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||||
|
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||||
|
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
|
||||||
|
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
||||||
|
|
||||||
|
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||||
|
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
||||||
|
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
||||||
|
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
||||||
|
|
||||||
|
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||||
|
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
||||||
|
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
||||||
|
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
||||||
|
|
||||||
|
cell->setParam("\\MODE_8x8", State::S0);
|
||||||
|
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||||
|
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||||
|
|
||||||
|
pm.autoremove(pm.st.mul);
|
||||||
|
pm.autoremove(pm.st.ffY);
|
||||||
|
pm.autoremove(pm.st.ffS);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ice40DspPass : public Pass {
|
||||||
|
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" ice40_dsp [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
|
{
|
||||||
|
// if (args[argidx] == "-singleton") {
|
||||||
|
// singleton_mode = true;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
for (auto module : design->selected_modules())
|
||||||
|
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
|
||||||
|
}
|
||||||
|
} Ice40DspPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,160 @@
|
||||||
|
state <SigBit> clock
|
||||||
|
state <bool> clock_pol clock_vld
|
||||||
|
state <SigSpec> sigA sigB sigY sigS
|
||||||
|
state <Cell*> addAB muxAB
|
||||||
|
|
||||||
|
match mul
|
||||||
|
select mul->type.in($mul)
|
||||||
|
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||||
|
select GetSize(mul->getPort(\Y)) > 10
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match ffA
|
||||||
|
select ffA->type.in($dff)
|
||||||
|
// select nusers(port(ffA, \Q)) == 2
|
||||||
|
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigA clock clock_pol clock_vld
|
||||||
|
sigA = port(mul, \A);
|
||||||
|
|
||||||
|
if (ffA) {
|
||||||
|
sigA = port(ffA, \D);
|
||||||
|
|
||||||
|
clock = port(ffA, \CLK).as_bit();
|
||||||
|
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
||||||
|
clock_vld = true;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffB
|
||||||
|
select ffB->type.in($dff)
|
||||||
|
// select nusers(port(ffB, \Q)) == 2
|
||||||
|
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigB clock clock_pol clock_vld
|
||||||
|
sigB = port(mul, \B);
|
||||||
|
|
||||||
|
if (ffB) {
|
||||||
|
sigB = port(ffB, \D);
|
||||||
|
SigBit c = port(ffB, \CLK).as_bit();
|
||||||
|
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
||||||
|
|
||||||
|
if (clock_vld && (c != clock || cp != clock_pol))
|
||||||
|
reject;
|
||||||
|
|
||||||
|
clock = c;
|
||||||
|
clock_pol = cp;
|
||||||
|
clock_vld = true;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffY
|
||||||
|
select ffY->type.in($dff)
|
||||||
|
select nusers(port(ffY, \D)) == 2
|
||||||
|
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigY clock clock_pol clock_vld
|
||||||
|
sigY = port(mul, \Y);
|
||||||
|
|
||||||
|
if (ffY) {
|
||||||
|
sigY = port(ffY, \Q);
|
||||||
|
SigBit c = port(ffY, \CLK).as_bit();
|
||||||
|
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
||||||
|
|
||||||
|
if (clock_vld && (c != clock || cp != clock_pol))
|
||||||
|
reject;
|
||||||
|
|
||||||
|
clock = c;
|
||||||
|
clock_pol = cp;
|
||||||
|
clock_vld = true;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match addA
|
||||||
|
select addA->type.in($add)
|
||||||
|
select nusers(port(addA, \A)) == 2
|
||||||
|
index <SigSpec> port(addA, \A) === sigY
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match addB
|
||||||
|
if !addA
|
||||||
|
select addB->type.in($add, $sub)
|
||||||
|
select nusers(port(addB, \B)) == 2
|
||||||
|
index <SigSpec> port(addB, \B) === sigY
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code addAB sigS
|
||||||
|
if (addA) {
|
||||||
|
addAB = addA;
|
||||||
|
sigS = port(addA, \B);
|
||||||
|
}
|
||||||
|
if (addB) {
|
||||||
|
addAB = addB;
|
||||||
|
sigS = port(addB, \A);
|
||||||
|
}
|
||||||
|
if (addAB) {
|
||||||
|
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||||
|
int actual_mul_width = GetSize(sigY);
|
||||||
|
int actual_acc_width = GetSize(sigS);
|
||||||
|
|
||||||
|
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||||
|
reject;
|
||||||
|
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match muxA
|
||||||
|
if addAB
|
||||||
|
select muxA->type.in($mux)
|
||||||
|
select nusers(port(muxA, \A)) == 2
|
||||||
|
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match muxB
|
||||||
|
if addAB
|
||||||
|
if !muxA
|
||||||
|
select muxB->type.in($mux)
|
||||||
|
select nusers(port(muxB, \B)) == 2
|
||||||
|
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code muxAB
|
||||||
|
muxAB = addAB;
|
||||||
|
if (muxA)
|
||||||
|
muxAB = muxA;
|
||||||
|
if (muxB)
|
||||||
|
muxAB = muxB;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffS
|
||||||
|
if muxAB
|
||||||
|
select ffS->type.in($dff)
|
||||||
|
select nusers(port(ffS, \D)) == 2
|
||||||
|
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
||||||
|
index <SigSpec> port(ffS, \Q) === sigS
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code clock clock_pol clock_vld
|
||||||
|
if (ffS) {
|
||||||
|
SigBit c = port(ffS, \CLK).as_bit();
|
||||||
|
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
||||||
|
|
||||||
|
if (clock_vld && (c != clock || cp != clock_pol))
|
||||||
|
reject;
|
||||||
|
|
||||||
|
clock = c;
|
||||||
|
clock_pol = cp;
|
||||||
|
clock_vld = true;
|
||||||
|
}
|
||||||
|
endcode
|
|
@ -0,0 +1,482 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
pp = pprint.PrettyPrinter(indent=4)
|
||||||
|
|
||||||
|
prefix = sys.argv[1]
|
||||||
|
|
||||||
|
state_types = dict()
|
||||||
|
udata_types = dict()
|
||||||
|
blocks = list()
|
||||||
|
ids = dict()
|
||||||
|
|
||||||
|
def rewrite_cpp(s):
|
||||||
|
t = list()
|
||||||
|
i = 0
|
||||||
|
while i < len(s):
|
||||||
|
if s[i] in ("'", '"') and i + 1 < len(s):
|
||||||
|
j = i + 1
|
||||||
|
while j + 1 < len(s) and s[j] != s[i]:
|
||||||
|
if s[j] == '\\' and j + 1 < len(s):
|
||||||
|
j += 1
|
||||||
|
j += 1
|
||||||
|
t.append(s[i:j+1])
|
||||||
|
i = j + 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if s[i] in ('$', '\\') and i + 1 < len(s):
|
||||||
|
j = i + 1
|
||||||
|
while True:
|
||||||
|
if j == len(s):
|
||||||
|
j -= 1
|
||||||
|
break
|
||||||
|
if ord('a') <= ord(s[j]) <= ord('z'):
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
if ord('A') <= ord(s[j]) <= ord('Z'):
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
if ord('0') <= ord(s[j]) <= ord('9'):
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
if s[j] == '_':
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
j -= 1
|
||||||
|
break
|
||||||
|
|
||||||
|
n = s[i:j+1]
|
||||||
|
i = j + 1
|
||||||
|
|
||||||
|
if n[0] == '$':
|
||||||
|
v = "id_d_" + n[1:]
|
||||||
|
else:
|
||||||
|
v = "id_b_" + n[1:]
|
||||||
|
|
||||||
|
if v not in ids:
|
||||||
|
ids[v] = n
|
||||||
|
else:
|
||||||
|
assert ids[v] == n
|
||||||
|
|
||||||
|
t.append(v)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if s[i] == "\t":
|
||||||
|
t.append(" ")
|
||||||
|
else:
|
||||||
|
t.append(s[i])
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return "".join(t)
|
||||||
|
|
||||||
|
with open("%s.pmg" % prefix, "r") as f:
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
if line == "": break
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
cmd = line.split()
|
||||||
|
if len(cmd) == 0 or cmd[0].startswith("//"): continue
|
||||||
|
cmd = cmd[0]
|
||||||
|
|
||||||
|
if cmd == "state":
|
||||||
|
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||||
|
assert m
|
||||||
|
type_str = m.group(1)
|
||||||
|
states_str = m.group(2)
|
||||||
|
for s in re.split(r"\s+", states_str):
|
||||||
|
assert s not in state_types
|
||||||
|
state_types[s] = type_str
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cmd == "udata":
|
||||||
|
m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||||
|
assert m
|
||||||
|
type_str = m.group(1)
|
||||||
|
udatas_str = m.group(2)
|
||||||
|
for s in re.split(r"\s+", udatas_str):
|
||||||
|
assert s not in udata_types
|
||||||
|
udata_types[s] = type_str
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cmd == "match":
|
||||||
|
block = dict()
|
||||||
|
block["type"] = "match"
|
||||||
|
|
||||||
|
line = line.split()
|
||||||
|
assert len(line) == 2
|
||||||
|
assert line[1] not in state_types
|
||||||
|
block["cell"] = line[1]
|
||||||
|
state_types[line[1]] = "Cell*";
|
||||||
|
|
||||||
|
block["if"] = list()
|
||||||
|
block["select"] = list()
|
||||||
|
block["index"] = list()
|
||||||
|
block["filter"] = list()
|
||||||
|
block["optional"] = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
assert l != ""
|
||||||
|
a = l.split()
|
||||||
|
if len(a) == 0 or a[0].startswith("//"): continue
|
||||||
|
if a[0] == "endmatch": break
|
||||||
|
|
||||||
|
if a[0] == "if":
|
||||||
|
b = l.lstrip()[2:]
|
||||||
|
block["if"].append(rewrite_cpp(b.strip()))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if a[0] == "select":
|
||||||
|
b = l.lstrip()[6:]
|
||||||
|
block["select"].append(rewrite_cpp(b.strip()))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if a[0] == "index":
|
||||||
|
m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
|
||||||
|
assert m
|
||||||
|
block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if a[0] == "filter":
|
||||||
|
b = l.lstrip()[6:]
|
||||||
|
block["filter"].append(rewrite_cpp(b.strip()))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if a[0] == "optional":
|
||||||
|
block["optional"] = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert False
|
||||||
|
|
||||||
|
blocks.append(block)
|
||||||
|
|
||||||
|
if cmd == "code":
|
||||||
|
block = dict()
|
||||||
|
block["type"] = "code"
|
||||||
|
block["code"] = list()
|
||||||
|
block["states"] = set()
|
||||||
|
|
||||||
|
for s in line.split()[1:]:
|
||||||
|
assert s in state_types
|
||||||
|
block["states"].add(s)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
assert l != ""
|
||||||
|
a = l.split()
|
||||||
|
if len(a) == 0: continue
|
||||||
|
if a[0] == "endcode": break
|
||||||
|
|
||||||
|
block["code"].append(rewrite_cpp(l.rstrip()))
|
||||||
|
|
||||||
|
blocks.append(block)
|
||||||
|
|
||||||
|
with open("%s_pm.h" % prefix, "w") as f:
|
||||||
|
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print("#include \"kernel/yosys.h\"", file=f)
|
||||||
|
print("#include \"kernel/sigtools.h\"", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print("YOSYS_NAMESPACE_BEGIN", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print("struct {}_pm {{".format(prefix), file=f)
|
||||||
|
print(" Module *module;", file=f)
|
||||||
|
print(" SigMap sigmap;", file=f)
|
||||||
|
print(" std::function<void()> on_accept;".format(prefix), file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
for index in range(len(blocks)):
|
||||||
|
block = blocks[index]
|
||||||
|
if block["type"] == "match":
|
||||||
|
index_types = list()
|
||||||
|
for entry in block["index"]:
|
||||||
|
index_types.append(entry[0])
|
||||||
|
print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
|
||||||
|
print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
|
||||||
|
print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
|
||||||
|
print(" pool<Cell*> blacklist_cells;", file=f)
|
||||||
|
print(" pool<Cell*> autoremove_cells;", file=f)
|
||||||
|
print(" bool blacklist_dirty;", file=f)
|
||||||
|
print(" int rollback;", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" struct state_t {", file=f)
|
||||||
|
for s, t in sorted(state_types.items()):
|
||||||
|
print(" {} {};".format(t, s), file=f)
|
||||||
|
print(" } st;", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" struct udata_t {", file=f)
|
||||||
|
for s, t in sorted(udata_types.items()):
|
||||||
|
print(" {} {};".format(t, s), file=f)
|
||||||
|
print(" } ud;", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
for v, n in sorted(ids.items()):
|
||||||
|
if n[0] == "\\":
|
||||||
|
print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
|
||||||
|
else:
|
||||||
|
print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
|
||||||
|
print(" for (auto bit : sigmap(sig)) {", file=f)
|
||||||
|
print(" if (bit.wire == nullptr) continue;", file=f)
|
||||||
|
print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
|
||||||
|
print(" sigusers[bit].insert(nullptr);", file=f)
|
||||||
|
print(" sigusers[bit].insert(cell);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void blacklist(Cell *cell) {", file=f)
|
||||||
|
print(" if (cell != nullptr) {", file=f)
|
||||||
|
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||||
|
print(" blacklist_dirty = true;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void autoremove(Cell *cell) {", file=f)
|
||||||
|
print(" if (cell != nullptr) {", file=f)
|
||||||
|
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||||
|
print(" blacklist_dirty = true;", file=f)
|
||||||
|
print(" autoremove_cells.insert(cell);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void check_blacklist() {", file=f)
|
||||||
|
print(" if (!blacklist_dirty)", file=f)
|
||||||
|
print(" return;", file=f)
|
||||||
|
print(" blacklist_dirty = false;", file=f)
|
||||||
|
for index in range(len(blocks)):
|
||||||
|
block = blocks[index]
|
||||||
|
if block["type"] == "match":
|
||||||
|
print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
|
||||||
|
print(" rollback = {};".format(index+1), file=f)
|
||||||
|
print(" return;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" rollback = 0;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
|
||||||
|
print(" return sigmap(cell->getPort(portname));", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
||||||
|
print(" return cell->getParam(paramname);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" int nusers(const SigSpec &sig) {", file=f)
|
||||||
|
print(" pool<Cell*> users;", file=f)
|
||||||
|
print(" for (auto bit : sigmap(sig))", file=f)
|
||||||
|
print(" for (auto user : sigusers[bit])", file=f)
|
||||||
|
print(" users.insert(user);", file=f)
|
||||||
|
print(" return GetSize(users);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
|
||||||
|
print(" module(module), sigmap(module) {", file=f)
|
||||||
|
for s, t in sorted(udata_types.items()):
|
||||||
|
if t.endswith("*"):
|
||||||
|
print(" ud.{} = nullptr;".format(s), file=f)
|
||||||
|
else:
|
||||||
|
print(" ud.{} = {}();".format(s, t), file=f)
|
||||||
|
print(" for (auto cell : module->cells()) {", file=f)
|
||||||
|
print(" for (auto &conn : cell->connections())", file=f)
|
||||||
|
print(" add_siguser(conn.second, cell);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" for (auto cell : cells) {", file=f)
|
||||||
|
|
||||||
|
for index in range(len(blocks)):
|
||||||
|
block = blocks[index]
|
||||||
|
if block["type"] == "match":
|
||||||
|
print(" do {", file=f)
|
||||||
|
print(" Cell *{} = cell;".format(block["cell"]), file=f)
|
||||||
|
for expr in block["select"]:
|
||||||
|
print(" if (!({})) break;".format(expr), file=f)
|
||||||
|
print(" index_{}_key_type key;".format(index), file=f)
|
||||||
|
for field, entry in enumerate(block["index"]):
|
||||||
|
print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
|
||||||
|
print(" index_{}[key].push_back(cell);".format(index), file=f)
|
||||||
|
print(" } while (0);", file=f)
|
||||||
|
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" ~{}_pm() {{".format(prefix), file=f)
|
||||||
|
print(" for (auto cell : autoremove_cells)", file=f)
|
||||||
|
print(" module->remove(cell);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void run(std::function<void()> on_accept_f) {", file=f)
|
||||||
|
print(" on_accept = on_accept_f;", file=f)
|
||||||
|
print(" rollback = 0;", file=f)
|
||||||
|
print(" blacklist_dirty = false;", file=f)
|
||||||
|
for s, t in sorted(state_types.items()):
|
||||||
|
if t.endswith("*"):
|
||||||
|
print(" st.{} = nullptr;".format(s), file=f)
|
||||||
|
else:
|
||||||
|
print(" st.{} = {}();".format(s, t), file=f)
|
||||||
|
print(" block_0();", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
|
||||||
|
print(" run([&](){on_accept_f(*this);});", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
for index in range(len(blocks)):
|
||||||
|
block = blocks[index]
|
||||||
|
|
||||||
|
print(" void block_{}() {{".format(index), file=f)
|
||||||
|
|
||||||
|
const_st = set()
|
||||||
|
nonconst_st = set()
|
||||||
|
restore_st = set()
|
||||||
|
|
||||||
|
for i in range(index):
|
||||||
|
if blocks[i]["type"] == "code":
|
||||||
|
for s in blocks[i]["states"]:
|
||||||
|
const_st.add(s)
|
||||||
|
elif blocks[i]["type"] == "match":
|
||||||
|
const_st.add(blocks[i]["cell"])
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
if block["type"] == "code":
|
||||||
|
for s in block["states"]:
|
||||||
|
if s in const_st:
|
||||||
|
const_st.remove(s)
|
||||||
|
restore_st.add(s)
|
||||||
|
nonconst_st.add(s)
|
||||||
|
elif block["type"] == "match":
|
||||||
|
s = block["cell"]
|
||||||
|
assert s not in const_st
|
||||||
|
nonconst_st.add(s)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
for s in sorted(const_st):
|
||||||
|
t = state_types[s]
|
||||||
|
if t.endswith("*"):
|
||||||
|
print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||||
|
else:
|
||||||
|
print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||||
|
|
||||||
|
for s in sorted(nonconst_st):
|
||||||
|
t = state_types[s]
|
||||||
|
print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||||
|
|
||||||
|
if len(restore_st):
|
||||||
|
print("", file=f)
|
||||||
|
for s in sorted(restore_st):
|
||||||
|
t = state_types[s]
|
||||||
|
print(" {} backup_{} = {};".format(t, s, s), file=f)
|
||||||
|
|
||||||
|
if block["type"] == "code":
|
||||||
|
print("", file=f)
|
||||||
|
print(" do {", file=f)
|
||||||
|
print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
|
||||||
|
print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
|
||||||
|
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
|
||||||
|
|
||||||
|
for line in block["code"]:
|
||||||
|
print(" " + line, file=f)
|
||||||
|
|
||||||
|
print("", file=f)
|
||||||
|
print(" block_{}();".format(index+1), file=f)
|
||||||
|
print("#undef reject", file=f)
|
||||||
|
print("#undef accept", file=f)
|
||||||
|
print("#undef branch", file=f)
|
||||||
|
print(" } while (0);", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
print("rollback_label:", file=f)
|
||||||
|
print(" YS_ATTRIBUTE(unused);", file=f)
|
||||||
|
|
||||||
|
if len(restore_st) or len(nonconst_st):
|
||||||
|
print("", file=f)
|
||||||
|
for s in sorted(restore_st):
|
||||||
|
t = state_types[s]
|
||||||
|
print(" {} = backup_{};".format(s, s), file=f)
|
||||||
|
for s in sorted(nonconst_st):
|
||||||
|
if s not in restore_st:
|
||||||
|
t = state_types[s]
|
||||||
|
if t.endswith("*"):
|
||||||
|
print(" {} = nullptr;".format(s), file=f)
|
||||||
|
else:
|
||||||
|
print(" {} = {}();".format(s, t), file=f)
|
||||||
|
|
||||||
|
elif block["type"] == "match":
|
||||||
|
assert len(restore_st) == 0
|
||||||
|
|
||||||
|
if len(block["if"]):
|
||||||
|
for expr in block["if"]:
|
||||||
|
print("", file=f)
|
||||||
|
print(" if (!({})) {{".format(expr), file=f)
|
||||||
|
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||||
|
print(" block_{}();".format(index+1), file=f)
|
||||||
|
print(" return;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
|
||||||
|
print("", file=f)
|
||||||
|
print(" index_{}_key_type key;".format(index), file=f)
|
||||||
|
for field, entry in enumerate(block["index"]):
|
||||||
|
print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
|
||||||
|
print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
|
||||||
|
|
||||||
|
print("", file=f)
|
||||||
|
print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
|
||||||
|
print(" {} = cells[idx];".format(block["cell"]), file=f)
|
||||||
|
print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
|
||||||
|
for expr in block["filter"]:
|
||||||
|
print(" if (!({})) continue;".format(expr), file=f)
|
||||||
|
print(" block_{}();".format(index+1), file=f)
|
||||||
|
print(" if (rollback) {", file=f)
|
||||||
|
print(" if (rollback != {}) {{".format(index+1), file=f)
|
||||||
|
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||||
|
print(" return;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" rollback = 0;", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
|
||||||
|
print("", file=f)
|
||||||
|
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||||
|
|
||||||
|
if block["optional"]:
|
||||||
|
print(" block_{}();".format(index+1), file=f)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void block_{}() {{".format(len(blocks)), file=f)
|
||||||
|
print(" on_accept();", file=f)
|
||||||
|
print(" check_blacklist();", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("};", file=f)
|
||||||
|
|
||||||
|
print("", file=f)
|
||||||
|
print("YOSYS_NAMESPACE_END", file=f)
|
||||||
|
|
||||||
|
# pp.pprint(blocks)
|
|
@ -886,59 +886,6 @@ module SB_WARMBOOT (
|
||||||
);
|
);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// UltraPlus feature cells
|
|
||||||
(* blackbox *)
|
|
||||||
module SB_MAC16 (
|
|
||||||
input CLK,
|
|
||||||
input CE,
|
|
||||||
input [15:0] C,
|
|
||||||
input [15:0] A,
|
|
||||||
input [15:0] B,
|
|
||||||
input [15:0] D,
|
|
||||||
input AHOLD,
|
|
||||||
input BHOLD,
|
|
||||||
input CHOLD,
|
|
||||||
input DHOLD,
|
|
||||||
input IRSTTOP,
|
|
||||||
input IRSTBOT,
|
|
||||||
input ORSTTOP,
|
|
||||||
input ORSTBOT,
|
|
||||||
input OLOADTOP,
|
|
||||||
input OLOADBOT,
|
|
||||||
input ADDSUBTOP,
|
|
||||||
input ADDSUBBOT,
|
|
||||||
input OHOLDTOP,
|
|
||||||
input OHOLDBOT,
|
|
||||||
input CI,
|
|
||||||
input ACCUMCI,
|
|
||||||
input SIGNEXTIN,
|
|
||||||
output [31:0] O,
|
|
||||||
output CO,
|
|
||||||
output ACCUMCO,
|
|
||||||
output SIGNEXTOUT
|
|
||||||
);
|
|
||||||
parameter NEG_TRIGGER = 1'b0;
|
|
||||||
parameter C_REG = 1'b0;
|
|
||||||
parameter A_REG = 1'b0;
|
|
||||||
parameter B_REG = 1'b0;
|
|
||||||
parameter D_REG = 1'b0;
|
|
||||||
parameter TOP_8x8_MULT_REG = 1'b0;
|
|
||||||
parameter BOT_8x8_MULT_REG = 1'b0;
|
|
||||||
parameter PIPELINE_16x16_MULT_REG1 = 1'b0;
|
|
||||||
parameter PIPELINE_16x16_MULT_REG2 = 1'b0;
|
|
||||||
parameter TOPOUTPUT_SELECT = 2'b00;
|
|
||||||
parameter TOPADDSUB_LOWERINPUT = 2'b00;
|
|
||||||
parameter TOPADDSUB_UPPERINPUT = 1'b0;
|
|
||||||
parameter TOPADDSUB_CARRYSELECT = 2'b00;
|
|
||||||
parameter BOTOUTPUT_SELECT = 2'b00;
|
|
||||||
parameter BOTADDSUB_LOWERINPUT = 2'b00;
|
|
||||||
parameter BOTADDSUB_UPPERINPUT = 1'b0;
|
|
||||||
parameter BOTADDSUB_CARRYSELECT = 2'b00;
|
|
||||||
parameter MODE_8x8 = 1'b0;
|
|
||||||
parameter A_SIGNED = 1'b0;
|
|
||||||
parameter B_SIGNED = 1'b0;
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
module SB_SPRAM256KA (
|
module SB_SPRAM256KA (
|
||||||
input [13:0] ADDRESS,
|
input [13:0] ADDRESS,
|
||||||
input [15:0] DATAIN,
|
input [15:0] DATAIN,
|
||||||
|
@ -1273,3 +1220,171 @@ module SB_IO_OD (
|
||||||
endgenerate
|
endgenerate
|
||||||
`endif
|
`endif
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
module SB_MAC16 (
|
||||||
|
input CLK, CE,
|
||||||
|
input [15:0] C, A, B, D,
|
||||||
|
input AHOLD, BHOLD, CHOLD, DHOLD,
|
||||||
|
input IRSTTOP, IRSTBOT,
|
||||||
|
input ORSTTOP, ORSTBOT,
|
||||||
|
input OLOADTOP, OLOADBOT,
|
||||||
|
input ADDSUBTOP, ADDSUBBOT,
|
||||||
|
input OHOLDTOP, OHOLDBOT,
|
||||||
|
input CI, ACCUMCI, SIGNEXTIN,
|
||||||
|
output [31:0] O,
|
||||||
|
output CO, ACCUMCO, SIGNEXTOUT
|
||||||
|
);
|
||||||
|
parameter [0:0] NEG_TRIGGER = 0;
|
||||||
|
parameter [0:0] C_REG = 0;
|
||||||
|
parameter [0:0] A_REG = 0;
|
||||||
|
parameter [0:0] B_REG = 0;
|
||||||
|
parameter [0:0] D_REG = 0;
|
||||||
|
parameter [0:0] TOP_8x8_MULT_REG = 0;
|
||||||
|
parameter [0:0] BOT_8x8_MULT_REG = 0;
|
||||||
|
parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
|
||||||
|
parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
|
||||||
|
parameter [1:0] TOPOUTPUT_SELECT = 0;
|
||||||
|
parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
|
||||||
|
parameter [0:0] TOPADDSUB_UPPERINPUT = 0;
|
||||||
|
parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
|
||||||
|
parameter [1:0] BOTOUTPUT_SELECT = 0;
|
||||||
|
parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
|
||||||
|
parameter [0:0] BOTADDSUB_UPPERINPUT = 0;
|
||||||
|
parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
|
||||||
|
parameter [0:0] MODE_8x8 = 0;
|
||||||
|
parameter [0:0] A_SIGNED = 0;
|
||||||
|
parameter [0:0] B_SIGNED = 0;
|
||||||
|
|
||||||
|
wire clock = CLK ^ NEG_TRIGGER;
|
||||||
|
|
||||||
|
// internal wires, compare Figure on page 133 of ICE Technology Library 3.0 and Fig 2 on page 2 of Lattice TN1295-DSP
|
||||||
|
// http://www.latticesemi.com/~/media/LatticeSemi/Documents/TechnicalBriefs/SBTICETechnologyLibrary201608.pdf
|
||||||
|
// https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/AD/DSPFunctionUsageGuideforICE40Devices.ashx
|
||||||
|
wire [15:0] iA, iB, iC, iD;
|
||||||
|
wire [15:0] iF, iJ, iK, iG;
|
||||||
|
wire [31:0] iL, iH;
|
||||||
|
wire [15:0] iW, iX, iP, iQ;
|
||||||
|
wire [15:0] iY, iZ, iR, iS;
|
||||||
|
wire HCI, LCI, LCO;
|
||||||
|
|
||||||
|
// Regs C and A
|
||||||
|
reg [15:0] rC, rA;
|
||||||
|
always @(posedge clock, posedge IRSTTOP) begin
|
||||||
|
if (IRSTTOP) begin
|
||||||
|
rC <= 0;
|
||||||
|
rA <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!CHOLD) rC <= C;
|
||||||
|
if (!AHOLD) rA <= A;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iC = C_REG ? rC : C;
|
||||||
|
assign iA = A_REG ? rA : A;
|
||||||
|
|
||||||
|
// Regs B and D
|
||||||
|
reg [15:0] rB, rD;
|
||||||
|
always @(posedge clock, posedge IRSTBOT) begin
|
||||||
|
if (IRSTBOT) begin
|
||||||
|
rB <= 0;
|
||||||
|
rD <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!BHOLD) rB <= B;
|
||||||
|
if (!DHOLD) rD <= D;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iB = B_REG ? rB : B;
|
||||||
|
assign iD = D_REG ? rD : D;
|
||||||
|
|
||||||
|
// Multiplier Stage
|
||||||
|
wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl;
|
||||||
|
wire [15:0] Ah, Al, Bh, Bl;
|
||||||
|
assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]};
|
||||||
|
assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]};
|
||||||
|
assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]};
|
||||||
|
assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]};
|
||||||
|
assign p_Ah_Bh = Ah * Bh;
|
||||||
|
assign p_Al_Bh = Al * Bh;
|
||||||
|
assign p_Ah_Bl = Ah * Bl;
|
||||||
|
assign p_Al_Bl = Al * Bl;
|
||||||
|
|
||||||
|
// Regs F and J
|
||||||
|
reg [15:0] rF, rJ;
|
||||||
|
always @(posedge clock, posedge IRSTTOP) begin
|
||||||
|
if (IRSTTOP) begin
|
||||||
|
rF <= 0;
|
||||||
|
rJ <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
rF <= p_Ah_Bh;
|
||||||
|
if (!MODE_8x8) rJ <= p_Al_Bh;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iF = TOP_8x8_MULT_REG ? rF : p_Ah_Bh;
|
||||||
|
assign iJ = PIPELINE_16x16_MULT_REG1 ? rJ : p_Al_Bh;
|
||||||
|
|
||||||
|
// Regs K and G
|
||||||
|
reg [15:0] rK, rG;
|
||||||
|
always @(posedge clock, posedge IRSTBOT) begin
|
||||||
|
if (IRSTBOT) begin
|
||||||
|
rK <= 0;
|
||||||
|
rG <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!MODE_8x8) rK <= p_Ah_Bl;
|
||||||
|
rG <= p_Al_Bl;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iK = PIPELINE_16x16_MULT_REG1 ? rK : p_Ah_Bl;
|
||||||
|
assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl;
|
||||||
|
|
||||||
|
// Adder Stage
|
||||||
|
assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16);
|
||||||
|
|
||||||
|
// Reg H
|
||||||
|
reg [31:0] rH;
|
||||||
|
always @(posedge clock, posedge IRSTBOT) begin
|
||||||
|
if (IRSTBOT) begin
|
||||||
|
rH <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!MODE_8x8) rH <= iL;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iH = PIPELINE_16x16_MULT_REG2 ? rH : iL;
|
||||||
|
|
||||||
|
// Hi Output Stage
|
||||||
|
wire [15:0] XW, Oh;
|
||||||
|
reg [15:0] rQ;
|
||||||
|
assign iW = TOPADDSUB_UPPERINPUT ? iC : iQ;
|
||||||
|
assign iX = (TOPADDSUB_LOWERINPUT == 0) ? iA : (TOPADDSUB_LOWERINPUT == 1) ? iF : (TOPADDSUB_LOWERINPUT == 2) ? iH[31:16] : {16{iZ[15]}};
|
||||||
|
assign {ACCUMCO, XW} = iX + (iW ^ {16{ADDSUBTOP}}) + HCI;
|
||||||
|
assign CO = ACCUMCO ^ ADDSUBTOP;
|
||||||
|
assign iP = OLOADTOP ? iC : XW ^ {16{ADDSUBTOP}};
|
||||||
|
always @(posedge clock, posedge ORSTTOP) begin
|
||||||
|
if (ORSTTOP) begin
|
||||||
|
rQ <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!OHOLDTOP) rQ <= iP;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iQ = rQ;
|
||||||
|
assign Oh = (TOPOUTPUT_SELECT == 0) ? iP : (TOPOUTPUT_SELECT == 1) ? iQ : (TOPOUTPUT_SELECT == 2) ? iF : iH[31:16];
|
||||||
|
assign HCI = (TOPADDSUB_CARRYSELECT == 0) ? 1'b0 : (TOPADDSUB_CARRYSELECT == 1) ? 1'b1 : (TOPADDSUB_CARRYSELECT == 2) ? LCO : LCO ^ ADDSUBBOT;
|
||||||
|
assign SIGNEXTOUT = iX[15];
|
||||||
|
|
||||||
|
// Lo Output Stage
|
||||||
|
wire [15:0] YZ, Ol;
|
||||||
|
reg [15:0] rS;
|
||||||
|
assign iY = BOTADDSUB_UPPERINPUT ? iD : iS;
|
||||||
|
assign iZ = (BOTADDSUB_LOWERINPUT == 0) ? iB : (BOTADDSUB_LOWERINPUT == 1) ? iG : (BOTADDSUB_LOWERINPUT == 2) ? iH[15:0] : {16{SIGNEXTIN}};
|
||||||
|
assign {LCO, YZ} = iZ + (iY ^ {16{ADDSUBBOT}}) + LCI;
|
||||||
|
assign iR = OLOADBOT ? iD : YZ ^ {16{ADDSUBBOT}};
|
||||||
|
always @(posedge clock, posedge ORSTBOT) begin
|
||||||
|
if (ORSTBOT) begin
|
||||||
|
rS <= 0;
|
||||||
|
end else if (CE) begin
|
||||||
|
if (!OHOLDBOT) rS <= iR;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign iS = rS;
|
||||||
|
assign Ol = (BOTOUTPUT_SELECT == 0) ? iR : (BOTOUTPUT_SELECT == 1) ? iS : (BOTOUTPUT_SELECT == 2) ? iG : iH[15:0];
|
||||||
|
assign LCI = (BOTADDSUB_CARRYSELECT == 0) ? 1'b0 : (BOTADDSUB_CARRYSELECT == 1) ? 1'b1 : (BOTADDSUB_CARRYSELECT == 2) ? ACCUMCI : CI;
|
||||||
|
assign O = {Oh, Ol};
|
||||||
|
endmodule
|
||||||
|
|
|
@ -79,6 +79,9 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
log(" -nobram\n");
|
log(" -nobram\n");
|
||||||
log(" do not use SB_RAM40_4K* cells in output netlist\n");
|
log(" do not use SB_RAM40_4K* cells in output netlist\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -dsp\n");
|
||||||
|
log(" use iCE40 UltraPlus DSP cells for large arithmetic\n");
|
||||||
|
log("\n");
|
||||||
log(" -noabc\n");
|
log(" -noabc\n");
|
||||||
log(" use built-in Yosys LUT techmapping instead of abc\n");
|
log(" use built-in Yosys LUT techmapping instead of abc\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -96,7 +99,7 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, blif_file, edif_file, json_file;
|
string top_opt, blif_file, edif_file, json_file;
|
||||||
bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr;
|
bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr;
|
||||||
int min_ce_use;
|
int min_ce_use;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
|
@ -109,6 +112,7 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
nodffe = false;
|
nodffe = false;
|
||||||
min_ce_use = -1;
|
min_ce_use = -1;
|
||||||
nobram = false;
|
nobram = false;
|
||||||
|
dsp = false;
|
||||||
flatten = true;
|
flatten = true;
|
||||||
retime = false;
|
retime = false;
|
||||||
relut = false;
|
relut = false;
|
||||||
|
@ -181,6 +185,10 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
nobram = true;
|
nobram = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-dsp") {
|
||||||
|
dsp = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-noabc") {
|
if (args[argidx] == "-noabc") {
|
||||||
noabc = true;
|
noabc = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -214,11 +222,11 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
{
|
{
|
||||||
run("read_verilog -lib +/ice40/cells_sim.v");
|
run("read_verilog -lib +/ice40/cells_sim.v");
|
||||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||||
|
run("proc");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flatten && check_label("flatten", "(unless -noflatten)"))
|
if (flatten && check_label("flatten", "(unless -noflatten)"))
|
||||||
{
|
{
|
||||||
run("proc");
|
|
||||||
run("flatten");
|
run("flatten");
|
||||||
run("tribuf -logic");
|
run("tribuf -logic");
|
||||||
run("deminout");
|
run("deminout");
|
||||||
|
@ -226,7 +234,23 @@ struct SynthIce40Pass : public ScriptPass
|
||||||
|
|
||||||
if (check_label("coarse"))
|
if (check_label("coarse"))
|
||||||
{
|
{
|
||||||
run("synth -lut 4 -run coarse");
|
run("opt_expr");
|
||||||
|
run("opt_clean");
|
||||||
|
run("check");
|
||||||
|
run("opt");
|
||||||
|
run("wreduce");
|
||||||
|
run("share");
|
||||||
|
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||||
|
run("opt_expr");
|
||||||
|
run("opt_clean");
|
||||||
|
if (help_mode || dsp)
|
||||||
|
run("ice40_dsp", "(if -dsp)");
|
||||||
|
run("alumacc");
|
||||||
|
run("opt");
|
||||||
|
run("fsm");
|
||||||
|
run("opt -fast");
|
||||||
|
run("memory -nomap");
|
||||||
|
run("opt_clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nobram && check_label("bram", "(skip if -nobram)"))
|
if (!nobram && check_label("bram", "(skip if -nobram)"))
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
test_ffs_[01][01][01][01][01]_*
|
/test_ffs_[01][01][01][01][01]_*
|
||||||
test_bram_[0-9]*
|
/test_bram_[0-9]*
|
||||||
|
/test_dsp_model
|
||||||
|
/test_dsp_model.vcd
|
||||||
|
/test_dsp_model_ref.v
|
||||||
|
/test_dsp_model_uut.v
|
||||||
|
/test_dsp_map
|
||||||
|
/test_dsp_map.vcd
|
||||||
|
/test_dsp_map_tb.v
|
||||||
|
/test_dsp_map_top.v
|
||||||
|
/test_dsp_map_syn.v
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
for iter in {1..100}
|
||||||
|
do
|
||||||
|
SZA=$(( 3 + $RANDOM % 13 ))
|
||||||
|
SZB=$(( 3 + $RANDOM % 13 ))
|
||||||
|
SZO=$(( 3 + $RANDOM % 29 ))
|
||||||
|
|
||||||
|
C0=clk$(( $RANDOM & 1))
|
||||||
|
C1=clk$(( $RANDOM & 1))
|
||||||
|
C2=clk$(( $RANDOM & 1))
|
||||||
|
C3=clk$(( $RANDOM & 1))
|
||||||
|
|
||||||
|
E0=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||||
|
E1=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||||
|
E2=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||||
|
E3=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||||
|
|
||||||
|
SP=$( test $(( $RANDOM & 1 )) -eq 0 && echo S || echo P )
|
||||||
|
|
||||||
|
RC=$( test $(( $RANDOM & 1 )) -eq 0 && echo "reset" || echo "!reset" )
|
||||||
|
RV="32'h$( echo $RANDOM | md5sum | cut -c1-8 )"
|
||||||
|
|
||||||
|
cat > test_dsp_map_top.v << EOT
|
||||||
|
module top (
|
||||||
|
input clk0, clk1, reset,
|
||||||
|
input [$SZA:0] A,
|
||||||
|
input [$SZB:0] B,
|
||||||
|
output [$SZO:0] O
|
||||||
|
);
|
||||||
|
reg [15:0] AA, BB;
|
||||||
|
reg [31:0] P, S;
|
||||||
|
|
||||||
|
always @($E0 $C0) AA <= A;
|
||||||
|
always @($E1 $C1) BB <= B;
|
||||||
|
always @($E2 $C2) P <= AA * BB;
|
||||||
|
always @($E3 $C3) S <= $RC ? $RV : S + P;
|
||||||
|
assign O = $SP;
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
cat > test_dsp_map_tb.v << EOT
|
||||||
|
\`timescale 1ns / 1ps
|
||||||
|
module testbench;
|
||||||
|
reg clk1, clk0, reset;
|
||||||
|
reg [$SZA:0] A;
|
||||||
|
reg [$SZB:0] B;
|
||||||
|
|
||||||
|
wire [$SZO:0] O_top, O_syn;
|
||||||
|
|
||||||
|
top top_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_top));
|
||||||
|
syn syn_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_syn));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// \$dumpfile("test_dsp_map.vcd");
|
||||||
|
// \$dumpvars(0, testbench);
|
||||||
|
|
||||||
|
#2;
|
||||||
|
clk0 = 0;
|
||||||
|
clk1 = 0;
|
||||||
|
reset = 1;
|
||||||
|
reset = $RC;
|
||||||
|
A = 0;
|
||||||
|
B = 0;
|
||||||
|
|
||||||
|
repeat (3) begin
|
||||||
|
#2; clk0 = ~clk0;
|
||||||
|
#2; clk0 = ~clk0;
|
||||||
|
#2; clk1 = ~clk1;
|
||||||
|
#2; clk1 = ~clk1;
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat (100) begin
|
||||||
|
#2;
|
||||||
|
A = \$urandom;
|
||||||
|
B = \$urandom;
|
||||||
|
reset = \$urandom & \$urandom & \$urandom & \$urandom;
|
||||||
|
if (\$urandom & 1) begin
|
||||||
|
#2; clk0 = ~clk0;
|
||||||
|
#2; clk0 = ~clk0;
|
||||||
|
end else begin
|
||||||
|
#2; clk1 = ~clk1;
|
||||||
|
#2; clk1 = ~clk1;
|
||||||
|
end
|
||||||
|
#2;
|
||||||
|
if (O_top !== O_syn) begin
|
||||||
|
\$display("ERROR: O_top=%b O_syn=%b", O_top, O_syn);
|
||||||
|
\$stop;
|
||||||
|
end
|
||||||
|
// \$display("OK O_top=O_syn=%b", O_top);
|
||||||
|
end
|
||||||
|
|
||||||
|
\$display("Test passed.");
|
||||||
|
\$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
../../../yosys -p 'read_verilog test_dsp_map_top.v; synth_ice40 -dsp; rename top syn; write_verilog test_dsp_map_syn.v'
|
||||||
|
iverilog -o test_dsp_map -s testbench test_dsp_map_tb.v test_dsp_map_top.v test_dsp_map_syn.v ../cells_sim.v
|
||||||
|
vvp -N test_dsp_map
|
||||||
|
done
|
||||||
|
|
||||||
|
: ""
|
||||||
|
: "#### All tests passed. ####"
|
||||||
|
: ""
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v
|
||||||
|
cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v
|
||||||
|
for tb in testbench \
|
||||||
|
testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \
|
||||||
|
testbench_seq_16x16_A testbench_seq_16x16_B
|
||||||
|
do
|
||||||
|
iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v
|
||||||
|
vvp -N ./test_dsp_model
|
||||||
|
done
|
|
@ -0,0 +1,342 @@
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module testbench;
|
||||||
|
parameter [0:0] NEG_TRIGGER = 0;
|
||||||
|
parameter [0:0] C_REG = 0;
|
||||||
|
parameter [0:0] A_REG = 0;
|
||||||
|
parameter [0:0] B_REG = 0;
|
||||||
|
parameter [0:0] D_REG = 0;
|
||||||
|
parameter [0:0] TOP_8x8_MULT_REG = 0;
|
||||||
|
parameter [0:0] BOT_8x8_MULT_REG = 0;
|
||||||
|
parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
|
||||||
|
parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
|
||||||
|
parameter [1:0] TOPOUTPUT_SELECT = 0;
|
||||||
|
parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
|
||||||
|
parameter [0:0] TOPADDSUB_UPPERINPUT = 1;
|
||||||
|
parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
|
||||||
|
parameter [1:0] BOTOUTPUT_SELECT = 0;
|
||||||
|
parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
|
||||||
|
parameter [0:0] BOTADDSUB_UPPERINPUT = 1;
|
||||||
|
parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
|
||||||
|
parameter [0:0] MODE_8x8 = 0;
|
||||||
|
parameter [0:0] A_SIGNED = 0;
|
||||||
|
parameter [0:0] B_SIGNED = 0;
|
||||||
|
|
||||||
|
reg CLK, CE;
|
||||||
|
reg [15:0] C, A, B, D;
|
||||||
|
reg AHOLD, BHOLD, CHOLD, DHOLD;
|
||||||
|
reg IRSTTOP, IRSTBOT;
|
||||||
|
reg ORSTTOP, ORSTBOT;
|
||||||
|
reg OLOADTOP, OLOADBOT;
|
||||||
|
reg ADDSUBTOP, ADDSUBBOT;
|
||||||
|
reg OHOLDTOP, OHOLDBOT;
|
||||||
|
reg CI, ACCUMCI, SIGNEXTIN;
|
||||||
|
|
||||||
|
output [31:0] REF_O, UUT_O;
|
||||||
|
output REF_CO, REF_ACCUMCO, REF_SIGNEXTOUT;
|
||||||
|
output UUT_CO, UUT_ACCUMCO, UUT_SIGNEXTOUT;
|
||||||
|
|
||||||
|
integer errcount = 0;
|
||||||
|
|
||||||
|
task clkcycle;
|
||||||
|
begin
|
||||||
|
#5;
|
||||||
|
CLK = ~CLK;
|
||||||
|
#10;
|
||||||
|
CLK = ~CLK;
|
||||||
|
#2;
|
||||||
|
if (REF_O !== UUT_O) begin
|
||||||
|
$display("ERROR at %1t: REF_O=%b UUT_O=%b DIFF=%b", $time, REF_O, UUT_O, REF_O ^ UUT_O);
|
||||||
|
errcount = errcount + 1;
|
||||||
|
end
|
||||||
|
if (REF_CO !== UUT_CO) begin
|
||||||
|
$display("ERROR at %1t: REF_CO=%b UUT_CO=%b", $time, REF_CO, UUT_CO);
|
||||||
|
errcount = errcount + 1;
|
||||||
|
end
|
||||||
|
if (REF_ACCUMCO !== UUT_ACCUMCO) begin
|
||||||
|
$display("ERROR at %1t: REF_ACCUMCO=%b UUT_ACCUMCO=%b", $time, REF_ACCUMCO, UUT_ACCUMCO);
|
||||||
|
errcount = errcount + 1;
|
||||||
|
end
|
||||||
|
if (REF_SIGNEXTOUT !== UUT_SIGNEXTOUT) begin
|
||||||
|
$display("ERROR at %1t: REF_SIGNEXTOUT=%b UUT_SIGNEXTOUT=%b", $time, REF_SIGNEXTOUT, UUT_SIGNEXTOUT);
|
||||||
|
errcount = errcount + 1;
|
||||||
|
end
|
||||||
|
#3;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$dumpfile("test_dsp_model.vcd");
|
||||||
|
$dumpvars(0, testbench);
|
||||||
|
|
||||||
|
#2;
|
||||||
|
CLK = NEG_TRIGGER;
|
||||||
|
CE = 1;
|
||||||
|
{C, A, B, D} = 0;
|
||||||
|
{AHOLD, BHOLD, CHOLD, DHOLD} = 0;
|
||||||
|
{OLOADTOP, OLOADBOT} = 0;
|
||||||
|
{ADDSUBTOP, ADDSUBBOT} = 0;
|
||||||
|
{OHOLDTOP, OHOLDBOT} = 0;
|
||||||
|
{CI, ACCUMCI, SIGNEXTIN} = 0;
|
||||||
|
|
||||||
|
{IRSTTOP, IRSTBOT} = ~0;
|
||||||
|
{ORSTTOP, ORSTBOT} = ~0;
|
||||||
|
|
||||||
|
#3;
|
||||||
|
{IRSTTOP, IRSTBOT} = 0;
|
||||||
|
{ORSTTOP, ORSTBOT} = 0;
|
||||||
|
|
||||||
|
repeat (300) begin
|
||||||
|
clkcycle;
|
||||||
|
|
||||||
|
A = $urandom;
|
||||||
|
B = $urandom;
|
||||||
|
C = $urandom;
|
||||||
|
D = $urandom;
|
||||||
|
|
||||||
|
{AHOLD, BHOLD, CHOLD, DHOLD} = $urandom & $urandom & $urandom;
|
||||||
|
{OLOADTOP, OLOADBOT} = $urandom & $urandom & $urandom;
|
||||||
|
{ADDSUBTOP, ADDSUBBOT} = $urandom & $urandom & $urandom;
|
||||||
|
{OHOLDTOP, OHOLDBOT} = $urandom & $urandom & $urandom;
|
||||||
|
{CI, ACCUMCI, SIGNEXTIN} = $urandom & $urandom & $urandom;
|
||||||
|
|
||||||
|
{IRSTTOP, IRSTBOT} = $urandom & $urandom & $urandom;
|
||||||
|
{ORSTTOP, ORSTBOT} = $urandom & $urandom & $urandom;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (errcount == 0) begin
|
||||||
|
$display("All tests passed.");
|
||||||
|
$finish;
|
||||||
|
end else begin
|
||||||
|
$display("Caught %1d errors.", errcount);
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
SB_MAC16 #(
|
||||||
|
.NEG_TRIGGER (NEG_TRIGGER ),
|
||||||
|
.C_REG (C_REG ),
|
||||||
|
.A_REG (A_REG ),
|
||||||
|
.B_REG (B_REG ),
|
||||||
|
.D_REG (D_REG ),
|
||||||
|
.TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ),
|
||||||
|
.BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
|
||||||
|
.TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ),
|
||||||
|
.TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ),
|
||||||
|
.TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ),
|
||||||
|
.TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ),
|
||||||
|
.BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ),
|
||||||
|
.BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ),
|
||||||
|
.BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ),
|
||||||
|
.BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ),
|
||||||
|
.MODE_8x8 (MODE_8x8 ),
|
||||||
|
.A_SIGNED (A_SIGNED ),
|
||||||
|
.B_SIGNED (B_SIGNED )
|
||||||
|
) ref (
|
||||||
|
.CLK (CLK ),
|
||||||
|
.CE (CE ),
|
||||||
|
.C (C ),
|
||||||
|
.A (A ),
|
||||||
|
.B (B ),
|
||||||
|
.D (D ),
|
||||||
|
.AHOLD (AHOLD ),
|
||||||
|
.BHOLD (BHOLD ),
|
||||||
|
.CHOLD (CHOLD ),
|
||||||
|
.DHOLD (DHOLD ),
|
||||||
|
.IRSTTOP (IRSTTOP ),
|
||||||
|
.IRSTBOT (IRSTBOT ),
|
||||||
|
.ORSTTOP (ORSTTOP ),
|
||||||
|
.ORSTBOT (ORSTBOT ),
|
||||||
|
.OLOADTOP (OLOADTOP ),
|
||||||
|
.OLOADBOT (OLOADBOT ),
|
||||||
|
.ADDSUBTOP (ADDSUBTOP ),
|
||||||
|
.ADDSUBBOT (ADDSUBBOT ),
|
||||||
|
.OHOLDTOP (OHOLDTOP ),
|
||||||
|
.OHOLDBOT (OHOLDBOT ),
|
||||||
|
.CI (CI ),
|
||||||
|
.ACCUMCI (ACCUMCI ),
|
||||||
|
.SIGNEXTIN (SIGNEXTIN ),
|
||||||
|
.O (REF_O ),
|
||||||
|
.CO (REF_CO ),
|
||||||
|
.ACCUMCO (REF_ACCUMCO ),
|
||||||
|
.SIGNEXTOUT (REF_SIGNEXTOUT)
|
||||||
|
);
|
||||||
|
|
||||||
|
SB_MAC16_UUT #(
|
||||||
|
.NEG_TRIGGER (NEG_TRIGGER ),
|
||||||
|
.C_REG (C_REG ),
|
||||||
|
.A_REG (A_REG ),
|
||||||
|
.B_REG (B_REG ),
|
||||||
|
.D_REG (D_REG ),
|
||||||
|
.TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ),
|
||||||
|
.BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
|
||||||
|
.TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ),
|
||||||
|
.TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ),
|
||||||
|
.TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ),
|
||||||
|
.TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ),
|
||||||
|
.BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ),
|
||||||
|
.BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ),
|
||||||
|
.BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ),
|
||||||
|
.BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ),
|
||||||
|
.MODE_8x8 (MODE_8x8 ),
|
||||||
|
.A_SIGNED (A_SIGNED ),
|
||||||
|
.B_SIGNED (B_SIGNED )
|
||||||
|
) uut (
|
||||||
|
.CLK (CLK ),
|
||||||
|
.CE (CE ),
|
||||||
|
.C (C ),
|
||||||
|
.A (A ),
|
||||||
|
.B (B ),
|
||||||
|
.D (D ),
|
||||||
|
.AHOLD (AHOLD ),
|
||||||
|
.BHOLD (BHOLD ),
|
||||||
|
.CHOLD (CHOLD ),
|
||||||
|
.DHOLD (DHOLD ),
|
||||||
|
.IRSTTOP (IRSTTOP ),
|
||||||
|
.IRSTBOT (IRSTBOT ),
|
||||||
|
.ORSTTOP (ORSTTOP ),
|
||||||
|
.ORSTBOT (ORSTBOT ),
|
||||||
|
.OLOADTOP (OLOADTOP ),
|
||||||
|
.OLOADBOT (OLOADBOT ),
|
||||||
|
.ADDSUBTOP (ADDSUBTOP ),
|
||||||
|
.ADDSUBBOT (ADDSUBBOT ),
|
||||||
|
.OHOLDTOP (OHOLDTOP ),
|
||||||
|
.OHOLDBOT (OHOLDBOT ),
|
||||||
|
.CI (CI ),
|
||||||
|
.ACCUMCI (ACCUMCI ),
|
||||||
|
.SIGNEXTIN (SIGNEXTIN ),
|
||||||
|
.O (UUT_O ),
|
||||||
|
.CO (UUT_CO ),
|
||||||
|
.ACCUMCO (UUT_ACCUMCO ),
|
||||||
|
.SIGNEXTOUT (UUT_SIGNEXTOUT)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module testbench_comb_8x8_A;
|
||||||
|
testbench #(
|
||||||
|
.NEG_TRIGGER (0),
|
||||||
|
.C_REG (0),
|
||||||
|
.A_REG (0),
|
||||||
|
.B_REG (0),
|
||||||
|
.D_REG (0),
|
||||||
|
.TOP_8x8_MULT_REG (0),
|
||||||
|
.BOT_8x8_MULT_REG (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (0),
|
||||||
|
.TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||||
|
.TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C
|
||||||
|
.TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||||
|
.BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D
|
||||||
|
.BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.MODE_8x8 (0),
|
||||||
|
.A_SIGNED (0),
|
||||||
|
.B_SIGNED (0)
|
||||||
|
) testbench ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module testbench_comb_8x8_B;
|
||||||
|
testbench #(
|
||||||
|
.NEG_TRIGGER (0),
|
||||||
|
.C_REG (0),
|
||||||
|
.A_REG (0),
|
||||||
|
.B_REG (0),
|
||||||
|
.D_REG (0),
|
||||||
|
.TOP_8x8_MULT_REG (0),
|
||||||
|
.BOT_8x8_MULT_REG (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (0),
|
||||||
|
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||||
|
.TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||||
|
.TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||||
|
.BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||||
|
.BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.MODE_8x8 (0),
|
||||||
|
.A_SIGNED (0),
|
||||||
|
.B_SIGNED (0)
|
||||||
|
) testbench ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module testbench_comb_16x16;
|
||||||
|
testbench #(
|
||||||
|
.NEG_TRIGGER (0),
|
||||||
|
.C_REG (0),
|
||||||
|
.A_REG (0),
|
||||||
|
.B_REG (0),
|
||||||
|
.D_REG (0),
|
||||||
|
.TOP_8x8_MULT_REG (0),
|
||||||
|
.BOT_8x8_MULT_REG (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (0),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (0),
|
||||||
|
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||||
|
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||||
|
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||||
|
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||||
|
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.MODE_8x8 (0),
|
||||||
|
.A_SIGNED (0),
|
||||||
|
.B_SIGNED (0)
|
||||||
|
) testbench ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module testbench_seq_16x16_A;
|
||||||
|
testbench #(
|
||||||
|
.NEG_TRIGGER (0),
|
||||||
|
.C_REG (1),
|
||||||
|
.A_REG (1),
|
||||||
|
.B_REG (1),
|
||||||
|
.D_REG (1),
|
||||||
|
.TOP_8x8_MULT_REG (1),
|
||||||
|
.BOT_8x8_MULT_REG (1),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (1),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (1),
|
||||||
|
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||||
|
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||||
|
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||||
|
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||||
|
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.MODE_8x8 (0),
|
||||||
|
.A_SIGNED (0),
|
||||||
|
.B_SIGNED (0)
|
||||||
|
) testbench ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module testbench_seq_16x16_B;
|
||||||
|
testbench #(
|
||||||
|
.NEG_TRIGGER (0),
|
||||||
|
.C_REG (1),
|
||||||
|
.A_REG (1),
|
||||||
|
.B_REG (1),
|
||||||
|
.D_REG (1),
|
||||||
|
.TOP_8x8_MULT_REG (1),
|
||||||
|
.BOT_8x8_MULT_REG (1),
|
||||||
|
.PIPELINE_16x16_MULT_REG1 (1),
|
||||||
|
.PIPELINE_16x16_MULT_REG2 (0),
|
||||||
|
.TOPOUTPUT_SELECT (1), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||||
|
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C
|
||||||
|
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.BOTOUTPUT_SELECT (1), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||||
|
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||||
|
.BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D
|
||||||
|
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||||
|
.MODE_8x8 (0),
|
||||||
|
.A_SIGNED (0),
|
||||||
|
.B_SIGNED (0)
|
||||||
|
) testbench ();
|
||||||
|
endmodule
|
Loading…
Reference in New Issue