mirror of https://github.com/llvm/circt.git
[Pipeline] Add non-stallable pipeline stages (#6018)
See rationale changes for an in-depth description of the why's and how's of this change.
This commit is contained in:
parent
8f4abacef5
commit
f3c9aa13ee
|
@ -169,3 +169,64 @@ pipeline.stage ^bb4 regs(%out_s3 : i32)
|
|||
^bb4(%out_s4 : i32):
|
||||
foo.bar %out_s4 : i32
|
||||
```
|
||||
|
||||
## Non-stallable Pipeline Stages
|
||||
|
||||
**Note:** the following is only valid for pipelines with a stall signal.
|
||||
|
||||
An option of the Pipeline abstraction presented in this dialect is the ability
|
||||
to have _non-stallable stages_ (NS). NS stages are used whereever a pipeline
|
||||
access resources that are not able to stop on a dime, and thus require a fixed
|
||||
amount of cycles to complete.
|
||||
|
||||
Non-stallable stages are marked as an attribute of the pipeline operations,
|
||||
wherein a bitvector is provided (by the user) to indicate which stage(s) are
|
||||
non-stallable.
|
||||
|
||||
To see how non-stallable stages are implemented, consider the following. For
|
||||
every stage, we define two signals - `S_{N,en}` is the signal that indicates
|
||||
that the stage currently has valid contents (i.e. not a bubble). `S_{N,valid}`
|
||||
is the signal that is used as a clock-enable for the output registers of a
|
||||
stage.
|
||||
|
||||
Stages can be grouped into three distinct types based on how their valid signal
|
||||
is defined: stallable stages, non-stallable stages and runoff stages.
|
||||
|
||||
<img title="Stage control" src="includes/img/Pipeline/stage_control.png"/>
|
||||
|
||||
1. Stallable stages are any stages which appear **before** the first
|
||||
non-stallable stage in the pipeline.
|
||||
2. Non-stallable stages are the stages explicitly marked as non-stallable by the
|
||||
user.
|
||||
3. Runoff stages and stages that appear **after** (and by extension, **between**
|
||||
non-stallable stages). Runoff stages consider their own enablement wrt. the
|
||||
stall signal, as well as the enablement of the **last non-stallable
|
||||
register** (LNS) wrt. the runoff stage's position in the pipeline.
|
||||
|
||||
The purpose of the runoff stages is to ensure that they are able to pass through
|
||||
as many pipeline cycles as there are upstream non-stallable stages, such that
|
||||
the contents of the non-stallable stages is not discarded.
|
||||
An important implication of this is that pipelines with non-stallable stages
|
||||
**must** be connected to some buffer mechanism that is able to hold as many
|
||||
pipeline output value cycles as there are non-stallable stages in the pipeline.
|
||||
|
||||
As an example, the following 6 stage pipeline will have the following valid
|
||||
signals: <img title="Stage control" src="includes/img/Pipeline/ns_ex1.png"/>
|
||||
|
||||
|
||||
### Example 1:
|
||||
In this example, we have two NS stages followed by three runoff stages:
|
||||
|
||||
<img title="Stage control" src="includes/img/Pipeline/ns_ex2.png"/>
|
||||
|
||||
In this example we see that, as expected, two cycles are output from the
|
||||
pipeline after the stall signal goes high, corresponding to the two NS stages.
|
||||
|
||||
### Example 2:
|
||||
In this example, we have two NS stages, then one runoff stage, then one NS
|
||||
stage, and finally one runoff stage:
|
||||
|
||||
<img title="Stage control" src="includes/img/Pipeline/ns_ex3.png"/>
|
||||
|
||||
In this example we see that, as expected, three cycles are output from the
|
||||
pipeline after the stall signal goes high, corresponding to the three NS stages.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
|
@ -24,6 +24,24 @@ namespace pipeline {
|
|||
class StageOp;
|
||||
class ScheduledPipelineOp;
|
||||
|
||||
// StageKind defines the control semantics of a pipeline stages.
|
||||
enum class StageKind {
|
||||
// All stages in a pipeline without a stall signal is a continuous stage.
|
||||
Continuous,
|
||||
// Stallable stages are any stages which appear **before** the first
|
||||
// non-stallable stage in the pipeline.
|
||||
Stallable,
|
||||
// Non-stallable stages are the stages explicitly marked as non-stallable by
|
||||
// the user.
|
||||
NonStallable,
|
||||
// Runoff stages and stages that appear **after** (and by extension,
|
||||
// **between** non-stallable stages). Runoff stages consider their own
|
||||
// enablement wrt. the stall signal, as well as the enablement of the **last
|
||||
// non-stallable register** (LNS) wrt. the runoff stage's position in the
|
||||
// pipeline.
|
||||
Runoff
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Returns the set of values defined outside of the given region, and the
|
||||
|
|
|
@ -27,12 +27,6 @@ class PipelineBase<string mnemonic, list<Trait> traits = []> :
|
|||
Pure,
|
||||
RegionKindInterface,
|
||||
AttrSizedOperandSegments])> {
|
||||
|
||||
let arguments = (ins
|
||||
OptionalAttr<StrAttr>:$name, Variadic<AnyType>:$inputs, Optional<I1>:$stall,
|
||||
I1:$clock, I1:$reset, I1:$go, StrArrayAttr:$inputNames,
|
||||
StrArrayAttr:$outputNames
|
||||
);
|
||||
let results = (outs Variadic<AnyType>:$dataOutputs, I1:$done);
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
|
||||
|
@ -117,6 +111,11 @@ def UnscheduledPipelineOp : PipelineBase<"unscheduled", [
|
|||
about the interface signals.
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
OptionalAttr<StrAttr>:$name, Variadic<AnyType>:$inputs, Optional<I1>:$stall,
|
||||
I1:$clock, I1:$reset, I1:$go, StrArrayAttr:$inputNames,
|
||||
StrArrayAttr:$outputNames
|
||||
);
|
||||
let regions = (region SizedRegion<1>: $body);
|
||||
let extraModuleClassDeclaration = "";
|
||||
}
|
||||
|
@ -148,19 +147,48 @@ def ScheduledPipelineOp : PipelineBase<"scheduled", [
|
|||
|
||||
Any value defined outside the pipeline is considered an external input. An
|
||||
external input will _not_ be registered.
|
||||
|
||||
The pipeline may optionally be provided with an array of bits `stallability`
|
||||
which is used to determine which stages are stallable.
|
||||
- If not provided and the pipeline has a stall signal, all stages are stallable.
|
||||
- If provided, and the pipeline has a stall signal, the number of bits must
|
||||
match the number of stages in the pipeline. Each bit represents a stage,
|
||||
in the order of which the stages appear wrt. the `pipeline.stage` operations.
|
||||
A bit set to 1 indicates that the stage is stallable, and 0 indicates that
|
||||
the stage is not stallable.
|
||||
|
||||
The exit (non-registered) stage of a pipeline cannot be non-stallable, and
|
||||
will always follow the stallability of the parent pipeline.
|
||||
|
||||
For more information about non-stallable stages, and how these are lowered,
|
||||
please refer to the Pipeline dialect rationale.
|
||||
}];
|
||||
|
||||
|
||||
let arguments = (ins
|
||||
OptionalAttr<StrAttr>:$name,
|
||||
Variadic<AnyType>:$inputs,
|
||||
Optional<I1>:$stall,
|
||||
I1:$clock, I1:$reset, I1:$go,
|
||||
StrArrayAttr:$inputNames, StrArrayAttr:$outputNames,
|
||||
OptionalAttr<BoolArrayAttr>:$stallability
|
||||
);
|
||||
let regions = (region AnyRegion:$body);
|
||||
let hasVerifier = 1;
|
||||
let skipDefaultBuilders = 1;
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "TypeRange":$dataOutputs, "ValueRange":$inputs,
|
||||
OpBuilder<(ins
|
||||
"TypeRange":$dataOutputs,
|
||||
"ValueRange":$inputs,
|
||||
"ArrayAttr":$inputNames, "ArrayAttr":$outputNames,
|
||||
"Value":$clock, "Value":$reset, "Value":$go, CArg<"Value", "{}">:$stall, CArg<"StringAttr", "{}">:$name)>
|
||||
"Value":$clock, "Value":$reset, "Value":$go,
|
||||
CArg<"Value", "{}">:$stall,
|
||||
CArg<"StringAttr", "{}">:$name,
|
||||
CArg<"ArrayAttr", "{}">:$stallability
|
||||
)>
|
||||
];
|
||||
|
||||
|
||||
code extraModuleClassDeclaration = [{
|
||||
static mlir::RegionKind getRegionKind(unsigned index) {
|
||||
return mlir::RegionKind::SSACFG;
|
||||
|
@ -171,6 +199,10 @@ def ScheduledPipelineOp : PipelineBase<"scheduled", [
|
|||
return getRegion().getBlocks();
|
||||
}
|
||||
|
||||
size_t getNumStages() {
|
||||
return getStages().size();
|
||||
}
|
||||
|
||||
void getAsmBlockArgumentNames(mlir::Region ®ion,
|
||||
mlir::OpAsmSetValueNameFn setNameFn);
|
||||
|
||||
|
@ -227,6 +259,9 @@ def ScheduledPipelineOp : PipelineBase<"scheduled", [
|
|||
return stage->getArguments().back();
|
||||
return getInnerGo();
|
||||
}
|
||||
|
||||
// Returns the stage kind of the given stage index.
|
||||
StageKind getStageKind(size_t stageIdx);
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import glob, os
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
for pyfile in glob.glob(os.path.join(dir_path, "**", "*.py")):
|
||||
for pyfile in glob.glob(os.path.join(dir_path, "**", "*.py"), recursive=True):
|
||||
# remove dir from pyfile
|
||||
config.excludes.add(os.path.basename(pyfile))
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import cocotb
|
||||
from cocotb.triggers import Timer
|
||||
import cocotb.clock
|
||||
|
||||
# Value that will be adjusted and assigned to the dut input argument, every
|
||||
# clock cycle. This is mainly to assist manual verification of the VCD trace/
|
||||
v = 1
|
||||
|
||||
|
||||
async def clock(dut):
|
||||
global v
|
||||
dut.clock.value = 0
|
||||
await Timer(1, units='ns')
|
||||
v += 1
|
||||
dut.arg0.value = v
|
||||
dut.clock.value = 1
|
||||
await Timer(1, units='ns')
|
||||
|
||||
|
||||
async def initDut(dut):
|
||||
"""
|
||||
Initializes a dut by adding a clock, setting initial valid and ready flags,
|
||||
and performing a reset.
|
||||
"""
|
||||
# Reset
|
||||
dut.reset.value = 1
|
||||
await clock(dut)
|
||||
dut.reset.value = 0
|
||||
await clock(dut)
|
||||
|
||||
|
||||
async def nonstallable_test(dut, stageStallability):
|
||||
"""
|
||||
Runs a test of a non-stallable pipeline.
|
||||
|
||||
Provided the number of stages in the pipeline and the indices of the non-stallable
|
||||
stages, the test will:
|
||||
|
||||
1. for NStallCycles : range(0 to nStages - 1):
|
||||
1. for fillCycles : range(0 to nStages - 1):
|
||||
1. fill the pipeline with $fillCycles valid tokens
|
||||
2. raise the stall signal
|
||||
3. wait for NStallCycles
|
||||
4. While doing so, check that len(nonStallableStageIdxs) dut.done assertions
|
||||
occur
|
||||
5. deassert stall
|
||||
6. check that the expected amount of bubbles exit the pipeline
|
||||
"""
|
||||
nStages = len(stageStallability)
|
||||
numNonstallableStages = sum(
|
||||
[1 if not stallable else 0 for stallable in stageStallability])
|
||||
|
||||
for nStallCycles in range(0, nStages * 2):
|
||||
for fillCycles in range(0, nStages + 1):
|
||||
print(f"nStallCycles: {nStallCycles}, fillCycles: {fillCycles}")
|
||||
# Reset the dut
|
||||
dut.go.value = 0
|
||||
dut.stall.value = 0
|
||||
dut.arg0.value = 42
|
||||
await initDut(dut)
|
||||
dut.stall.value = 0
|
||||
|
||||
# Fill the pipeline with fillCycles valid tokens
|
||||
dut.go.value = 1
|
||||
for i in range(fillCycles):
|
||||
await clock(dut)
|
||||
dut.go.value = 0
|
||||
|
||||
nBufferedTokensExpected = min(numNonstallableStages, nStallCycles,
|
||||
fillCycles)
|
||||
nBubblesExpected = nBufferedTokensExpected
|
||||
|
||||
# Raise the stall signal. We now expect that numNonStallableStages dut.done
|
||||
# assertions will occur in a row.
|
||||
sequence = []
|
||||
dut.stall.value = 1
|
||||
for cycle in range(nStallCycles + nStages):
|
||||
if cycle > nStallCycles:
|
||||
dut.stall.value = 0
|
||||
|
||||
await Timer(1, units='ns')
|
||||
sequence.append(int(dut.done))
|
||||
await clock(dut)
|
||||
|
||||
# Check that exactly fill_cycles tokens exited the pipeline
|
||||
nTokensExited = sum(sequence)
|
||||
assert nTokensExited == fillCycles, f"Expected {fillCycles} tokens to exit the pipeline, but {nTokensExited} did"
|
|
@ -0,0 +1,30 @@
|
|||
// REQUIRES: iverilog,cocotb
|
||||
|
||||
// RUN: circt-opt %s -pipeline-explicit-regs -lower-pipeline-to-hw -lower-seq-to-sv -sv-trace-iverilog -export-verilog \
|
||||
// RUN: -o %t.mlir > %t.sv
|
||||
|
||||
// RUN: circt-cocotb-driver.py --objdir=%T --topLevel=nonstallable_test1 \
|
||||
// RUN: --pythonModule=nonstallable_test1 --pythonFolder="%S,%S/.." %t.sv 2>&1 | FileCheck %s
|
||||
|
||||
|
||||
// CHECK: ** TEST
|
||||
// CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0
|
||||
|
||||
hw.module @nonstallable_test1(%arg0: i32, %go: i1, %clock: i1, %reset: i1, %stall: i1) -> (out: i32, done: i1) {
|
||||
%out, %done = pipeline.scheduled "nonstallable_test1"(%a0 : i32 = %arg0)
|
||||
stall(%s = %stall) clock(%c = %clock) reset(%r = %reset) go(%g = %go)
|
||||
{stallability = [true, false, false, true, true]} -> (out : i32) {
|
||||
pipeline.stage ^bb1
|
||||
^bb1(%s1_enable: i1):
|
||||
pipeline.stage ^bb2
|
||||
^bb2(%s2_enable: i1):
|
||||
pipeline.stage ^bb3
|
||||
^bb3(%s3_enable: i1):
|
||||
pipeline.stage ^bb4
|
||||
^bb4(%s4_enable: i1):
|
||||
pipeline.stage ^bb5
|
||||
^bb5(%s5_enable: i1):
|
||||
pipeline.return %a0 : i32
|
||||
}
|
||||
hw.output %out, %done : i32, i1
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import cocotb
|
||||
from nonstallable_helper import nonstallable_test
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def test1(dut):
|
||||
await nonstallable_test(dut, [0, 1, 1, 0, 0])
|
|
@ -0,0 +1,30 @@
|
|||
// REQUIRES: iverilog,cocotb
|
||||
|
||||
// RUN: circt-opt %s -pipeline-explicit-regs -lower-pipeline-to-hw -lower-seq-to-sv -sv-trace-iverilog -export-verilog \
|
||||
// RUN: -o %t.mlir > %t.sv
|
||||
|
||||
// RUN: circt-cocotb-driver.py --objdir=%T --topLevel=nonstallable_test2 \
|
||||
// RUN: --pythonModule=nonstallable_test2 --pythonFolder="%S,%S/.." %t.sv 2>&1 | FileCheck %s
|
||||
|
||||
|
||||
// CHECK: ** TEST
|
||||
// CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0
|
||||
|
||||
hw.module @nonstallable_test2(%arg0: i32, %go: i1, %clock: i1, %reset: i1, %stall: i1) -> (out: i32, done : i1) {
|
||||
%out, %done = pipeline.scheduled "nonstallable_test2"(%a0 : i32 = %arg0)
|
||||
stall(%s = %stall) clock(%c = %clock) reset(%r = %reset) go(%g = %go)
|
||||
{stallability = [true, false, true, false, true]} -> (out : i32) {
|
||||
pipeline.stage ^bb1
|
||||
^bb1(%s1_enable: i1):
|
||||
pipeline.stage ^bb2
|
||||
^bb2(%s2_enable: i1):
|
||||
pipeline.stage ^bb3
|
||||
^bb3(%s3_enable: i1):
|
||||
pipeline.stage ^bb4
|
||||
^bb4(%s4_enable: i1):
|
||||
pipeline.stage ^bb5
|
||||
^bb5(%s5_enable: i1):
|
||||
pipeline.return %a0 : i32
|
||||
}
|
||||
hw.output %out, %done : i32, i1
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import cocotb
|
||||
|
||||
from nonstallable_helper import nonstallable_test
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def test1(dut):
|
||||
await nonstallable_test(dut, [0, 1, 0, 1, 0])
|
|
@ -46,6 +46,7 @@ public:
|
|||
Value stall;
|
||||
Value clock;
|
||||
Value reset;
|
||||
Value lnsEn;
|
||||
};
|
||||
|
||||
// Arguments used for returning the results from a stage. These values must
|
||||
|
@ -54,6 +55,11 @@ public:
|
|||
llvm::SmallVector<Value> regs;
|
||||
llvm::SmallVector<Value> passthroughs;
|
||||
Value valid;
|
||||
|
||||
// In case this was the last register in a non-stallable register chain, the
|
||||
// register will also return its enable signal to be used for LNS of
|
||||
// downstream stages.
|
||||
Value lnsEn;
|
||||
};
|
||||
|
||||
virtual FailureOr<StageReturns>
|
||||
|
@ -89,6 +95,41 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
auto loc = terminator->getLoc();
|
||||
Value notStalled;
|
||||
auto getOrSetNotStalled = [&]() {
|
||||
if (!notStalled) {
|
||||
notStalled = comb::createOrFoldNot(loc, args.stall, builder);
|
||||
}
|
||||
return notStalled;
|
||||
};
|
||||
|
||||
// Determine the stage kind. This will influence how the stage valid and
|
||||
// enable signals are defined.
|
||||
StageKind stageKind = pipeline.getStageKind(stageIndex);
|
||||
Value stageValid;
|
||||
StringAttr validSignalName =
|
||||
builder.getStringAttr(getStagePrefix(stageIndex).strref() + "_valid");
|
||||
switch (stageKind) {
|
||||
case StageKind::Continuous:
|
||||
LLVM_FALLTHROUGH;
|
||||
case StageKind::NonStallable:
|
||||
stageValid = args.enable;
|
||||
break;
|
||||
case StageKind::Stallable:
|
||||
stageValid =
|
||||
builder.create<comb::AndOp>(loc, args.enable, getOrSetNotStalled());
|
||||
stageValid.getDefiningOp()->setAttr("sv.namehint", validSignalName);
|
||||
break;
|
||||
case StageKind::Runoff:
|
||||
assert(args.lnsEn && "Expected an LNS signal if this was a runoff stage");
|
||||
stageValid = builder.create<comb::AndOp>(
|
||||
loc, args.enable,
|
||||
builder.create<comb::OrOp>(loc, args.lnsEn, getOrSetNotStalled()));
|
||||
stageValid.getDefiningOp()->setAttr("sv.namehint", validSignalName);
|
||||
break;
|
||||
}
|
||||
|
||||
StageReturns rets;
|
||||
auto stageOp = dyn_cast<StageOp>(terminator);
|
||||
if (!stageOp) {
|
||||
|
@ -97,32 +138,19 @@ public:
|
|||
// register its operands, hence, all return operands are passthrough
|
||||
// and the valid signal is equal to the unregistered enable signal.
|
||||
rets.passthroughs = terminator->getOperands();
|
||||
rets.valid = args.enable;
|
||||
rets.valid = stageValid;
|
||||
return rets;
|
||||
}
|
||||
|
||||
assert(registerNames.size() == stageOp.getRegisters().size() &&
|
||||
"register names and registers must be the same size");
|
||||
|
||||
// Build data registers.
|
||||
auto stageRegPrefix = getStageRegPrefix(stageIndex);
|
||||
auto loc = stageOp->getLoc();
|
||||
|
||||
// Build the clock enable signal: enable && !stall (if applicable)
|
||||
Value stageValidAndNotStalled = args.enable;
|
||||
Value notStalled;
|
||||
bool hasStall = static_cast<bool>(args.stall);
|
||||
if (hasStall) {
|
||||
notStalled = comb::createOrFoldNot(loc, args.stall, builder);
|
||||
stageValidAndNotStalled =
|
||||
builder.create<comb::AndOp>(loc, stageValidAndNotStalled, notStalled);
|
||||
}
|
||||
|
||||
bool isStallablePipeline = stageKind != StageKind::Continuous;
|
||||
Value notStalledClockGate;
|
||||
if (this->clockGateRegs) {
|
||||
// Create the top-level clock gate.
|
||||
notStalledClockGate = builder.create<seq::ClockGateOp>(
|
||||
loc, args.clock, stageValidAndNotStalled, /*test_enable=*/Value(),
|
||||
loc, args.clock, stageValid, /*test_enable=*/Value(),
|
||||
/*inner_sym=*/hw::InnerSymAttr());
|
||||
}
|
||||
|
||||
|
@ -138,18 +166,19 @@ public:
|
|||
for (auto hierClockGateEnable : stageOp.getClockGatesForReg(regIdx)) {
|
||||
// Create clock gates for any hierarchically nested clock gates.
|
||||
currClockGate = builder.create<seq::ClockGateOp>(
|
||||
loc, currClockGate, hierClockGateEnable, /*test_enable=*/Value(),
|
||||
loc, currClockGate, hierClockGateEnable,
|
||||
/*test_enable=*/Value(),
|
||||
/*inner_sym=*/hw::InnerSymAttr());
|
||||
}
|
||||
dataReg = builder.create<seq::CompRegOp>(stageOp->getLoc(), regIn,
|
||||
currClockGate, regName);
|
||||
} else {
|
||||
// Only clock-enable the register if the pipeline is stallable.
|
||||
// For non-stallable pipelines, a data register can always be clocked.
|
||||
if (hasStall) {
|
||||
// For non-stallable (continuous) pipelines, a data register can always
|
||||
// be clocked.
|
||||
if (isStallablePipeline) {
|
||||
dataReg = builder.create<seq::CompRegClockEnabledOp>(
|
||||
stageOp->getLoc(), regIn, args.clock, stageValidAndNotStalled,
|
||||
regName);
|
||||
stageOp->getLoc(), regIn, args.clock, stageValid, regName);
|
||||
} else {
|
||||
dataReg = builder.create<seq::CompRegOp>(stageOp->getLoc(), regIn,
|
||||
args.clock, regName);
|
||||
|
@ -158,38 +187,26 @@ public:
|
|||
rets.regs.push_back(dataReg);
|
||||
}
|
||||
|
||||
// Build valid register. The valid register is always reset to 0, and
|
||||
// clock enabled when not stalling.
|
||||
auto validRegName = (stageRegPrefix.strref() + "_valid").str();
|
||||
Value validRegResetVal =
|
||||
builder.create<hw::ConstantOp>(terminator->getLoc(), APInt(1, 0, false))
|
||||
.getResult();
|
||||
if (hasStall) {
|
||||
rets.valid = builder.create<seq::CompRegClockEnabledOp>(
|
||||
loc, args.enable, args.clock, notStalled, args.reset,
|
||||
validRegResetVal, validRegName);
|
||||
} else {
|
||||
rets.valid = builder.create<seq::CompRegOp>(loc, args.enable, args.clock,
|
||||
args.reset, validRegResetVal,
|
||||
validRegName);
|
||||
}
|
||||
rets.valid = stageValid;
|
||||
if (stageKind == StageKind::NonStallable)
|
||||
rets.lnsEn = args.enable;
|
||||
|
||||
rets.passthroughs = stageOp.getPassthroughs();
|
||||
return rets;
|
||||
}
|
||||
|
||||
// A container carrying all-things stage output naming related.
|
||||
// To avoid overloading 'output's to much (i'm trying to keep that reserved
|
||||
// for "output" ports), this is named "egress".
|
||||
// To avoid overloading 'output's to much (i'm trying to keep that
|
||||
// reserved for "output" ports), this is named "egress".
|
||||
struct StageEgressNames {
|
||||
llvm::SmallVector<Attribute> regNames;
|
||||
llvm::SmallVector<Attribute> outNames;
|
||||
llvm::SmallVector<Attribute> inNames;
|
||||
};
|
||||
|
||||
// Returns a set of names for the output values of a given stage (registers
|
||||
// and passthrough). If `withPipelinePrefix` is true, the names will be
|
||||
// prefixed with the pipeline name.
|
||||
// Returns a set of names for the output values of a given stage
|
||||
// (registers and passthrough). If `withPipelinePrefix` is true, the names
|
||||
// will be prefixed with the pipeline name.
|
||||
void getStageEgressNames(size_t stageIndex, Operation *stageTerminator,
|
||||
bool withPipelinePrefix,
|
||||
StageEgressNames &egressNames) {
|
||||
|
@ -243,15 +260,15 @@ public:
|
|||
egressNames.inNames.push_back(builder.getStringAttr(assignedInName));
|
||||
}
|
||||
} else {
|
||||
// For the return op, we just inherit the names of the top-level pipeline
|
||||
// as stage output names.
|
||||
// For the return op, we just inherit the names of the top-level
|
||||
// pipeline as stage output names.
|
||||
llvm::copy(pipeline.getOutputNames().getAsRange<StringAttr>(),
|
||||
std::back_inserter(egressNames.outNames));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a string to be used as a prefix for all stage registers.
|
||||
virtual StringAttr getStageRegPrefix(size_t stageIdx) = 0;
|
||||
virtual StringAttr getStagePrefix(size_t stageIdx) = 0;
|
||||
|
||||
protected:
|
||||
// Determine a reasonable name for the pipeline. This will affect naming
|
||||
|
@ -288,7 +305,7 @@ class PipelineInlineLowering : public PipelineLowering {
|
|||
public:
|
||||
using PipelineLowering::PipelineLowering;
|
||||
|
||||
StringAttr getStageRegPrefix(size_t stageIdx) override {
|
||||
StringAttr getStagePrefix(size_t stageIdx) override {
|
||||
return builder.getStringAttr(pipelineName.strref() + "_stage" +
|
||||
Twine(stageIdx));
|
||||
}
|
||||
|
@ -328,6 +345,8 @@ public:
|
|||
lowerStage(Block *stage, StageArgs args, size_t stageIndex,
|
||||
llvm::ArrayRef<Attribute> /*inputNames*/ = {}) override {
|
||||
OpBuilder::InsertionGuard guard(builder);
|
||||
Operation *terminator = stage->getTerminator();
|
||||
Location loc = terminator->getLoc();
|
||||
|
||||
if (stage != pipeline.getEntryStage()) {
|
||||
// Replace the internal stage inputs with the provided arguments.
|
||||
|
@ -336,11 +355,55 @@ public:
|
|||
vInput.replaceAllUsesWith(vArg);
|
||||
}
|
||||
|
||||
// Build stage enable register. The enable register is always reset to 0.
|
||||
// The stage enable register takes the previous-stage combinational valid
|
||||
// output and determines whether this stage is active or not in the next
|
||||
// cycle.
|
||||
// A non-stallable stage always registers the incoming enable signal,
|
||||
// whereas other stages register based on the current stall state.
|
||||
StageKind stageKind = pipeline.getStageKind(stageIndex);
|
||||
Value stageEnabled;
|
||||
if (stageIndex == 0) {
|
||||
stageEnabled = args.enable;
|
||||
} else {
|
||||
auto stageRegPrefix = getStagePrefix(stageIndex);
|
||||
auto enableRegName = (stageRegPrefix.strref() + "_enable").str();
|
||||
Value enableRegResetVal =
|
||||
builder.create<hw::ConstantOp>(loc, APInt(1, 0, false)).getResult();
|
||||
|
||||
switch (stageKind) {
|
||||
case StageKind::Continuous:
|
||||
LLVM_FALLTHROUGH;
|
||||
case StageKind::NonStallable:
|
||||
stageEnabled = builder.create<seq::CompRegOp>(
|
||||
loc, args.enable, args.clock, args.reset, enableRegResetVal,
|
||||
enableRegName);
|
||||
break;
|
||||
case StageKind::Stallable:
|
||||
stageEnabled = builder.create<seq::CompRegClockEnabledOp>(
|
||||
loc, args.enable, args.clock,
|
||||
comb::createOrFoldNot(loc, args.stall, builder), args.reset,
|
||||
enableRegResetVal, enableRegName);
|
||||
break;
|
||||
case StageKind::Runoff:
|
||||
assert(args.lnsEn &&
|
||||
"Expected an LNS signal if this was a runoff stage");
|
||||
stageEnabled = builder.create<seq::CompRegClockEnabledOp>(
|
||||
loc, args.enable, args.clock,
|
||||
builder.create<comb::OrOp>(
|
||||
loc, args.lnsEn,
|
||||
comb::createOrFoldNot(loc, args.stall, builder)),
|
||||
args.reset, enableRegResetVal, enableRegName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the stage valid signal.
|
||||
pipeline.getStageEnableSignal(stage).replaceAllUsesWith(args.enable);
|
||||
args.enable = stageEnabled;
|
||||
pipeline.getStageEnableSignal(stage).replaceAllUsesWith(stageEnabled);
|
||||
|
||||
// Determine stage egress info.
|
||||
auto nextStage = dyn_cast<StageOp>(stage->getTerminator());
|
||||
auto nextStage = dyn_cast<StageOp>(terminator);
|
||||
StageEgressNames egressNames;
|
||||
if (nextStage)
|
||||
getStageEgressNames(stageIndex, nextStage,
|
||||
|
@ -357,6 +420,11 @@ public:
|
|||
llvm::append_range(nextStageArgs, stageRets.regs);
|
||||
llvm::append_range(nextStageArgs, stageRets.passthroughs);
|
||||
args.enable = stageRets.valid;
|
||||
if (stageRets.lnsEn) {
|
||||
// Swap the lnsEn signal if the current stage lowering generated an
|
||||
// lnsEn.
|
||||
args.lnsEn = stageRets.lnsEn;
|
||||
}
|
||||
args.data = nextStageArgs;
|
||||
return lowerStage(nextStage.getNextStage(), args, stageIndex + 1);
|
||||
}
|
||||
|
@ -366,7 +434,7 @@ public:
|
|||
llvm::SmallVector<Value> pipelineReturns;
|
||||
llvm::append_range(pipelineReturns, returnOp.getInputs());
|
||||
// The last stage valid signal is the 'done' output of the pipeline.
|
||||
pipelineReturns.push_back(args.enable);
|
||||
pipelineReturns.push_back(stageRets.valid);
|
||||
pipeline.replaceAllUsesWith(pipelineReturns);
|
||||
return stageRets;
|
||||
}
|
||||
|
@ -382,8 +450,8 @@ struct PipelineToHWPass : public PipelineToHWBase<PipelineToHWPass> {
|
|||
|
||||
private:
|
||||
// Lowers pipelines within HWModules. This pass is currently expecting that
|
||||
// Pipelines are always nested with HWModule's but could be written to be more
|
||||
// generic.
|
||||
// Pipelines are always nested with HWModule's but could be written to be
|
||||
// more generic.
|
||||
void runOnHWModule(hw::HWModuleOp mod);
|
||||
};
|
||||
|
||||
|
@ -396,8 +464,8 @@ void PipelineToHWPass::runOnHWModule(hw::HWModuleOp mod) {
|
|||
OpBuilder builder(&getContext());
|
||||
// Iterate over each pipeline op in the module and convert.
|
||||
// Note: This pass matches on `hw::ModuleOp`s and not directly on the
|
||||
// `ScheduledPipelineOp` due to the `ScheduledPipelineOp` being erased during
|
||||
// this pass.
|
||||
// `ScheduledPipelineOp` due to the `ScheduledPipelineOp` being erased
|
||||
// during this pass.
|
||||
size_t pipelinesSeen = 0;
|
||||
for (auto pipeline :
|
||||
llvm::make_early_inc_range(mod.getOps<ScheduledPipelineOp>())) {
|
||||
|
|
|
@ -269,15 +269,15 @@ static void printPipelineOp(OpAsmPrinter &p, TPipelineOp op) {
|
|||
printKeywordAssignment(p, "reset", op.getInnerReset(), op.getReset());
|
||||
p << " ";
|
||||
printKeywordAssignment(p, "go", op.getInnerGo(), op.getGo());
|
||||
// Print the optional attribute dict.
|
||||
p.printOptionalAttrDict(op->getAttrs(),
|
||||
/*elidedAttrs=*/{"name", "operandSegmentSizes",
|
||||
"outputNames", "inputNames"});
|
||||
p << " -> ";
|
||||
|
||||
// Print the output list.
|
||||
printOutputList(p, op.getDataOutputs().getTypes(), op.getOutputNames());
|
||||
|
||||
// Print the optional attribute dict.
|
||||
p.printOptionalAttrDict(op->getAttrs(),
|
||||
/*elidedAttrs=*/{"name", "operandSegmentSizes",
|
||||
"outputNames", "inputNames"});
|
||||
p << " ";
|
||||
|
||||
// Print the inner region, eliding the entry block arguments - we've already
|
||||
|
@ -314,7 +314,7 @@ void ScheduledPipelineOp::build(OpBuilder &odsBuilder, OperationState &odsState,
|
|||
TypeRange dataOutputs, ValueRange inputs,
|
||||
ArrayAttr inputNames, ArrayAttr outputNames,
|
||||
Value clock, Value reset, Value go, Value stall,
|
||||
StringAttr name) {
|
||||
StringAttr name, ArrayAttr stallability) {
|
||||
odsState.addOperands(inputs);
|
||||
if (stall)
|
||||
odsState.addOperands(stall);
|
||||
|
@ -359,6 +359,9 @@ void ScheduledPipelineOp::build(OpBuilder &odsBuilder, OperationState &odsState,
|
|||
|
||||
// entry stage valid signal.
|
||||
entryBlock.addArgument(i1, odsState.location);
|
||||
|
||||
if (stallability)
|
||||
odsState.addAttribute("stallability", stallability);
|
||||
}
|
||||
|
||||
Block *ScheduledPipelineOp::addStage() {
|
||||
|
@ -558,9 +561,61 @@ LogicalResult ScheduledPipelineOp::verify() {
|
|||
}
|
||||
}
|
||||
|
||||
if (auto stallability = getStallability()) {
|
||||
// Only allow specifying stallability if there is a stall signal.
|
||||
if (!hasStall())
|
||||
return emitOpError("cannot specify stallability without a stall signal.");
|
||||
|
||||
// Ensure that the # of stages is equal to the length of the stallability
|
||||
// array - the exit stage is never stallable.
|
||||
size_t nRegisterStages = stages.size() - 1;
|
||||
if (stallability->size() != nRegisterStages)
|
||||
return emitOpError("stallability array must be the same length as the "
|
||||
"number of stages. Pipeline has ")
|
||||
<< nRegisterStages << " stages but array had "
|
||||
<< stallability->size() << " elements.";
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
StageKind ScheduledPipelineOp::getStageKind(size_t stageIndex) {
|
||||
size_t nStages = getNumStages();
|
||||
assert(stageIndex < nStages && "invalid stage index");
|
||||
|
||||
if (!hasStall())
|
||||
return StageKind::Continuous;
|
||||
|
||||
// There is a stall signal - also check whether stage-level stallability is
|
||||
// specified.
|
||||
std::optional<ArrayAttr> stallability = getStallability();
|
||||
if (!stallability) {
|
||||
// All stages are stallable.
|
||||
return StageKind::Stallable;
|
||||
}
|
||||
|
||||
if (stageIndex < stallability->size()) {
|
||||
bool stageIsStallable =
|
||||
(*stallability)[stageIndex].cast<BoolAttr>().getValue();
|
||||
if (!stageIsStallable) {
|
||||
// This is a non-stallable stage.
|
||||
return StageKind::NonStallable;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk backwards from this stage to see if any non-stallable stage exists.
|
||||
// If so, this is a runoff stage.
|
||||
// TODO: This should be a pre-computed property.
|
||||
if (stageIndex == 0)
|
||||
return StageKind::Stallable;
|
||||
|
||||
for (size_t i = stageIndex - 1; i > 0; --i) {
|
||||
if (getStageKind(i) == StageKind::NonStallable)
|
||||
return StageKind::Runoff;
|
||||
}
|
||||
return StageKind::Stallable;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ReturnOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -15,15 +15,15 @@ hw.module @testBasic(%arg0: i1, %clk: i1, %rst: i1) -> (out: i1) {
|
|||
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i1, %[[VAL_3:.*]]: i1, %[[VAL_4:.*]]: i1) -> (out: i32, done: i1) {
|
||||
// CHECK: %[[VAL_5:.*]] = comb.add %[[VAL_0]], %[[VAL_0]] : i32
|
||||
// CHECK: %[[VAL_6:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_6]] : i1
|
||||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_6]] : i1
|
||||
// CHECK: %[[VAL_8:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage1_valid %[[VAL_7]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage2_enable %[[VAL_7]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_10:.*]] = seq.compreg sym @p0_stage2_reg0 %[[VAL_5]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_11:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_12:.*]] = seq.compreg sym @p0_stage2_valid %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_11]] : i1
|
||||
// CHECK: %[[VAL_12:.*]] = seq.compreg sym @p0_stage3_enable %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_11]] : i1
|
||||
// CHECK: %[[VAL_13:.*]] = seq.compreg sym @p0_stage3_reg0 %[[VAL_10]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_14:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_15:.*]] = seq.compreg sym @p0_stage3_valid %[[VAL_12]], %[[VAL_3]], %[[VAL_4]], %[[VAL_14]] : i1
|
||||
// CHECK: %[[VAL_15:.*]] = seq.compreg sym @p0_stage4_enable %[[VAL_12]], %[[VAL_3]], %[[VAL_4]], %[[VAL_14]] : i1
|
||||
// CHECK: hw.output %[[VAL_13]], %[[VAL_15]] : i32, i1
|
||||
// CHECK: }
|
||||
hw.module @testLatency1(%arg0: i32, %arg1: i32, %go: i1, %clk: i1, %rst: i1) -> (out: i32, done: i1) {
|
||||
|
@ -51,7 +51,7 @@ hw.module @testLatency1(%arg0: i32, %arg1: i32, %go: i1, %clk: i1, %rst: i1) ->
|
|||
// CHECK: %[[VAL_6:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_5]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_8:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_10:.*]] = comb.add %[[VAL_6]], %[[VAL_7]] : i32
|
||||
// CHECK: hw.output %[[VAL_10]], %[[VAL_9]] : i32, i1
|
||||
// CHECK: }
|
||||
|
@ -72,23 +72,23 @@ hw.module @testSingle(%arg0: i32, %arg1: i32, %go: i1, %clk: i1, %rst: i1) -> (o
|
|||
// CHECK: %[[VAL_6:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_5]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_8:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_10:.*]] = comb.add %[[VAL_6]], %[[VAL_7]] : i32
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage1_reg0 %[[VAL_10]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_12:.*]] = seq.compreg sym @p0_stage1_reg1 %[[VAL_6]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_13:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_14:.*]] = seq.compreg sym @p0_stage1_valid %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_13]] : i1
|
||||
// CHECK: %[[VAL_14:.*]] = seq.compreg sym @p0_stage2_enable %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_13]] : i1
|
||||
// CHECK: %[[VAL_15:.*]] = comb.mul %[[VAL_11]], %[[VAL_12]] : i32
|
||||
// CHECK: %[[VAL_16:.*]] = comb.sub %[[VAL_15]], %[[VAL_1]] : i32
|
||||
// CHECK: %[[VAL_17:.*]] = seq.compreg sym @p1_stage0_reg0 %[[VAL_16]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_18:.*]] = seq.compreg sym @p1_stage0_reg1 %[[VAL_15]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_19:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_20:.*]] = seq.compreg sym @p1_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_19]] : i1
|
||||
// CHECK: %[[VAL_20:.*]] = seq.compreg sym @p1_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_19]] : i1
|
||||
// CHECK: %[[VAL_21:.*]] = comb.add %[[VAL_17]], %[[VAL_18]] : i32
|
||||
// CHECK: %[[VAL_22:.*]] = seq.compreg sym @p1_stage1_reg0 %[[VAL_21]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_23:.*]] = seq.compreg sym @p1_stage1_reg1 %[[VAL_17]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_24:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_25:.*]] = seq.compreg sym @p1_stage1_valid %[[VAL_20]], %[[VAL_3]], %[[VAL_4]], %[[VAL_24]] : i1
|
||||
// CHECK: %[[VAL_25:.*]] = seq.compreg sym @p1_stage2_enable %[[VAL_20]], %[[VAL_3]], %[[VAL_4]], %[[VAL_24]] : i1
|
||||
// CHECK: %[[VAL_26:.*]] = comb.mul %[[VAL_22]], %[[VAL_23]] : i32
|
||||
// CHECK: hw.output %[[VAL_15]], %[[VAL_14]] : i32, i1
|
||||
// CHECK: }
|
||||
|
@ -124,11 +124,11 @@ hw.module @testMultiple(%arg0: i32, %arg1: i32, %go: i1, %clk: i1, %rst: i1) ->
|
|||
// CHECK: %[[VAL_6:.*]] = comb.sub %[[VAL_0]], %[[VAL_0]] : i32
|
||||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_6]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_8:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_8]] : i1
|
||||
// CHECK: %[[VAL_10:.*]] = comb.add %[[VAL_7]], %[[VAL_1]] : i32
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage1_reg0 %[[VAL_10]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_12:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_13:.*]] = seq.compreg sym @p0_stage1_valid %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_12]] : i1
|
||||
// CHECK: %[[VAL_13:.*]] = seq.compreg sym @p0_stage2_enable %[[VAL_9]], %[[VAL_3]], %[[VAL_4]], %[[VAL_12]] : i1
|
||||
// CHECK: hw.output %[[VAL_11]], %[[VAL_1]] : i32, i32
|
||||
// CHECK: }
|
||||
hw.module @testSingleWithExt(%arg0: i32, %ext1: i32, %go : i1, %clk: i1, %rst: i1) -> (out0: i32, out1: i32) {
|
||||
|
@ -159,7 +159,7 @@ hw.module @testSingleWithExt(%arg0: i32, %ext1: i32, %go : i1, %clk: i1, %rst: i
|
|||
// CHECK: sv.assign %[[VAL_5]], %[[VAL_8]] : i32
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_8]], %[[VAL_2]] : i32
|
||||
// CHECK: %[[VAL_10:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_1]], %[[VAL_2]], %[[VAL_3]], %[[VAL_10]] : i1
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_1]], %[[VAL_2]], %[[VAL_3]], %[[VAL_10]] : i1
|
||||
// CHECK: %[[VAL_12:.*]] = sv.wire : !hw.inout<i32>
|
||||
// CHECK: %[[VAL_13:.*]] = sv.read_inout %[[VAL_12]] : !hw.inout<i32>
|
||||
// CHECK: %[[VAL_14:.*]] = comb.add %[[VAL_13]], %[[VAL_9]] : i32
|
||||
|
@ -167,7 +167,7 @@ hw.module @testSingleWithExt(%arg0: i32, %ext1: i32, %go : i1, %clk: i1, %rst: i
|
|||
// CHECK: sv.assign %[[VAL_12]], %[[VAL_15]] : i32
|
||||
// CHECK: %[[VAL_16:.*]] = seq.compreg sym @p0_stage1_reg0 %[[VAL_15]], %[[VAL_2]] : i32
|
||||
// CHECK: %[[VAL_17:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_18:.*]] = seq.compreg sym @p0_stage1_valid %[[VAL_11]], %[[VAL_2]], %[[VAL_3]], %[[VAL_17]] : i1
|
||||
// CHECK: %[[VAL_18:.*]] = seq.compreg sym @p0_stage2_enable %[[VAL_11]], %[[VAL_2]], %[[VAL_3]], %[[VAL_17]] : i1
|
||||
// CHECK: %[[VAL_19:.*]] = sv.wire : !hw.inout<i32>
|
||||
// CHECK: %[[VAL_20:.*]] = sv.read_inout %[[VAL_19]] : !hw.inout<i32>
|
||||
// CHECK: %[[VAL_21:.*]] = comb.add %[[VAL_20]], %[[VAL_16]] : i32
|
||||
|
@ -211,11 +211,16 @@ hw.module @testControlUsage(%arg0: i32, %go : i1, %clk: i1, %rst: i1) -> (out0:
|
|||
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i1, %[[VAL_2:.*]]: i1, %[[VAL_3:.*]]: i1, %[[VAL_4:.*]]: i1) -> (out0: i32, out1: i1) {
|
||||
// CHECK: %[[VAL_5:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_6:.*]] = comb.xor %[[VAL_2]], %[[VAL_5]] : i1
|
||||
// CHECK: %[[VAL_7:.*]] = comb.and %[[VAL_1]], %[[VAL_6]] : i1
|
||||
// CHECK: %[[VAL_8:.*]] = seq.compreg.ce sym @p0_stage0_reg0 %[[VAL_0]], %[[VAL_3]], %[[VAL_7]] : i32
|
||||
// CHECK: %[[VAL_7:.*]] = comb.and %[[VAL_1]], %[[VAL_6]]
|
||||
// CHECK: %[[VAL_8:.*]] = seq.compreg.ce sym @p0_stage0_reg0 %[[VAL_0]], %[[VAL_3]], %[[VAL_7]] : i32, i1
|
||||
// CHECK: %[[VAL_9:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_10:.*]] = seq.compreg.ce sym @p0_stage0_valid %[[VAL_1]], %[[VAL_3]], %[[VAL_6]], %[[VAL_4]], %[[VAL_9]] : i1
|
||||
// CHECK: hw.output %[[VAL_8]], %[[VAL_10]] : i32, i1
|
||||
// CHECK: %[[VAL_10:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_11:.*]] = comb.xor %[[VAL_2]], %[[VAL_10]] : i1
|
||||
// CHECK: %[[VAL_12:.*]] = seq.compreg.ce sym @p0_stage1_enable %[[VAL_7]], %[[VAL_3]], %[[VAL_11]], %[[VAL_4]], %[[VAL_9]] : i1, i1
|
||||
// CHECK: %[[VAL_13:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_14:.*]] = comb.xor %[[VAL_2]], %[[VAL_13]] : i1
|
||||
// CHECK: %[[VAL_15:.*]] = comb.and %[[VAL_12]], %[[VAL_14]]
|
||||
// CHECK: hw.output %[[VAL_8]], %[[VAL_15]] : i32, i1
|
||||
// CHECK: }
|
||||
hw.module @testWithStall(%arg0: i32, %go: i1, %stall : i1, %clk: i1, %rst: i1) -> (out0: i32, out1: i1) {
|
||||
%0:2 = pipeline.scheduled(%a0 : i32 = %arg0) stall(%s = %stall) clock(%c = %clk) reset(%r = %rst) go(%g = %go) -> (out: i32) {
|
||||
|
@ -225,3 +230,51 @@ hw.module @testWithStall(%arg0: i32, %go: i1, %stall : i1, %clk: i1, %rst: i1) -
|
|||
}
|
||||
hw.output %0#0, %0#1 : i32, i1
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: hw.module @testStallability(
|
||||
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i1, %[[VAL_2:.*]]: i1, %[[VAL_3:.*]]: i1, %[[VAL_4:.*]]: i1) -> (out: i32) {
|
||||
// CHECK: %[[VAL_5:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_6:.*]] = comb.xor %[[VAL_4]], %[[VAL_5]] : i1
|
||||
// CHECK: %[[VAL_7:.*]] = comb.and %[[VAL_1]], %[[VAL_6]]
|
||||
// CHECK: %[[VAL_8:.*]] = seq.compreg.ce sym @MyPipeline_a0 %[[VAL_0]], %[[VAL_2]], %[[VAL_7]] : i32, i1
|
||||
// CHECK: %[[VAL_9:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_10:.*]] = seq.compreg sym @MyPipeline_stage1_enable %[[VAL_7]], %[[VAL_2]], %[[VAL_3]], %[[VAL_9]] : i1, i1
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg.ce sym @MyPipeline_a0 %[[VAL_8]], %[[VAL_2]], %[[VAL_10]] {name = "MyPipeline_a0"} : i32, i1
|
||||
// CHECK: %[[VAL_12:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_13:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_14:.*]] = comb.xor %[[VAL_4]], %[[VAL_13]] : i1
|
||||
// CHECK: %[[VAL_15:.*]] = comb.or %[[VAL_10]], %[[VAL_14]] : i1
|
||||
// CHECK: %[[VAL_16:.*]] = seq.compreg.ce sym @MyPipeline_stage2_enable %[[VAL_10]], %[[VAL_2]], %[[VAL_15]], %[[VAL_3]], %[[VAL_12]] : i1, i1
|
||||
// CHECK: %[[VAL_17:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_18:.*]] = comb.xor %[[VAL_4]], %[[VAL_17]] : i1
|
||||
// CHECK: %[[VAL_19:.*]] = comb.or %[[VAL_10]], %[[VAL_18]] : i1
|
||||
// CHECK: %[[VAL_20:.*]] = comb.and %[[VAL_16]], %[[VAL_19]]
|
||||
// CHECK: %[[VAL_21:.*]] = seq.compreg.ce sym @MyPipeline_a0 %[[VAL_11]], %[[VAL_2]], %[[VAL_20]] {name = "MyPipeline_a0"} : i32, i1
|
||||
// CHECK: %[[VAL_22:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_23:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_24:.*]] = comb.xor %[[VAL_4]], %[[VAL_23]] : i1
|
||||
// CHECK: %[[VAL_25:.*]] = comb.or %[[VAL_10]], %[[VAL_24]] : i1
|
||||
// CHECK: %[[VAL_26:.*]] = seq.compreg.ce sym @MyPipeline_stage3_enable %[[VAL_20]], %[[VAL_2]], %[[VAL_25]], %[[VAL_3]], %[[VAL_22]] : i1, i1
|
||||
// CHECK: %[[VAL_27:.*]] = hw.constant true
|
||||
// CHECK: %[[VAL_28:.*]] = comb.xor %[[VAL_4]], %[[VAL_27]] : i1
|
||||
// CHECK: %[[VAL_29:.*]] = comb.or %[[VAL_10]], %[[VAL_28]] : i1
|
||||
// CHECK: %[[VAL_30:.*]] = comb.and %[[VAL_26]], %[[VAL_29]]
|
||||
// CHECK: hw.output %[[VAL_21]] : i32
|
||||
// CHECK: }
|
||||
|
||||
hw.module @testStallability(%arg0: i32, %go: i1, %clk: i1, %rst: i1, %stall: i1) -> (out: i32) {
|
||||
%out, %done = pipeline.scheduled "MyPipeline"(%a0 : i32 = %arg0)
|
||||
stall(%s = %stall) clock(%c = %clk) reset(%r = %rst) go(%g = %go)
|
||||
{stallability = [true, false, true]} -> (out : i32) {
|
||||
pipeline.stage ^bb1 regs("a0" = %a0 : i32)
|
||||
^bb1(%a0_0: i32, %s1_enable: i1): // pred: ^bb0
|
||||
pipeline.stage ^bb2 regs("a0" = %a0_0 : i32)
|
||||
^bb2(%a0_1: i32, %s2_enable: i1): // pred: ^bb1
|
||||
pipeline.stage ^bb3 regs("a0" = %a0_1 : i32)
|
||||
^bb3(%a0_2: i32, %s3_enable: i1): // pred: ^bb2
|
||||
pipeline.return %a0_2 : i32
|
||||
}
|
||||
hw.output %out : i32
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_5]], %[[VAL_6]] : i32
|
||||
// CHECK: %[[VAL_8:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_6]] : i32
|
||||
// CHECK: %[[VAL_9:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_10:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_9]] : i1
|
||||
// CHECK: %[[VAL_10:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_9]] : i1
|
||||
// CHECK: %[[VAL_11:.*]] = comb.add %[[VAL_7]], %[[VAL_8]] : i32
|
||||
// CHECK: hw.output %[[VAL_11]], %[[VAL_10]] : i32, i1
|
||||
// CHECK: }
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// CHECK: %[[VAL_8:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_5]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_3]] : i32
|
||||
// CHECK: %[[VAL_10:.*]] = hw.constant false
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_10]] : i1
|
||||
// CHECK: %[[VAL_11:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_10]] : i1
|
||||
// CHECK: %[[VAL_12:.*]] = comb.add %[[VAL_8]], %[[VAL_9]] : i32
|
||||
// CHECK: hw.output %[[VAL_12]], %[[VAL_11]] : i32, i1
|
||||
// CHECK: }
|
||||
|
@ -26,7 +26,7 @@
|
|||
// CGATE: %[[VAL_11:.*]] = seq.compreg sym @p0_stage0_reg0 %[[VAL_5]], %[[VAL_10]] : i32
|
||||
// CGATE: %[[VAL_12:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_8]] : i32
|
||||
// CGATE: %[[VAL_13:.*]] = hw.constant false
|
||||
// CGATE: %[[VAL_14:.*]] = seq.compreg sym @p0_stage0_valid %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_13]] : i1
|
||||
// CGATE: %[[VAL_14:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_13]] : i1
|
||||
// CGATE: %[[VAL_15:.*]] = comb.add %[[VAL_11]], %[[VAL_12]] : i32
|
||||
// CGATE: hw.output %[[VAL_15]], %[[VAL_14]] : i32, i1
|
||||
// CGATE: }
|
||||
|
|
|
@ -180,3 +180,39 @@ hw.module @invalid_clock_gate(%arg : i32, %go : i1, %clk : i1, %rst : i1) -> ()
|
|||
}
|
||||
hw.output
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
hw.module @noStallSignalWithStallability(%arg0 : i32, %go : i1, %clk : i1, %rst : i1) -> (out: i32) {
|
||||
// expected-error @+1 {{'pipeline.scheduled' op cannot specify stallability without a stall signal.}}
|
||||
%0:2 = pipeline.scheduled "MyPipeline"(%a0 : i32 = %arg0) clock(%c = %clk) reset(%r = %rst) go(%g = %go)
|
||||
{stallability = [true, false, true]}
|
||||
-> (out: i32) {
|
||||
pipeline.stage ^bb1
|
||||
^bb1(%s1_enable : i1):
|
||||
pipeline.stage ^bb2
|
||||
^bb2(%s2_enable : i1):
|
||||
pipeline.stage ^bb3
|
||||
^bb3(%s3_enable : i1):
|
||||
pipeline.return %a0 : i32
|
||||
}
|
||||
hw.output %0 : i32
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
hw.module @incorrectStallabilitySize(%arg0 : i32, %go : i1, %clk : i1, %rst : i1, %stall : i1) -> (out: i32) {
|
||||
// expected-error @+1 {{'pipeline.scheduled' op stallability array must be the same length as the number of stages. Pipeline has 3 stages but array had 2 elements.}}
|
||||
%0:2 = pipeline.scheduled "MyPipeline"(%a0 : i32 = %arg0) stall(%s = %stall) clock(%c = %clk) reset(%r = %rst) go(%g = %go)
|
||||
{stallability = [true, false]}
|
||||
-> (out: i32) {
|
||||
pipeline.stage ^bb1
|
||||
^bb1(%s1_enable : i1):
|
||||
pipeline.stage ^bb2
|
||||
^bb2(%s2_enable : i1):
|
||||
pipeline.stage ^bb3
|
||||
^bb3(%s3_enable : i1):
|
||||
pipeline.return %a0 : i32
|
||||
}
|
||||
hw.output %0 : i32
|
||||
}
|
||||
|
|
|
@ -156,3 +156,20 @@ hw.module @withNames(%arg0 : i32, %arg1 : i32, %go : i1, %clk : i1, %rst : i1) -
|
|||
}
|
||||
hw.output %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: hw.module @withStallability(
|
||||
// CHECK: %out, %done = pipeline.scheduled "MyPipeline"(%a0 : i32 = %arg0) stall(%s = %stall) clock(%c = %clk) reset(%r = %rst) go(%g = %go) {stallability = [true, false, true]} -> (out : i32)
|
||||
hw.module @withStallability(%arg0 : i32, %go : i1, %clk : i1, %rst : i1, %stall : i1) -> (out: i32) {
|
||||
%0:2 = pipeline.scheduled "MyPipeline"(%a0 : i32 = %arg0) stall(%s = %stall) clock(%c = %clk) reset(%r = %rst) go(%g = %go)
|
||||
{stallability = [true, false, true]}
|
||||
-> (out: i32) {
|
||||
pipeline.stage ^bb1
|
||||
^bb1(%s1_enable : i1):
|
||||
pipeline.stage ^bb2
|
||||
^bb2(%s2_enable : i1):
|
||||
pipeline.stage ^bb3
|
||||
^bb3(%s3_enable : i1):
|
||||
pipeline.return %a0 : i32
|
||||
}
|
||||
hw.output %0 : i32
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue