File: microchip_dsp_cascade.pmg

package info (click to toggle)
yosys 0.52-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 69,796 kB
  • sloc: ansic: 696,955; cpp: 239,736; python: 14,617; yacc: 3,529; sh: 2,175; makefile: 1,945; lex: 697; perl: 445; javascript: 323; tcl: 162; vhdl: 115
file content (236 lines) | stat: -rw-r--r-- 7,427 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// ISC License
// 
// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
// 
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


// This file describes the third of three pattern matcher setups that
//   forms the `microchip_dsp` pass described in microchip_dsp.cc
// At a high level, it works as follows:
//   (1) Starting from a DSP cell that 
//         (a) CDIN_FDBK_SEL is set to default "00"
//         (b) doesn't already use the 'PCOUT' port
//   (2) Match another DSP cell that 
//         (a) does not have the CREG enabled,
//         (b) 'C' port is driven by the 'P' output of the previous DSP cell
//         (c) has its 'PCIN' port unused
//   (3) Recursively go to (2) until no more matches possible, keeping track
//       of the longest possible chain found
//   (4) The longest chain is then divided into chunks of no more than
//       MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
//       height of a DSP column) with each DSP in each chunk being rewritten
//       to use [ABP]COUT -> [ABP]CIN cascading as appropriate

pattern microchip_dsp_cascade

udata <std::function<SigSpec(const SigSpec&)>> unextend
udata <vector<std::tuple<Cell*,int>>> chain longest_chain
udata <std::set<Cell*>> visited
state <Cell*> next
state <SigSpec> clock

// Variables used for subpatterns
state <SigSpec> argQ argD
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff

// Maximum of 24 cascaded blocks
code
#define MAX_DSP_CASCADE 24
endcode

// NOTE: Chain vector
//  +--------+      +--------+
//  | first  |----> |  next  | ----> ...
//  +--------+      +--------+
//  first.COUT cascades to next.CIN, so on and so forth

// Helper function to remove unused bits
code
	unextend = [](const SigSpec &sig) {
		int i;
		for (i = GetSize(sig)-1; i > 0; i--)
			if (sig[i] != sig[i-1])
				break;
		// Do not remove non-const sign bit
		if (sig[i].wire)
			++i;
		return sig.extract(0, i);
	};
endcode

// (1) Starting from a DSP cell that 
//     (a) CDIN_FDBK_SEL is set to default "00"
//     (b) doesn't already use the 'PCOUT' port
match first
	select first->type.in(\MACC_PA) && port(first, \CDIN_FDBK_SEL, Const(0, 2)) == Const::from_string("00")
	select nusers(port(first, \CDOUT, SigSpec())) <= 1
endmatch

// (4) The longest chain is then divided into chunks of no more than
//     MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
//     height of a DSP column) with each DSP in each chunk being rewritten
//     to use [ABP]COUT -> [ABP]CIN cascading as appropriate
code
	visited.clear();
	visited.insert(first);

	longest_chain.clear();
	chain.emplace_back(first, -1);
	subpattern(tail);
finally

	// longest cascade chain has been found with DSP "first" being the head of the chain
	// do some post processing

	chain.pop_back();
	visited.clear();
	log_assert(chain.empty());

	if (GetSize(longest_chain) > 1) {
		Cell *dsp = std::get<0>(longest_chain.front());

		Cell *dsp_pcin;
		int SHIFT = -1;
		for (int i = 1; i < GetSize(longest_chain); i++) {
			log_assert(dsp->type.in(\MACC_PA));

			std::tie(dsp_pcin,SHIFT) = longest_chain[i];

			// Chain length exceeds the maximum cascade length, must split it up
			if (i % MAX_DSP_CASCADE > 0) {
				Wire *cascade = module->addWire(NEW_ID, 48);

				// zero port C and move wire to cascade
				dsp_pcin->setPort(ID(C), Const(0, 48));
				dsp_pcin->setPort(ID(CDIN), cascade);
				dsp->setPort(ID(CDOUT), cascade);

				// Configure wire to cascade the dsps
				add_siguser(cascade, dsp_pcin);
				add_siguser(cascade, dsp);

				// configure mux to use cascade for signal E
				SigSpec cdin_fdbk_sel = port(dsp_pcin, \CDIN_FDBK_SEL, Const(0, 2));
				cdin_fdbk_sel[1] = State::S1;
				dsp_pcin->setPort(\CDIN_FDBK_SEL, cdin_fdbk_sel);

				// check if shifting is required for wide multiplier implmentation
				if (SHIFT == 17)
				{
					dsp_pcin->setPort(\ARSHFT17, State::S1);
				}
				

				log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));

			} else {
				log_debug("  Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
			}

			dsp = dsp_pcin;
		}

		accept;
	}
endcode

// ------------------------------------------------------------------

subpattern tail
arg first
arg next

// (2) Match another DSP cell that 
//          (a) does not have the CREG enabled,
//          (b) 'C' port is driven by the 'P' output of the previous DSP cell
//          (c) has its 'PCIN' port unused
match nextP
	// find candidates where nextP.C port is driven (maybe partially) by chain's tail DSP.P port
	//      and with no registers in between (since cascade path cannot be pipelined)

	// reg C must not be used
	select port(nextP, \C_BYPASS, SigSpec()).is_fully_ones()

	// must be same DSP type
	select nextP->type.in(\MACC_PA)

	// port C should be driven by something
	select nusers(port(nextP, \C, SigSpec())) > 1

	// CIN must be unused
	select nusers(port(nextP, \PCIN, SigSpec())) == 0

	// should not have internal feedback connection
	select port(nextP, \CDIN_FDBK_SEL, SigSpec()).is_fully_zero()

	// SHIFT should be unused
	select port(nextP, \ARSHFT17_BYPASS).is_fully_ones()
	select port(nextP, \ARSHFT17).is_fully_zero()
	select nusers(port(nextP, \ARSHFT17, SigSpec())) == 0

	// current DSP cell can be cascaded with the back of the cascade chain
	// index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[17]
	filter port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[17]

	// semioptional

	optional
endmatch

code next
	next = nextP;

	// keep DSP type consistent in the chain
	// currently since we only have one type anyways, this line is always false
	if (next && next->type != first->type) reject;

	// break infinite recursion when there's a combinational loop
	if (visited.count(next) > 0) reject;

endcode

// (3) Recursively go to (2) until no more matches possible, recording the
//     longest possible chain
code
	if (next) {
		SigSpec driver_sigP = port(std::get<0>(chain.back()), \P);
		int shift = 0;
		if (port(next, \C)[0] == port(std::get<0>(chain.back()), \P)[17]) shift = 17;

		chain.emplace_back(next, shift);
		visited.insert(next);
		
		SigSpec sigC = unextend(port(next, \C));

		// Make sure driverDSP.P === DSP.C
		if (GetSize(sigC) + shift <= GetSize(driver_sigP) && driver_sigP.extract(shift, GetSize(sigC)) == sigC)
		{
			subpattern(tail);
		}
	} else {
		if (GetSize(chain) > GetSize(longest_chain))
			longest_chain = chain;
	}
finally
	if (next)
	{
		visited.erase(next);
		chain.pop_back();
	}
		

endcode