Skip to content

Commit

Permalink
[Arc] Add InitialOp and lowering support for FirReg preset values. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
fzi-hielscher authored Aug 15, 2024
1 parent eacf554 commit 4201039
Show file tree
Hide file tree
Showing 21 changed files with 584 additions and 64 deletions.
2 changes: 1 addition & 1 deletion include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ def ConvertToArcs : Pass<"convert-to-arcs", "mlir::ModuleOp"> {
latency.
}];
let constructor = "circt::createConvertToArcsPass()";
let dependentDialects = ["circt::arc::ArcDialect"];
let dependentDialects = ["circt::arc::ArcDialect", "circt::hw::HWDialect"];
let options = [
Option<"tapRegisters", "tap-registers", "bool", "true",
"Make registers observable">,
Expand Down
78 changes: 52 additions & 26 deletions include/circt/Dialect/Arc/ArcOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ def StateOp : ArcOp<"state", [
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<ClockedOpInterface>,
PredOpTrait<"types of initial arguments match result types",
CPred<[{getInitials().empty() ||
llvm::equal(getInitials().getType(), getResults().getType())}]>>
]> {
let summary = "State transfer arc";

Expand All @@ -143,35 +146,40 @@ def StateOp : ArcOp<"state", [
Optional<I1>:$enable,
Optional<I1>:$reset,
I32Attr:$latency,
Variadic<AnyType>:$inputs);
Variadic<AnyType>:$inputs,
Variadic<AnyType>:$initials);
let results = (outs Variadic<AnyType>:$outputs);

let assemblyFormat = [{
$arc `(` $inputs `)` (`clock` $clock^)? (`enable` $enable^)?
(`reset` $reset^)? `latency` $latency attr-dict
`:` functional-type($inputs, results)
(`reset` $reset^)?
( `initial` ` ` `(` $initials^ `:` type($initials) `)`)?
`latency` $latency attr-dict `:` functional-type($inputs, results)
}];

let hasFolder = 1;
let hasCanonicalizeMethod = 1;

let builders = [
OpBuilder<(ins "DefineOp":$arc, "mlir::Value":$clock, "mlir::Value":$enable,
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs), [{
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials), [{
build($_builder, $_state, mlir::SymbolRefAttr::get(arc),
arc.getFunctionType().getResults(), clock, enable, latency,
inputs);
inputs, initials);
}]>,
OpBuilder<(ins "mlir::SymbolRefAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state, arc, results, clock, enable, Value(), latency,
inputs);
inputs, initials);
}]>,
OpBuilder<(ins "mlir::SymbolRefAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "mlir::Value":$reset,
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials
), [{
if (clock)
$_state.addOperands(clock);
Expand All @@ -180,30 +188,34 @@ def StateOp : ArcOp<"state", [
if (reset)
$_state.addOperands(reset);
$_state.addOperands(inputs);
$_state.addOperands(initials);
$_state.addAttribute("arc", arc);
$_state.addAttribute("latency", $_builder.getI32IntegerAttr(latency));
$_state.addAttribute(getOperandSegmentSizeAttr(),
$_builder.getDenseI32ArrayAttr({
clock ? 1 : 0,
enable ? 1 : 0,
reset ? 1 : 0,
static_cast<int32_t>(inputs.size())}));
static_cast<int32_t>(inputs.size()),
static_cast<int32_t>(initials.size())}));
$_state.addTypes(results);
}]>,
OpBuilder<(ins "mlir::StringAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state, mlir::SymbolRefAttr::get(arc), results, clock,
enable, latency, inputs);
enable, latency, inputs, initials);
}]>,
OpBuilder<(ins "mlir::StringRef":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state,
mlir::StringAttr::get($_builder.getContext(), arc),
results, clock, enable, latency, inputs);
results, clock, enable, latency, inputs, initials);
}]>
];
let skipDefaultBuilders = 1;
Expand Down Expand Up @@ -429,26 +441,37 @@ def ClockDomainOp : ArcOp<"clock_domain", [
let hasCanonicalizeMethod = 1;
}

def ClockTreeOp : ArcOp<"clock_tree", [NoTerminator, NoRegionArguments]> {
//===----------------------------------------------------------------------===//
// (Pseudo) Clock Trees
//===----------------------------------------------------------------------===//

class ClockTreeLikeOp<string mnemonic, list<Trait> traits = []>:
ArcOp<mnemonic, !listconcat(traits, [
RecursiveMemoryEffects, NoTerminator, NoRegionArguments, SingleBlock,
HasParent<"ModelOp">
])> {
let regions = (region SizedRegion<1>:$body);
}

def ClockTreeOp : ClockTreeLikeOp<"clock_tree"> {
let summary = "A clock tree";
let arguments = (ins I1:$clock);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = [{
$clock attr-dict-with-keyword $body
}];
let extraClassDeclaration = [{
mlir::Block &getBodyBlock() { return getBody().front(); }
}];
}

def PassThroughOp : ArcOp<"passthrough", [NoTerminator, NoRegionArguments]> {
def PassThroughOp : ClockTreeLikeOp<"passthrough"> {
let summary = "Clock-less logic that is on the pass-through path";
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = [{
attr-dict-with-keyword $body
}];
let extraClassDeclaration = [{
mlir::Block &getBodyBlock() { return getBody().front(); }
}

def InitialOp : ClockTreeLikeOp<"initial"> {
let summary = "Clock-less logic called at the start of simulation";
let assemblyFormat = [{
attr-dict-with-keyword $body
}];
}

Expand Down Expand Up @@ -651,19 +674,22 @@ def TapOp : ArcOp<"tap"> {
let assemblyFormat = [{ $value attr-dict `:` type($value) }];
}

def ModelOp : ArcOp<"model", [RegionKindInterface, IsolatedFromAbove,
NoTerminator, Symbol]> {
def ModelOp : ArcOp<"model", [
RegionKindInterface, IsolatedFromAbove, NoTerminator, Symbol,
DeclareOpInterfaceMethods<SymbolUserOpInterface>
]> {
let summary = "A model with stratified clocks";
let description = [{
A model with stratified clocks. The `io` optional attribute
specifies the I/O of the module associated to this model.
}];
let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttrOf<ModuleType>:$io);
TypeAttrOf<ModuleType>:$io,
OptionalAttr<FlatSymbolRefAttr>:$initialFn);
let regions = (region SizedRegion<1>:$body);

let assemblyFormat = [{
$sym_name `io` $io attr-dict-with-keyword $body
$sym_name `io` $io (`initializer` $initialFn^)? attr-dict-with-keyword $body
}];

let extraClassDeclaration = [{
Expand Down
6 changes: 4 additions & 2 deletions include/circt/Dialect/Arc/ModelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ struct ModelInfo {
std::string name;
size_t numStateBytes;
llvm::SmallVector<StateInfo> states;
mlir::FlatSymbolRefAttr initialFnSym;

ModelInfo(std::string name, size_t numStateBytes,
llvm::SmallVector<StateInfo> states)
llvm::SmallVector<StateInfo> states,
mlir::FlatSymbolRefAttr initialFnSym)
: name(std::move(name)), numStateBytes(numStateBytes),
states(std::move(states)) {}
states(std::move(states)), initialFnSym(initialFnSym) {}
};

/// Collects information about states within the provided Arc model storage
Expand Down
69 changes: 69 additions & 0 deletions integration_test/arcilator/JIT/initial-shift-reg.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: arcilator %s --run --jit-entry=main | FileCheck %s
// REQUIRES: arcilator-jit

// CHECK-LABEL: output = ca
// CHECK-NEXT: output = ca
// CHECK-NEXT: output = 0
// CHECK-NEXT: output = fe
// CHECK-NEXT: output = ff

module {

hw.module @shiftreg(in %clock : i1, in %reset : i1, in %en : i1, in %din : i8, out dout : i8) {
%seq_clk = seq.to_clock %clock
%srA = seq.firreg %0 clock %seq_clk preset 0xFE : i8
%srB = seq.firreg %1 clock %seq_clk : i8
%srC = seq.firreg %2 clock %seq_clk preset 0xCA : i8
%0 = comb.mux bin %en, %din, %srA : i8
%1 = comb.mux bin %en, %srA, %srB : i8
%2 = comb.mux bin %en, %srB, %srC : i8
hw.output %srC : i8
}

func.func @main() {
%ff = arith.constant 0xFF : i8
%false = arith.constant 0 : i1
%true = arith.constant 1 : i1

arc.sim.instantiate @shiftreg as %model {
arc.sim.set_input %model, "en" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "reset" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "din" = %ff : i8, !arc.sim.instance<@shiftreg>

%res0 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res0 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>

%res1 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res1 : i8

arc.sim.set_input %model, "en" = %true : i1, !arc.sim.instance<@shiftreg>

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res2 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res2 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res3 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res3 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res4 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res4 : i8
}
return
}
}
25 changes: 25 additions & 0 deletions integration_test/arcilator/JIT/initial.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: arcilator %s --run --jit-entry=main 2>&1 >/dev/null | FileCheck %s
// REQUIRES: arcilator-jit

// CHECK: - Init -

module {
llvm.func @_arc_env_get_print_stream(i32) -> !llvm.ptr
llvm.func @_arc_libc_fputs(!llvm.ptr, !llvm.ptr) -> i32
llvm.mlir.global internal constant @global_init_str(" - Init -\0A\00") {addr_space = 0 : i32}

arc.model @initmodel io !hw.modty<> {
^bb0(%arg0: !arc.storage):
arc.initial {
%cst0 = llvm.mlir.constant(0 : i32) : i32
%stderr = llvm.call @_arc_env_get_print_stream(%cst0) : (i32) -> !llvm.ptr
%str = llvm.mlir.addressof @global_init_str : !llvm.ptr
%0 = llvm.call @_arc_libc_fputs(%str, %stderr) : (!llvm.ptr, !llvm.ptr) -> i32
}
}
func.func @main() {
arc.sim.instantiate @initmodel as %arg0 {
}
return
}
}
14 changes: 13 additions & 1 deletion lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ namespace {
struct ModelInfoMap {
size_t numStateBytes;
llvm::DenseMap<StringRef, StateInfo> states;
mlir::FlatSymbolRefAttr initialFnSymbol;
};

template <typename OpTy>
Expand Down Expand Up @@ -378,6 +379,16 @@ struct SimInstantiateOpLowering
Value zero =
rewriter.create<LLVM::ConstantOp>(loc, rewriter.getI8Type(), 0);
rewriter.create<LLVM::MemsetOp>(loc, allocated, zero, numStateBytes, false);

// Call the model's 'initial' function if present.
if (model.initialFnSymbol) {
auto initialFnType = LLVM::LLVMFunctionType::get(
LLVM::LLVMVoidType::get(op.getContext()),
{LLVM::LLVMPointerType::get(op.getContext())});
rewriter.create<LLVM::CallOp>(loc, initialFnType, model.initialFnSymbol,
ValueRange{allocated});
}

rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
{allocated});
rewriter.create<LLVM::CallOp>(loc, freeFunc, ValueRange{allocated});
Expand Down Expand Up @@ -646,7 +657,8 @@ void LowerArcToLLVMPass::runOnOperation() {
for (StateInfo &stateInfo : modelInfo.states)
states.insert({stateInfo.name, stateInfo});
modelMap.insert({modelInfo.name,
ModelInfoMap{modelInfo.numStateBytes, std::move(states)}});
ModelInfoMap{modelInfo.numStateBytes, std::move(states),
modelInfo.initialFnSym}});
}

patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
Expand Down
Loading

0 comments on commit 4201039

Please sign in to comment.