CIRCT  20.0.0git
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 
15 #include "circt/Dialect/HW/HWOps.h"
22 #include "circt/Dialect/SV/SVOps.h"
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 
31 using namespace circt;
32 using namespace hw;
33 using namespace msft;
34 
35 TclEmitter::TclEmitter(mlir::ModuleOp topLevel)
36  : topLevel(topLevel), populated(false) {}
37 
38 LogicalResult 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 
74 Operation *TclEmitter::getDefinition(FlatSymbolRefAttr sym) {
75  if (failed(populate()))
76  return nullptr;
77  return topLevelSymbols.getDefinition(sym);
78 }
79 
80 // TODO: Currently assumes Stratix 10 and QuartusPro. Make more general.
81 namespace {
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).
84 struct 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 
130 void 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 
139 void 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 
147 void 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)"
175 LogicalResult
176 TclOutputState::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 
189 LogicalResult TclOutputState::emit(PDPhysLocationOp loc) {
190  if (failed(emitLocationAssignment(loc, loc.getLoc(), loc.getSubPath())))
191  return failure();
192  os << '\n';
193  return success();
194 }
195 
196 LogicalResult 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 
209 LogicalResult 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"
224 LogicalResult 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
241 LogicalResult 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.
294 LogicalResult 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")
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition: HWSymCache.h:56
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
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1
Definition: msft.py:1