aboutsummaryrefslogtreecommitdiffstats
path: root/passes/pmgen/xilinx_dsp_CREG.pmg
diff options
context:
space:
mode:
Diffstat (limited to 'passes/pmgen/xilinx_dsp_CREG.pmg')
-rw-r--r--passes/pmgen/xilinx_dsp_CREG.pmg81
1 files changed, 67 insertions, 14 deletions
diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/passes/pmgen/xilinx_dsp_CREG.pmg
index a31dc80bf..a57043009 100644
--- a/passes/pmgen/xilinx_dsp_CREG.pmg
+++ b/passes/pmgen/xilinx_dsp_CREG.pmg
@@ -1,3 +1,26 @@
+// This file describes the second of three pattern matcher setups that
+// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
+// At a high level, it works as follows:
+// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
+// and (b) uses the 'C' port
+// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed below)
+// Notes:
+// - Running CREG packing after xilinx_dsp_pack is necessary since there is no
+// guarantee that the cell ordering corresponds to the "expected" case (i.e.
+// the order in which they appear in the source) thus the possiblity existed
+// that a register got packed as a CREG into a downstream DSP that should
+// have otherwise been a PREG of an upstream DSP that had not been visited
+// yet
+// - The reason this is separated out from the xilinx_dsp.pmg file is
+// for efficiency --- each *.pmg file creates a class of the same basename,
+// which when constructed, creates a custom database tailored to the
+// pattern(s) contained within. Since the pattern in this file must be
+// executed after the pattern contained in xilinx_dsp.pmg, it is necessary
+// to reconstruct this database. Separating the two patterns into
+// independent files causes two smaller, more specific, databases.
+
pattern xilinx_dsp_packC
udata <std::function<SigSpec(const SigSpec&)>> unextend
@@ -6,7 +29,7 @@ state <SigSpec> sigC sigP
state <bool> ffCcepol ffCrstpol
state <Cell*> ffC ffCcemux ffCrstmux
-// subpattern
+// Variables used for subpatterns
state <SigSpec> argQ argD
state <bool> ffcepol ffrstpol
state <int> ffoffset
@@ -15,13 +38,15 @@ udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
+// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
+// and (b) uses the 'C' port
match dsp
select dsp->type.in(\DSP48E1)
select param(dsp, \CREG, 1).as_int() == 0
select nusers(port(dsp, \C, SigSpec())) > 1
endmatch
-code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC sigP clock
+code sigC sigP clock
unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
@@ -48,11 +73,13 @@ code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC sigP clock
else
sigP = P;
- if (sigC == sigP)
- reject;
-
clock = port(dsp, \CLK, SigBit());
+endcode
+// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using the in_dffe subpattern)
+code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
argQ = sigC;
subpattern(in_dffe);
if (dff) {
@@ -77,22 +104,44 @@ endcode
// #######################
+// Subpattern for matching against input registers, based on knowledge of the
+// 'Q' input. Typically, identifying registers with clock-enable and reset
+// capability would be a task would be handled by other Yosys passes such as
+// dff2dffe, but since DSP inference happens much before this, these patterns
+// have to be manually identified.
+// At a high level:
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// one that exclusively drives the 'D' input of the $dff, with one of its
+// $mux inputs being fully zero
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
subpattern in_dffe
arg argD argQ clock
code
dff = nullptr;
- for (auto c : argQ.chunks()) {
+ for (const auto &c : argQ.chunks()) {
+ // Abandon matches when 'Q' is a constant
if (!c.wire)
reject;
+ // Abandon matches when 'Q' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
- Const init = c.wire->attributes.at(\init, State::Sx);
- if (!init.is_fully_undef() && !init.is_fully_zero())
- reject;
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ Const init = c.wire->attributes.at(\init, Const());
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
}
endcode
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
@@ -105,14 +154,12 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
set ffoffset offset
endmatch
code argQ argD
-{
- if (clock != SigBit() && port(ff, \CLK) != clock)
- reject;
-
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
@@ -124,9 +171,11 @@ code argQ argD
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
-}
endcode
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// exclusively drives the 'D' input of the $dff, with one of the $mux
+// inputs being fully zero
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
@@ -158,6 +207,10 @@ code argD
dffrstmux = nullptr;
endcode
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
match ffcemux
if !argD.empty()
select ffcemux->type.in($mux)