-
Notifications
You must be signed in to change notification settings - Fork 283
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Seq] Add a pass to implement firreg randomization
- Loading branch information
Showing
5 changed files
with
238 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
//===- FirregRandomization.cpp - Randomize initial values of registers --===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "circt/Dialect/Comb/CombOps.h" | ||
#include "circt/Dialect/HW/HWOps.h" | ||
#include "circt/Dialect/SV/SVOps.h" | ||
#include "circt/Dialect/Seq/SeqOps.h" | ||
#include "circt/Dialect/Seq/SeqPasses.h" | ||
#include "mlir/Dialect/Func/IR/FuncOps.h" | ||
#include "mlir/IR/ImplicitLocOpBuilder.h" | ||
#include "mlir/Pass/Pass.h" | ||
|
||
namespace circt { | ||
namespace seq { | ||
#define GEN_PASS_DEF_FIRREGRANDOMIZATION | ||
#include "circt/Dialect/Seq/SeqPasses.h.inc" | ||
} // namespace seq | ||
} // namespace circt | ||
|
||
using namespace circt; | ||
using namespace seq; | ||
using namespace hw; | ||
using namespace mlir; | ||
using namespace func; | ||
|
||
namespace { | ||
struct FirregRandomizationPass | ||
: public circt::seq::impl::FirregRandomizationBase< | ||
FirregRandomizationPass> { | ||
using FirregRandomizationBase< | ||
FirregRandomizationPass>::FirregRandomizationBase; | ||
void runOnOperation() override; | ||
void runOnModule(hw::HWModuleOp module, Operation *randomDecl); | ||
using FirregRandomizationBase<FirregRandomizationPass>::emitSV; | ||
}; | ||
} // anonymous namespace | ||
|
||
struct RegLowerInfo { | ||
CompRegOp compReg; | ||
int64_t randStart; | ||
size_t width; | ||
}; | ||
|
||
static Value initialize(OpBuilder &builder, RegLowerInfo reg, | ||
ArrayRef<Value> rands) { | ||
|
||
auto loc = reg.compReg.getLoc(); | ||
SmallVector<Value> nibbles; | ||
if (reg.width == 0) | ||
return builder.create<hw::ConstantOp>(loc, APInt(reg.width, 0)); | ||
|
||
uint64_t width = reg.width; | ||
uint64_t offset = reg.randStart; | ||
while (width) { | ||
auto index = offset / 32; | ||
auto start = offset % 32; | ||
auto nwidth = std::min(32 - start, width); | ||
auto elemVal = rands[index]; | ||
auto elem = | ||
builder.createOrFold<comb::ExtractOp>(loc, elemVal, start, nwidth); | ||
nibbles.push_back(elem); | ||
offset += nwidth; | ||
width -= nwidth; | ||
} | ||
auto concat = builder.createOrFold<comb::ConcatOp>(loc, nibbles); | ||
auto bitcast = builder.createOrFold<hw::BitcastOp>( | ||
loc, reg.compReg.getResult().getType(), concat); | ||
|
||
// Initialize register elements. | ||
return bitcast; | ||
} | ||
|
||
void FirregRandomizationPass::runOnOperation() { | ||
auto module = getOperation(); | ||
OpBuilder builder(module); | ||
|
||
builder.setInsertionPointToStart(module.getBody()); | ||
Operation *randomDecl; | ||
if (emitSV) { | ||
randomDecl = | ||
builder.create<sv::MacroDeclOp>(builder.getUnknownLoc(), "RANDOM"); | ||
} else { | ||
auto funcType = builder.getFunctionType({}, {builder.getIntegerType(32)}); | ||
auto randomFunc = builder.create<func::FuncOp>(builder.getUnknownLoc(), | ||
"random", funcType); | ||
randomFunc.setPrivate(); | ||
randomDecl = randomFunc; | ||
} | ||
|
||
for (auto hwModule : module.getBody()->getOps<hw::HWModuleOp>()) | ||
runOnModule(hwModule, randomDecl); | ||
} | ||
|
||
void FirregRandomizationPass::runOnModule(hw::HWModuleOp module, | ||
Operation *randomDecl) { | ||
SmallVector<RegLowerInfo> regs; | ||
for (auto reg : module.getOps<seq::CompRegOp>()) { | ||
// If it has an initial value, we don't randomize it. | ||
if (reg.getInitialValue()) | ||
continue; | ||
|
||
RegLowerInfo info; | ||
info.compReg = reg; | ||
info.width = hw::getBitWidth(reg.getType()); | ||
|
||
// If it has a random init start attribute, we randomize it. | ||
if (auto attr = reg->getAttrOfType<IntegerAttr>("firrtl.random_init_start")) | ||
info.randStart = attr.getInt(); | ||
else | ||
info.randStart = -1; | ||
|
||
regs.push_back(info); | ||
} | ||
|
||
// Compute total width of random space. Place non-chisel registers at the end | ||
// of the space. The Random space is unique to the initial block, due to | ||
// verilog thread rules, so we can drop trailing random calls if they are | ||
// unused. | ||
uint64_t maxBit = 0; | ||
for (auto reg : regs) | ||
if (reg.randStart >= 0) | ||
maxBit = std::max(maxBit, (uint64_t)reg.randStart + reg.width); | ||
|
||
for (auto ® : regs) { | ||
if (reg.randStart == -1) { | ||
reg.randStart = maxBit; | ||
maxBit += reg.width; | ||
} | ||
} | ||
|
||
auto builder = ImplicitLocOpBuilder::atBlockTerminator(module.getLoc(), | ||
module.getBodyBlock()); | ||
|
||
SmallVector<Type> resultTypes; | ||
for (auto reg : regs) | ||
resultTypes.push_back(reg.compReg.getResult().getType()); | ||
|
||
auto loc = module.getLoc(); | ||
|
||
auto init = builder.create<seq::InitialOp>(resultTypes, [&] { | ||
SmallVector<Value> initValues; | ||
|
||
// Create randomization vector | ||
SmallVector<Value> randValues; | ||
auto numRandomCalls = (maxBit + 31) / 32; | ||
if (emitSV) { | ||
if (!regs.empty()) { | ||
builder.create<sv::VerbatimOp>("`INIT_RANDOM_PROLOG_"); | ||
for (uint64_t x = 0; x < numRandomCalls; ++x) { | ||
auto rand = builder.create<sv::MacroRefExprSEOp>( | ||
loc, builder.getIntegerType(32), "RANDOM"); | ||
randValues.push_back(rand); | ||
} | ||
}; | ||
} else { | ||
// Native function. Create func.call | ||
for (uint64_t x = 0; x < numRandomCalls; ++x) { | ||
randValues.push_back( | ||
builder | ||
.create<mlir::func::CallOp>(loc, cast<func::FuncOp>(randomDecl)) | ||
.getResult(0)); | ||
} | ||
} | ||
// Create initialisers for all registers. | ||
for (auto &svReg : regs) | ||
initValues.push_back(::initialize(builder, svReg, randValues)); | ||
builder.create<seq::YieldOp>(initValues); | ||
}); | ||
|
||
for (auto [reg, init] : llvm::zip(regs, init.getResults())) { | ||
reg.compReg.getInitialValueMutable().assign(init); | ||
} | ||
} | ||
|
||
std::unique_ptr<Pass> circt::seq::createFirregRandomizationPass( | ||
const FirregRandomizationOptions &options) { | ||
return std::make_unique<FirregRandomizationPass>(options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// RUN: circt-opt %s -verify-diagnostics -seq-firreg-randomization | FileCheck %s --check-prefixes=COMMON,CHECK | ||
// RUN: circt-opt %s -verify-diagnostics --pass-pipeline='builtin.module(seq-firreg-randomization{emit-sv=true})' | FileCheck %s --check-prefixes=COMMON,SV | ||
sv.macro.decl @INIT_RANDOM_PROLOG_ | ||
hw.module @top(in %clk: !seq.clock, in %rst: i1, in %i: i18, out o: i18, out j: i18) { | ||
%c0_i18 = hw.constant 0 : i18 | ||
%r0 = seq.compreg %i, %clk reset %rst, %c0_i18 : i18 | ||
%r1 = seq.compreg %i, %clk : i18 | ||
// COMMON: %r0 = seq.compreg %i, %clk reset %rst, %c0_i18 initial %0#0 : i18 | ||
// COMMON-NEXT: %r1 = seq.compreg %i, %clk initial %0#1 : i18 | ||
// COMMON-NEXT: %0:2 = seq.initial { | ||
// CHECK-NEXT: %[[RAND1:.+]] = func.call @random() : () -> i32 | ||
// CHECK-NEXT: %[[RAND2:.+]] = func.call @random() : () -> i32 | ||
// CHECK-NEXT: %[[EXTRACT1:.+]] = comb.extract %[[RAND1]] from 0 : (i32) -> i18 | ||
// CHECK-NEXT: %[[EXTRACT2:.+]] = comb.extract %[[RAND1]] from 18 : (i32) -> i14 | ||
// CHECK-NEXT: %[[EXTRACT3:.+]] = comb.extract %[[RAND2]] from 0 : (i32) -> i4 | ||
// CHECK-NEXT: %[[CONCAT:.+]] = comb.concat %[[EXTRACT2]], %[[EXTRACT3]] : i14, i4 | ||
// CHECK-NEXT: seq.yield %[[EXTRACT1]], %[[CONCAT]] : i18, i18 | ||
|
||
// SV-NEXT: sv.verbatim "`INIT_RANDOM_PROLOG_" | ||
// SV-NEXT: %RANDOM = sv.macro.ref.se @RANDOM() : () -> i32 | ||
// SV-NEXT: %RANDOM_0 = sv.macro.ref.se @RANDOM() : () -> i32 | ||
// SV-NEXT: %[[EXTRACT1:.+]] = comb.extract %RANDOM from 0 : (i32) -> i18 | ||
// SV-NEXT: %[[EXTRACT2:.+]] = comb.extract %RANDOM from 18 : (i32) -> i14 | ||
// SV-NEXT: %[[EXTRACT3:.+]] = comb.extract %RANDOM_0 from 0 : (i32) -> i4 | ||
// SV-NEXT: %[[CONCAT:.+]] = comb.concat %[[EXTRACT2]], %[[EXTRACT3]] : i14, i4 | ||
// SV-NEXT: seq.yield %[[EXTRACT1]], %[[CONCAT]] : i18, i18 | ||
|
||
// COMMON: } : !seq.immutable<i18>, !seq.immutable<i18> | ||
|
||
hw.output %r0, %r1: i18, i18 | ||
} |