CIRCT 20.0.0git
Loading...
Searching...
No Matches
ExportQuartusTcl.cpp
Go to the documentation of this file.
1//===- ExportQuartusTcl.cpp - Emit Quartus-flavored Tcl -------------------===//
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// Write out Tcl with the appropriate API calls for Quartus.
10//
11//===----------------------------------------------------------------------===//
12
24#include "circt/Support/LLVM.h"
25#include "mlir/IR/Builders.h"
26#include "mlir/Tools/mlir-translate/Translation.h"
27#include "llvm/ADT/StringSet.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/raw_ostream.h"
30
31using namespace circt;
32using namespace hw;
33using namespace msft;
34
35TclEmitter::TclEmitter(mlir::ModuleOp topLevel)
36 : topLevel(topLevel), populated(false) {}
37
38LogicalResult TclEmitter::populate() {
39 if (populated)
40 return success();
41
42 // Populated the symbol cache.
43 for (auto symOp : topLevel.getOps<mlir::SymbolOpInterface>())
44 if (auto name = symOp.getNameAttr())
45 topLevelSymbols.addDefinition(name, symOp);
47 populated = true;
48
49 // Bin any operations we may need to emit based on the root module in the
50 // instance hierarchy path and the potential instance name.
51
52 // Look in InstanceHierarchyOps to get the instance named ones.
53 for (auto hier : topLevel.getOps<InstanceHierarchyOp>()) {
54 Operation *mod = topLevelSymbols.getDefinition(hier.getTopModuleRefAttr());
55 auto &tclOps = tclOpsForModInstance[mod][hier.getInstNameAttr()];
56 for (auto tclOp : hier.getOps<DynInstDataOpInterface>()) {
57 assert(tclOp.getTopModule(topLevelSymbols) == mod &&
58 "Referenced mod does does not match");
59 tclOps.push_back(tclOp);
60 }
61 }
62
63 // Locations at the global scope are assumed to refer to the module without an
64 // instance.
65 for (auto tclOp : topLevel.getOps<DynInstDataOpInterface>()) {
66 Operation *mod = tclOp.getTopModule(topLevelSymbols);
67 assert(mod && "Must be able to resolve top module");
68 tclOpsForModInstance[mod][{}].push_back(tclOp);
69 }
70
71 return success();
72}
73
74Operation *TclEmitter::getDefinition(FlatSymbolRefAttr sym) {
75 if (failed(populate()))
76 return nullptr;
78}
79
80// TODO: Currently assumes Stratix 10 and QuartusPro. Make more general.
81namespace {
82/// Utility struct to assist in output and track other relevent state which are
83/// not specific to the entity hierarchy (global WRT to the entity hierarchy).
84struct TclOutputState {
85 TclOutputState(TclEmitter &emitter, llvm::raw_ostream &os)
86 : os(os), emitter(emitter) {}
87
88 llvm::raw_ostream &os;
89 llvm::raw_ostream &indent() {
90 os.indent(2);
91 return os;
92 };
93
94 TclEmitter &emitter;
95 SmallVector<Attribute> symbolRefs;
96
97 void emit(PhysLocationAttr);
98 LogicalResult emitLocationAssignment(UnaryDynInstDataOpInterface refOp,
99 PhysLocationAttr,
100 std::optional<StringRef> subpath);
101
102 LogicalResult emit(PDPhysRegionOp region);
103 LogicalResult emit(PDPhysLocationOp loc);
104 LogicalResult emit(PDRegPhysLocationOp);
105 LogicalResult emit(DynamicInstanceVerbatimAttrOp attr);
106 LogicalResult emit(PDMulticycleOp op);
107
108 void emitPath(hw::HierPathOp ref, std::optional<StringRef> subpath);
109 void emitInnerRefPart(hw::InnerRefAttr innerRef);
110
111 /// Get the HierPathOp to which the given operation is pointing. Add it to
112 /// the set of used global refs.
113 HierPathOp getRefOp(UnaryDynInstDataOpInterface op) {
114 return getRefOp(op.getLoc(), op.getPathSym());
115 }
116
117 /// Get the HierPathOp to which a given value is pointing. Add it to the
118 /// set of used global refs.
119 HierPathOp getRefOp(Location loc, FlatSymbolRefAttr pathSym) {
120 auto ref = dyn_cast_or_null<hw::HierPathOp>(emitter.getDefinition(pathSym));
121 if (ref)
122 emitter.usedRef(ref);
123 else
124 emitError(loc, "could not find hw.hierpath named ") << pathSym;
125 return ref;
126 }
127};
128} // anonymous namespace
129
130void TclOutputState::emitInnerRefPart(hw::InnerRefAttr innerRef) {
131 // We append new symbolRefs to the state, so s.symbolRefs.size() is the
132 // index of the InnerRefAttr we are about to add.
133 os << "{{" << symbolRefs.size() << "}}";
134
135 // Append a new inner reference for the template above.
136 symbolRefs.push_back(innerRef);
137}
138
139void TclOutputState::emitPath(hw::HierPathOp ref,
140 std::optional<StringRef> subpath) {
141 os << "{{" << symbolRefs.size() << ":|}}";
142 symbolRefs.push_back(FlatSymbolRefAttr::get(ref));
143 if (subpath)
144 os << subpath;
145}
146
147void TclOutputState::emit(PhysLocationAttr pla) {
148 // Different devices have different 'number' letters (the 'N' in 'N0'). M20Ks
149 // and DSPs happen to have the same one, probably because they never co-exist
150 // at the same location.
151 char numCharacter;
152 switch (pla.getPrimitiveType().getValue()) {
153 case PrimitiveType::M20K:
154 os << "M20K";
155 numCharacter = 'N';
156 break;
157 case PrimitiveType::DSP:
158 os << "MPDSP";
159 numCharacter = 'N';
160 break;
161 case PrimitiveType::FF:
162 os << "FF";
163 numCharacter = 'N';
164 break;
165 }
166
167 // Write out the rest of the location info.
168 os << "_X" << pla.getX() << "_Y" << pla.getY() << "_" << numCharacter
169 << pla.getNum();
170}
171
172/// Emit tcl in the form of:
173/// "set_location_assignment MPDSP_X34_Y285_N0 -to
174/// $parent|fooInst|entityName(subpath)"
175LogicalResult
176TclOutputState::emitLocationAssignment(UnaryDynInstDataOpInterface refOp,
177 PhysLocationAttr loc,
178 std::optional<StringRef> subpath) {
179 indent() << "set_location_assignment ";
180 emit(loc);
181
182 // To which entity does this apply?
183 os << " -to $parent|";
184 emitPath(getRefOp(refOp), subpath);
185
186 return success();
187}
188
189LogicalResult TclOutputState::emit(PDPhysLocationOp loc) {
190 if (failed(emitLocationAssignment(loc, loc.getLoc(), loc.getSubPath())))
191 return failure();
192 os << '\n';
193 return success();
194}
195
196LogicalResult TclOutputState::emit(PDRegPhysLocationOp locs) {
197 ArrayRef<PhysLocationAttr> locArr = locs.getLocs().getLocs();
198 for (size_t i = 0, e = locArr.size(); i < e; ++i) {
199 PhysLocationAttr pla = locArr[i];
200 if (!pla)
201 continue;
202 if (failed(emitLocationAssignment(locs, pla, {})))
203 return failure();
204 os << "[" << i << "]\n";
205 }
206 return success();
207}
208
209LogicalResult TclOutputState::emit(PDMulticycleOp op) {
210 indent() << "set_multicycle_path ";
211 os << "-hold 1 ";
212 os << "-setup " << op.getCycles() << " ";
213 os << "-from [get_registers {$parent|";
214 emitPath(getRefOp(op.getLoc(), op.getSourceAttr()), std::nullopt);
215 os << "}] ";
216 os << "-to [get_registers {$parent|";
217 emitPath(getRefOp(op.getLoc(), op.getDestAttr()), std::nullopt);
218 os << "}]\n";
219 return success();
220}
221
222/// Emit tcl in the form of:
223/// "set_global_assignment -name NAME VALUE -to $parent|fooInst|entityName"
224LogicalResult TclOutputState::emit(DynamicInstanceVerbatimAttrOp attr) {
225 HierPathOp ref = getRefOp(attr);
226 indent() << "set_instance_assignment -name " << attr.getName() << " "
227 << attr.getValue();
228
229 // To which entity does this apply?
230 os << " -to $parent|";
231 emitPath(ref, attr.getSubPath());
232 os << '\n';
233 return success();
234}
235
236/// Emit tcl in the form of:
237/// set_instance_assignment -name PLACE_REGION "X1 Y1 X20 Y20" -to $parent|a|b|c
238/// set_instance_assignment -name RESERVE_PLACE_REGION OFF -to $parent|a|b|c
239/// set_instance_assignment -name CORE_ONLY_PLACE_REGION ON -to $parent|a|b|c
240/// set_instance_assignment -name REGION_NAME test_region -to $parent|a|b|c
241LogicalResult TclOutputState::emit(PDPhysRegionOp region) {
242 HierPathOp ref = getRefOp(region);
243
244 auto physicalRegion = dyn_cast_or_null<DeclPhysicalRegionOp>(
245 emitter.getDefinition(region.getPhysRegionRefAttr()));
246 if (!physicalRegion)
247 return region.emitOpError(
248 "could not find physical region declaration named ")
249 << region.getPhysRegionRefAttr();
250
251 // PLACE_REGION directive.
252 indent() << "set_instance_assignment -name PLACE_REGION \"";
253 auto physicalBounds =
254 physicalRegion.getBounds().getAsRange<PhysicalBoundsAttr>();
255 llvm::interleave(
256 physicalBounds, os,
257 [&](PhysicalBoundsAttr bounds) {
258 os << 'X' << bounds.getXMin() << ' ';
259 os << 'Y' << bounds.getYMin() << ' ';
260 os << 'X' << bounds.getXMax() << ' ';
261 os << 'Y' << bounds.getYMax();
262 },
263 ";");
264 os << '"';
265
266 os << " -to $parent|";
267 emitPath(ref, region.getSubPath());
268 os << '\n';
269
270 // RESERVE_PLACE_REGION directive.
271 indent() << "set_instance_assignment -name RESERVE_PLACE_REGION OFF";
272 os << " -to $parent|";
273 emitPath(ref, region.getSubPath());
274 os << '\n';
275
276 // CORE_ONLY_PLACE_REGION directive.
277 indent() << "set_instance_assignment -name CORE_ONLY_PLACE_REGION ON";
278 os << " -to $parent|";
279 emitPath(ref, region.getSubPath());
280 os << '\n';
281
282 // REGION_NAME directive.
283 indent() << "set_instance_assignment -name REGION_NAME ";
284 os << physicalRegion.getName();
285 os << " -to $parent|";
286 emitPath(ref, region.getSubPath());
287 os << '\n';
288 return success();
289}
290
291/// Write out all the relevant tcl commands. Create one 'proc' per module which
292/// takes the parent entity name since we don't assume that the created module
293/// is the top level for the entire design.
294LogicalResult TclEmitter::emit(Operation *hwMod, StringRef outputFile) {
295 if (failed(populate()))
296 return failure();
297
298 // Build up the output Tcl, tracking symbol references in state.
299 std::string s;
300 llvm::raw_string_ostream os(s);
301 TclOutputState state(*this, os);
302
303 // Iterate through all the "instances" for 'hwMod' and produce a tcl proc for
304 // each one.
305 for (const auto &tclOpsForInstancesKV : tclOpsForModInstance[hwMod]) {
306 StringAttr instName = tclOpsForInstancesKV.first;
307 os << "proc {{" << state.symbolRefs.size() << "}}";
308 if (instName)
309 os << '_' << instName.getValue();
310 os << "_config { parent } {\n";
311 state.symbolRefs.push_back(SymbolRefAttr::get(hwMod));
312
313 // Loop through the ops relevant to the specified root module "instance".
314 LogicalResult ret = success();
315 const auto &tclOpsForMod = tclOpsForInstancesKV.second;
316 for (Operation *tclOp : tclOpsForMod) {
317 LogicalResult rc =
318 TypeSwitch<Operation *, LogicalResult>(tclOp)
319 .Case([&](PDPhysLocationOp op) { return state.emit(op); })
320 .Case([&](PDRegPhysLocationOp op) { return state.emit(op); })
321 .Case([&](PDPhysRegionOp op) { return state.emit(op); })
322 .Case([&](PDMulticycleOp op) { return state.emit(op); })
323 .Case([&](DynamicInstanceVerbatimAttrOp op) {
324 return state.emit(op);
325 })
326 .Default([](Operation *op) {
327 return op->emitOpError("could not determine how to output tcl");
328 });
329 if (failed(rc))
330 ret = failure();
331 }
332 os << "}\n\n";
333 }
334
335 // Create a verbatim op containing the Tcl and symbol references.
336 auto builder = ImplicitLocOpBuilder::atBlockEnd(
337 UnknownLoc::get(hwMod->getContext()), hwMod->getBlock());
338 builder.create<emit::FileOp>(outputFile, [&] {
339 builder.create<sv::VerbatimOp>(os.str(), ValueRange{},
340 builder.getArrayAttr(state.symbolRefs));
341 });
342
343 return success();
344}
assert(baseType &&"element must be base type")
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
Definition HWSymCache.h:75
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=~0ULL)
Definition HWSymCache.h:43
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition HWSymCache.h:56
Instantiate for all Tcl emissions.
Definition ExportTcl.h:29
Operation * getDefinition(FlatSymbolRefAttr)
hw::HWSymbolCache topLevelSymbols
Definition ExportTcl.h:42
mlir::ModuleOp topLevel
Definition ExportTcl.h:39
DenseMap< Operation *, llvm::MapVector< StringAttr, SmallVector< DynInstDataOpInterface, 0 > > > tclOpsForModInstance
Map Module operations to their top-level "instance" names.
Definition ExportTcl.h:48
LogicalResult emit(Operation *hwMod, StringRef outputFile)
Write out all the relevant tcl commands.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition emit.py:1
Definition hw.py:1
Definition msft.py:1