CIRCT 24.0.0git
Loading...
Searching...
No Matches
FirRegLowering.h
Go to the documentation of this file.
1//===- FirRegLowering.h - FirReg lowering utilities ===========--*- C++ -*-===//
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// NOLINTNEXTLINE(llvm-header-guard)
10#ifndef CONVERSION_SEQTOSV_FIRREGLOWERING_H
11#define CONVERSION_SEQTOSV_FIRREGLOWERING_H
12
18#include "circt/Support/LLVM.h"
21#include "mlir/IR/Attributes.h"
22#include "llvm/ADT/SmallPtrSet.h"
23
24namespace circt {
25
26using namespace hw;
27// This class computes the set of muxes that are reachable from an op.
28// The heuristic propagates the reachability only through the 3 ops, mux,
29// array_create and array_get. All other ops block the reachability.
30// This analysis is built lazily on every query.
31// The query: is a mux is reachable from a reg, results in a DFS traversal
32// of the IR rooted at the register. This traversal is completed and the
33// result is cached in a Map, for faster retrieval on any future query of any
34// op in this subgraph.
36public:
37 ReachableMuxes(HWModuleOp m) : module(m) {}
38
39 bool isMuxReachableFrom(seq::FirRegOp regOp, comb::MuxOp muxOp);
40
41private:
42 void buildReachabilityFrom(Operation *startNode);
43 HWModuleOp module;
44 llvm::DenseMap<Operation *, llvm::SmallDenseSet<Operation *>> reachableMuxes;
45 llvm::SmallPtrSet<Operation *, 16> visited;
46};
47
48// The op and its users information that needs to be tracked on the stack
49// for an iterative DFS traversal.
50struct OpUserInfo {
51 Operation *op;
53 llvm::filter_iterator<Operation::user_iterator,
54 std::function<bool(const Operation *)>>;
55
57 static std::function<bool(const Operation *op)> opAllowsReachability;
58
59 OpUserInfo(Operation *op)
60 : op(op), userIter(op->getUsers().begin(), op->getUsers().end(),
62 userEnd(op->getUsers().end(), op->getUsers().end(),
64
66 if (unvisited) {
67 unvisited = false;
68 return true;
69 }
70 return false;
71 }
72
73private:
74 bool unvisited = true;
75};
76
77/// Lower FirRegOp to `sv.reg` and `sv.always`.
79public:
80 /// A map sending registers to their paths.
81 using PathTable = DenseMap<seq::FirRegOp, hw::HierPathOp>;
82
83 /// When a register is buried under an ifdef op, the initialization code at
84 /// the footer of the HW module will refer to the register using a
85 /// hierarchical path op. This function creates any necessary hier paths and
86 /// returns a map from buried registers to their hier path ops. HierPathOps
87 /// creation needs to be serialized to keep the symbol creation deterministic,
88 /// so this is done as a pre-pass on the entire MLIR module. The result should
89 /// be passed in to each invocation of FirRefLowering.
90 static PathTable createPaths(mlir::ModuleOp top);
91
92 FirRegLowering(TypeConverter &typeConverter, hw::HWModuleOp module,
93 const PathTable &pathTable,
94 bool disableRegRandomization = false,
95 bool emitSeparateAlwaysBlocks = false,
96 bool emitPresetAsInlineInit = true);
97
98 void lower();
99 bool needsRegRandomization() const { return needsRandom; }
100
102
103private:
104 /// The conditions under which a register is defined.
106 enum Kind {
107 /// The register is under an ifdef "then" branch.
109 /// The register is under an ifdef "else" branch.
111 };
112 RegCondition(Kind kind, sv::MacroIdentAttr macro) : data(macro, kind) {}
113 Kind getKind() const { return data.getInt(); }
114 sv::MacroIdentAttr getMacro() const {
115 return cast<sv::MacroIdentAttr>(data.getPointer());
116 }
117 llvm::PointerIntPair<Attribute, 1, Kind> data;
118 };
119
122 hw::HierPathOp path;
123 IntegerAttr preset;
126 int64_t randStart;
127 size_t width;
128 };
129
130 void lowerUnderIfDef(sv::IfDefOp ifDefOp);
131 void lowerInBlock(Block *block);
132 void lowerReg(seq::FirRegOp reg);
133 void createInitialBlock();
134
135 void initialize(OpBuilder &builder, RegLowerInfo reg, ArrayRef<Value> rands);
136 void initializeRegisterElements(Location loc, OpBuilder &builder, Value reg,
137 Value rand, unsigned &pos);
138
139 void createTree(OpBuilder &builder, Value reg, Value term, Value next);
140 std::optional<std::tuple<Value, Value, Value>>
141 tryRestoringSubaccess(OpBuilder &builder, Value reg, Value term,
142 hw::ArrayCreateOp nextRegValue);
143
144 void addToAlwaysBlock(Block *block, sv::EventControl clockEdge, Value clock,
145 const std::function<void(OpBuilder &)> &body,
146 sv::ResetType resetStyle = {},
147 sv::EventControl resetEdge = {}, Value reset = {},
148 const std::function<void(OpBuilder &)> &resetBody = {});
149
150 void addToIfBlock(OpBuilder &builder, Value cond,
151 const std::function<void()> &trueSide,
152 const std::function<void()> &falseSide);
153
154 SmallVector<Value> createRandomizationVector(OpBuilder &builder,
155 Location loc);
156 void createRandomInitialization(ImplicitLocOpBuilder &builder);
157 void createPresetInitialization(ImplicitLocOpBuilder &builder);
158 void createAsyncResetInitialization(ImplicitLocOpBuilder &builder);
159
160 hw::ConstantOp getOrCreateConstant(Location loc, const APInt &value) {
161 OpBuilder builder(module.getBody());
162 auto &constant = constantCache[value];
163 if (constant) {
164 constant->setLoc(builder.getFusedLoc({constant->getLoc(), loc}));
165 return constant;
166 }
167
168 constant = hw::ConstantOp::create(builder, loc, value);
169 return constant;
170 }
171
172 /// Recreate the ifdefs under which `reg` was defined. Leave the builder with
173 /// its insertion point inside the created ifdef guards.
174 void buildRegConditions(OpBuilder &b, sv::RegOp reg);
175
176 using AlwaysKeyType = std::tuple<Block *, sv::EventControl, Value,
177 sv::ResetType, sv::EventControl, Value>;
180
181 using IfKeyType = std::pair<Block *, Value>;
183
186
187 /// The ambient ifdef conditions we have encountered while lowering.
188 std::vector<RegCondition> conditions;
189
190 /// A list of registers discovered, bucketed by initialization style.
191 SmallVector<RegLowerInfo> randomInitRegs, presetInitRegs;
192
193 /// A map from RegOps to the ifdef conditions under which they are defined.
194 /// We only bother recording a list of conditions if there is at least one.
195 DenseMap<sv::RegOp, std::vector<RegCondition>> regConditionTable;
196
197 /// A map from async reset signal to the registers that use it.
199
200 std::unique_ptr<ReachableMuxes> reachableMuxes;
201
203 TypeConverter &typeConverter;
204 hw::HWModuleOp module;
205
209
210 bool needsRandom = false;
211};
212} // namespace circt
213
214#endif // CONVERSION_SEQTOSV_FIRREGLOWERING_H
Lower FirRegOp to sv.reg and sv.always.
std::unique_ptr< ReachableMuxes > reachableMuxes
bool needsRegRandomization() const
void initialize(OpBuilder &builder, RegLowerInfo reg, ArrayRef< Value > rands)
llvm::SmallDenseMap< std::pair< Value, unsigned >, Value > arrayIndexCache
void createAsyncResetInitialization(ImplicitLocOpBuilder &builder)
llvm::SmallDenseMap< IfKeyType, sv::IfOp > ifCache
static PathTable createPaths(mlir::ModuleOp top)
When a register is buried under an ifdef op, the initialization code at the footer of the HW module w...
DenseMap< seq::FirRegOp, hw::HierPathOp > PathTable
A map sending registers to their paths.
void addToIfBlock(OpBuilder &builder, Value cond, const std::function< void()> &trueSide, const std::function< void()> &falseSide)
std::optional< std::tuple< Value, Value, Value > > tryRestoringSubaccess(OpBuilder &builder, Value reg, Value term, hw::ArrayCreateOp nextRegValue)
llvm::SmallDenseMap< APInt, hw::ConstantOp > constantCache
void createRandomInitialization(ImplicitLocOpBuilder &builder)
void lowerUnderIfDef(sv::IfDefOp ifDefOp)
void lowerInBlock(Block *block)
void buildRegConditions(OpBuilder &b, sv::RegOp reg)
Recreate the ifdefs under which reg was defined.
const PathTable & pathTable
void lowerReg(seq::FirRegOp reg)
std::pair< Block *, Value > IfKeyType
SmallVector< Value > createRandomizationVector(OpBuilder &builder, Location loc)
std::vector< RegCondition > conditions
The ambient ifdef conditions we have encountered while lowering.
void createTree(OpBuilder &builder, Value reg, Value term, Value next)
void createPresetInitialization(ImplicitLocOpBuilder &builder)
hw::ConstantOp getOrCreateConstant(Location loc, const APInt &value)
void addToAlwaysBlock(Block *block, sv::EventControl clockEdge, Value clock, const std::function< void(OpBuilder &)> &body, sv::ResetType resetStyle={}, sv::EventControl resetEdge={}, Value reset={}, const std::function< void(OpBuilder &)> &resetBody={})
SmallVector< RegLowerInfo > randomInitRegs
A list of registers discovered, bucketed by initialization style.
std::tuple< Block *, sv::EventControl, Value, sv::ResetType, sv::EventControl, Value > AlwaysKeyType
llvm::MapVector< Value, SmallVector< RegLowerInfo > > asyncResets
A map from async reset signal to the registers that use it.
void initializeRegisterElements(Location loc, OpBuilder &builder, Value reg, Value rand, unsigned &pos)
DenseMap< sv::RegOp, std::vector< RegCondition > > regConditionTable
A map from RegOps to the ifdef conditions under which they are defined.
TypeConverter & typeConverter
hw::HWModuleOp bool disableRegRandomization
SmallVector< RegLowerInfo > presetInitRegs
llvm::SmallDenseMap< AlwaysKeyType, std::pair< sv::AlwaysOp, sv::IfOp > > alwaysBlocks
void buildReachabilityFrom(Operation *startNode)
llvm::SmallPtrSet< Operation *, 16 > visited
HWModuleOp llvm::DenseMap< Operation *, llvm::SmallDenseSet< Operation * > > reachableMuxes
bool isMuxReachableFrom(seq::FirRegOp regOp, comb::MuxOp muxOp)
ReachableMuxes(HWModuleOp m)
create(data_type, value)
Definition hw.py:433
Definition sv.py:70
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
The conditions under which a register is defined.
sv::MacroIdentAttr getMacro() const
RegCondition(Kind kind, sv::MacroIdentAttr macro)
llvm::PointerIntPair< Attribute, 1, Kind > data
@ IfDefThen
The register is under an ifdef "then" branch.
@ IfDefElse
The register is under an ifdef "else" branch.
llvm::filter_iterator< Operation::user_iterator, std::function< bool(const Operation *)> > ValidUsersIterator
static std::function< bool(const Operation *op)> opAllowsReachability
OpUserInfo(Operation *op)
ValidUsersIterator userIter
ValidUsersIterator userEnd