CIRCT 24.0.0git
Loading...
Searching...
No Matches
DatapathOps.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements datapath ops.
10//
11//===----------------------------------------------------------------------===//
12
16#include "llvm/Support/Debug.h"
17#include "llvm/Support/Format.h"
18#include "llvm/Support/KnownBits.h"
19
20#define DEBUG_TYPE "datapath-ops"
21
22using namespace circt;
23using namespace datapath;
24
25LogicalResult CompressOp::verify() {
26 // The compressor must reduce the number of operands by at least 1 otherwise
27 // it fails to perform any reduction.
28 if (getNumOperands() < 3)
29 return emitOpError("requires 3 or more arguments - otherwise use add");
30
31 if (getNumResults() >= getNumOperands())
32 return emitOpError("must reduce the number of operands by at least 1");
33
34 if (getNumResults() < 2)
35 return emitOpError("must produce at least 2 results");
36
37 return success();
38}
39
40// Parser for the custom type format
41// Parser for "<input-type> [<num-inputs> -> <num-outputs>]"
42static ParseResult parseCompressFormat(OpAsmParser &parser,
43 SmallVectorImpl<Type> &inputTypes,
44 SmallVectorImpl<Type> &resultTypes) {
45
46 int64_t inputCount, resultCount;
47 Type inputElementType;
48
49 if (parser.parseType(inputElementType) || parser.parseLSquare() ||
50 parser.parseInteger(inputCount) || parser.parseArrow() ||
51 parser.parseInteger(resultCount) || parser.parseRSquare())
52 return failure();
53
54 // Inputs and results have same type
55 inputTypes.assign(inputCount, inputElementType);
56 resultTypes.assign(resultCount, inputElementType);
57
58 return success();
59}
60
61// Printer for "<input-type> [<num-inputs> -> <num-outputs>]"
62static void printCompressFormat(OpAsmPrinter &printer, Operation *op,
63 TypeRange inputTypes, TypeRange resultTypes) {
64
65 printer << inputTypes[0] << " [" << inputTypes.size() << " -> "
66 << resultTypes.size() << "]";
67}
68
69//===----------------------------------------------------------------------===//
70// Compressor Tree Logic.
71//===----------------------------------------------------------------------===//
72
73// Construct a full adder for three 1-bit inputs.
74std::pair<CompressorBit, CompressorBit>
77
78 auto aXorB = builder.createOrFold<comb::XorOp>(loc, a.val, b.val, true);
79 Value sumVal = builder.createOrFold<comb::XorOp>(loc, aXorB, c.val, true);
80
81 auto carryVal = builder.createOrFold<comb::OrOp>(
82 loc,
83 ArrayRef<Value>{
84 builder.createOrFold<comb::AndOp>(loc, a.val, b.val, true),
85 builder.createOrFold<comb::AndOp>(loc, aXorB, c.val, true)},
86 true);
87
88 auto sumDelay = std::max(std::max(a.delay, b.delay) + 1, c.delay) + 1;
89 auto carryDelay = sumDelay + 1;
90
91 CompressorBit sum = {sumVal, sumDelay};
92 CompressorBit carry = {carryVal, carryDelay};
93 std::pair<CompressorBit, CompressorBit> fa{sum, carry};
95 return fa;
96}
97
98// Construct a half adder for two 1-bit inputs.
99std::pair<CompressorBit, CompressorBit>
101 CompressorBit b) {
102 auto sumVal = builder.createOrFold<comb::XorOp>(loc, a.val, b.val, true);
103 auto carryVal = builder.createOrFold<comb::AndOp>(loc, a.val, b.val, true);
104
105 auto sumDelay = std::max(a.delay, b.delay) + 1;
106 auto carryDelay = sumDelay;
107
108 CompressorBit sum = {sumVal, sumDelay};
109 CompressorBit carry = {carryVal, carryDelay};
110 std::pair<CompressorBit, CompressorBit> ha{sum, carry};
111 return ha;
112}
113
114// Map input rows to column representation
116 const SmallVector<SmallVector<Value>> &addends,
117 Location loc, OpBuilder &builder)
118 : columns(width), width(width), numStages(0), numFullAdders(0), loc(loc) {
119 assert(!addends.empty());
120
121 SmallVector<size_t> constantOnes(width, 0);
122
123 // Convert addends rows to columns
124 // Known bits analysis constructs a minimal array - skipping zeros
125 for (auto row : addends) {
126 // Number of bits in a row == bitwidth of input addends
127 // Compressors will be formed of uniform bitwidth addends
128 assert(row.size() == width);
129 for (size_t i = 0; i < width; ++i) {
130 auto knownBit = comb::computeKnownBits(row[i]);
131 if (knownBit.isZero())
132 continue;
133 if (knownBit.isAllOnes()) {
134 ++constantOnes[i];
135 continue;
136 }
137 // Add non-zero bit to the column
138 CompressorBit bit = {row[i], 0};
139 columns[i].push_back(bit);
140 }
141 }
142
143 // Fold constant one bits into a binary carry chain before tree reduction.
144 // Two ones in column i are equivalent to one carry into column i+1; `carry`
145 // tracks those propagated known-one bits from lower columns.
146 Value trueValue;
147 size_t carry = 0;
148 for (size_t i = 0; i < width; ++i) {
149 size_t ones = constantOnes[i] + carry;
150 if (ones % 2) {
151 if (!trueValue)
152 trueValue = hw::ConstantOp::create(builder, loc, APInt(1, 1));
153 columns[i].push_back(CompressorBit{trueValue, 0});
154 }
155 // Each pair of ones contributes a carry to the next column.
156 carry = ones / 2;
157 }
158}
159
160// Update the input delays based on longest path analysis
162 llvm::function_ref<FailureOr<int64_t>(Value)> getDelay) {
163 for (auto &column : columns) {
164 for (auto &[value, result] : column) {
165 auto delay = getDelay(value);
166 if (failed(delay))
167 return failure();
168 result = *delay;
169 }
170 }
171 return success();
172}
173
175 size_t maxSize = 0;
176 for (const auto &column : columns)
177 maxSize = std::max(maxSize, column.size());
178
179 return maxSize;
180}
181
182// Use Dadda's ALAP alogrithm to determine the target height of the next stage
183// https://en.wikipedia.org/wiki/Dadda_multiplier
185 auto maxHeight = getMaxHeight();
186 size_t mPrev = 2;
187 while (true) {
188 size_t m = static_cast<size_t>(std::floor(1.5 * mPrev));
189 if (m >= maxHeight)
190 return mPrev;
191 mPrev = m;
192 }
193}
194
195// Convert back to a concatenated addend representation
196SmallVector<Value> CompressorTree::columnsToAddends(OpBuilder &builder,
197 size_t targetHeight) {
198 SmallVector<Value> addend;
199 SmallVector<Value> addends;
200 auto falseValue = hw::ConstantOp::create(builder, loc, APInt(1, 0));
201 for (size_t i = 0; i < targetHeight; ++i) {
202 // Pad with zeros
203 if (i >= getMaxHeight()) {
204 addends.push_back(hw::ConstantOp::create(builder, loc, APInt(width, 0)));
205 continue;
206 }
207 // Otherwise populate a addend formed from a concatenation
208 for (size_t j = 0; j < width; ++j) {
209 if (i < columns[j].size())
210 addend.push_back(columns[j][i].val);
211 else {
212 addend.push_back(falseValue);
213 }
214 }
215 std::reverse(addend.begin(), addend.end());
216 addends.push_back(comb::ConcatOp::create(builder, loc, addend));
217 addend.clear();
218 }
219 return addends;
220}
221
222// Perform recursive compression until reduced to the target height
223SmallVector<Value> CompressorTree::compressToHeight(OpBuilder &builder,
224 size_t targetHeight) {
225
226 auto maxHeight = getMaxHeight();
227
228 if (maxHeight <= targetHeight)
229 return columnsToAddends(builder, targetHeight);
230
231 return compressUsingTiming(builder, targetHeight);
232}
233
234// Perform recursive compression using timing information until reduced to the
235// target height - this currently uses Dadda's algorithm and timing driven
236// signal selection
237// TODO: Dadda's algorithm is redundant here since it assumes uniform arrival so
238// need to implement a more timing driven approach
239SmallVector<Value> CompressorTree::compressUsingTiming(OpBuilder &builder,
240 size_t targetHeight) {
241 while (getMaxHeight() > targetHeight) {
242 LLVM_DEBUG(dump(););
243 // Increment the number of reduction stages for debugging/reporting
244 ++numStages;
245
246 // Use Dadda's algorithm to compute next stage height
247 auto targetStageHeight = getNextStageTargetHeight();
248 // Initialize empty newColumns
249 SmallVector<SmallVector<CompressorBit>> newColumns(width);
250
251 for (size_t i = 0; i < width; ++i) {
252 auto col = columns[i];
253
254 // Sort the column by arrival time - fastest at the end
255 std::stable_sort(
256 col.begin(), col.end(),
257 [](const auto &a, const auto &b) { return a.delay > b.delay; });
258 // Only compress to reach the target stage height - Dadda's Algorithm
259 while (col.size() + newColumns[i].size() > targetStageHeight) {
260 if (col.size() < 2) {
261 llvm::errs() << "CompressorTree: Not enough bits in column " << i
262 << " to compress further.\n New Columns size: "
263 << newColumns[i].size()
264 << ", Current Column size: " << col.size() << "\n";
265 llvm::report_fatal_error(
266 "Expected at least two bits in compressor column");
267 }
268
269 auto bit0 = col.pop_back_val();
270 auto bit1 = col.pop_back_val();
271
272 // If we have an additional bit we can apply a full adder
273 if (col.size() >= 1) {
274 // bit2 can arrive 1 delay unit after bit0 and bit1 without delaying
275 // the full-adder
276 auto targetDelay = std::max(bit0.delay, bit1.delay) + 1;
277 CompressorBit bit2;
278
279 // Find the third bit of the full-adder that satisfies the delay
280 // constraint
281 auto it = std::find_if(col.begin(), col.end(),
282 [targetDelay](const auto &pair) {
283 return pair.delay <= targetDelay;
284 });
285
286 if (it != col.end()) {
287 bit2 = *it;
288 col.erase(it);
289 } else {
290 // If no bit satisfies the delay constraint pick the fastest one
291 bit2 = col.pop_back_val();
292 }
293 auto [sum, carry] = fullAdderWithDelay(builder, bit0, bit1, bit2);
294
295 newColumns[i].push_back(sum);
296 if (i + 1 < newColumns.size())
297 newColumns[i + 1].push_back(carry);
298 } else {
299 // Apply a half adder to bit0 and bit1
300 auto [sum, carry] = halfAdderWithDelay(builder, bit0, bit1);
301
302 newColumns[i].push_back(sum);
303 if (i + 1 < newColumns.size())
304 newColumns[i + 1].push_back(carry);
305 }
306 }
307
308 // Pass through remaining bits
309 newColumns[i].append(col);
310 }
311
312 // Compute another stage of reduction
313 columns = std::move(newColumns);
314 }
315 LLVM_DEBUG(dump(););
316 return columnsToAddends(builder, targetHeight);
317}
318
320 llvm::dbgs() << "Compressor Tree: Height = " << getMaxHeight()
321 << ", Number of FA = " << numFullAdders
322 << ", Number of Stages = " << numStages
323 << ", Next Stage Target = " << getNextStageTargetHeight()
324 << "\n";
325 // Print column headers
326 llvm::dbgs() << std::string(9, ' ');
327 for (size_t j = width; j > 0; --j) {
328 if (j < width)
329 llvm::dbgs() << " ";
330 llvm::dbgs() << llvm::format("%02d", j - 1);
331 }
332 llvm::dbgs() << "\n"
333 << std::string(9, ' ') << std::string(width * 3, '-') << "\n";
334
335 for (size_t i = 0; i < getMaxHeight(); ++i) {
336 llvm::dbgs() << " [" << llvm::format("%02d", i) << "]: [";
337 for (size_t j = width; j > 0; --j) {
338 if (j < width)
339 llvm::dbgs() << " ";
340 if (i < columns[j - 1].size())
341 llvm::dbgs() << llvm::format(
342 "%02d",
343 columns[j - 1][i].delay); // Assumes CompressorBit has operator
344 else
345 llvm::dbgs() << " ";
346 }
347 llvm::dbgs() << "]\n";
348 }
349}
350
351//===----------------------------------------------------------------------===//
352// TableGen generated logic.
353//===----------------------------------------------------------------------===//
354
355// Provide the autogenerated implementation guts for the Op classes.
356#define GET_OP_CLASSES
357#include "circt/Dialect/Datapath/Datapath.cpp.inc"
assert(baseType &&"element must be base type")
static void printCompressFormat(OpAsmPrinter &printer, Operation *op, TypeRange inputTypes, TypeRange resultTypes)
static ParseResult parseCompressFormat(OpAsmParser &parser, SmallVectorImpl< Type > &inputTypes, SmallVectorImpl< Type > &resultTypes)
SmallVector< SmallVector< CompressorBit > > columns
Definition DatapathOps.h:58
SmallVector< Value > compressToHeight(OpBuilder &builder, size_t targetHeight)
SmallVector< Value > columnsToAddends(OpBuilder &builder, size_t targetHeight)
CompressorTree(size_t width, const SmallVector< SmallVector< Value > > &addends, Location loc, OpBuilder &builder)
LogicalResult withInputDelays(llvm::function_ref< FailureOr< int64_t >(Value)> getDelay)
std::pair< CompressorBit, CompressorBit > halfAdderWithDelay(OpBuilder &builder, CompressorBit a, CompressorBit b)
std::pair< CompressorBit, CompressorBit > fullAdderWithDelay(OpBuilder &builder, CompressorBit a, CompressorBit b, CompressorBit c)
SmallVector< Value > compressUsingTiming(OpBuilder &builder, size_t targetHeight)
create(data_type, value)
Definition hw.py:433
KnownBits computeKnownBits(Value value)
Compute "known bits" information about the specified value - the set of bits that are guaranteed to a...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.