Skip to content

Commit

Permalink
[LLHD] Remove RegOp
Browse files Browse the repository at this point in the history
  • Loading branch information
maerhart committed Aug 11, 2024
1 parent ac8c3a1 commit f3387df
Show file tree
Hide file tree
Showing 3 changed files with 0 additions and 340 deletions.
133 changes: 0 additions & 133 deletions include/circt/Dialect/LLHD/IR/SignalOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -175,139 +175,6 @@ def LLHD_DrvOp : LLHD_Op<"drv", [
let hasCanonicalizeMethod = 1;
}

def REG_MODE_LOW : I64EnumAttrCase<"low", 0>;
def REG_MODE_HIGH : I64EnumAttrCase<"high", 1>;
def REG_MODE_RISE : I64EnumAttrCase<"rise", 2>;
def REG_MODE_FALL : I64EnumAttrCase<"fall", 3>;
def REG_MODE_BOTH : I64EnumAttrCase<"both", 4>;

def LLHD_RegModeAttr : I64EnumAttr<"RegMode", "", [
REG_MODE_LOW, REG_MODE_HIGH, REG_MODE_RISE, REG_MODE_FALL, REG_MODE_BOTH
]> {
let cppNamespace = "::circt::llhd";
}

def LLHD_RegModeArrayAttr
: TypedArrayAttrBase<LLHD_RegModeAttr, "reg mode array attribute"> {}

def LLHD_RegOp : LLHD_Op<"reg", [
HasParent<"hw::HWModuleOp">,
AttrSizedOperandSegments
]> {
let summary = "Represents a storage element";
let description = [{
This instruction represents a storage element. It drives its output onto
the 'signal' value. An arbitrary amount of triggers can be added to the
storage element. However, at least one is required. They are quadruples
consisting of the new value to be stored if the trigger applies, the
mode and trigger value which specify when this trigger has to be applied
as well as a delay. Optionally, each triple may also have a gate
condition, in this case the trigger only applies if the gate is one. If
multiple triggers apply the left-most in the list takes precedence.

There are five modes available:

| Mode | Meaning |
|--------|-----------------------------------------------------------------|
| "low" | Storage element stores `value` while the `trigger` is low. Models active-low resets and low-transparent latches.
| "high" | Storage element stores `value` while the `trigger` is high. Models active-high resets and high-transparent latches.
| "rise" | Storage element stores `value` upon the rising edge of the `trigger`. Models rising-edge flip-flops.
| "fall" | Storage element stores `value` upon the falling edge of the `trigger`. Models falling-edge flip-flops.
| "both" | Storage element stores `value` upon the a rising or a falling edge of the `trigger`. Models dual-edge flip-flops.

This instruction may only be used in an LLHD entity.

Syntax:

```
reg-op ::= `llhd.reg` signal-ssa-value
( `,` `(` value-ssa-value `,` mode-string trigger-ssa-value `after`
delay-ssa-value ( `if` gate-ssa-value )? `:` value-type )+
attr-dict `:` signal-type
```

Examples:

A rising, falling, and dual-edge triggered flip-flop:

```mlir
llhd.reg %Q, (%D, "rise" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
llhd.reg %Q, (%D, "fall" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
llhd.reg %Q, (%D, "both" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
```

A rising-edge triggered flip-flop with active-low reset:

```mlir
llhd.reg %Q, (%init, "low" %RSTB after %T : !hw.inout<i8>),
(%D, "rise" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
```

A rising-edge triggered enable flip-flop with active-low reset:

```mlir
llhd.reg %Q, (%init, "low" %RSTB after %T : !hw.inout<i8>),
(%D, "rise" %CLK after %T if %EN : !hw.inout<i8>) : !hw.inout<i8>
```

A transparent-low and transparent-high latch:

```mlir
llhd.reg %Q, (%D, "low" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
llhd.reg %Q, (%D, "high" %CLK after %T : !hw.inout<i8>) : !hw.inout<i8>
```

An SR latch:

```mlir
%0 = llhd.const 0 : i1
%1 = llhd.const 1 : i1
llhd.reg %Q, (%0, "high" %R after %T : !hw.inout<i1>),
(%1, "high" %S after %T : !hw.inout<i1>) : !hw.inout<i1>
```
}];

let arguments = (ins
InOutType: $signal,
LLHD_RegModeArrayAttr: $modes,
Variadic<AnyTypeOf<[LLHD_AnyElementType, InOutType]>>: $values,
Variadic<I1>: $triggers,
Variadic<LLHD_TimeType>: $delays,
Variadic<I1>: $gates,
I64ArrayAttr: $gateMask);

let extraClassDeclaration = [{
static StringRef getModeAttrName() { return "modes"; }
static RegMode getRegModeByName(StringRef name) {
std::optional<RegMode> optional = symbolizeRegMode(name);
assert(optional && "Invalid RegMode string.");
return *optional;
}

bool hasGate(unsigned index) {
assert(index < getGateMask().getValue().size() && "Index out of range.");
return llvm::cast<IntegerAttr>(
getGateMask().getValue()[index]).getInt() != 0;
}

Value getGateAt(unsigned index) {
assert(index < getGateMask().getValue().size() && "Index out of range.");
if (!hasGate(index)) return Value();
return
getGates()[llvm::cast<IntegerAttr>(
getGateMask().getValue()[index]).getInt()-1];
}

RegMode getRegModeAt(unsigned index) {
assert(index < getModes().getValue().size() && "Index out of range.");
return (RegMode) llvm::cast<IntegerAttr>(
getModes().getValue()[index]).getInt();
}
}];

let hasVerifier = 1;
}

def DelayOp : LLHD_Op<"delay", [Pure, SameOperandsAndResultType]> {
let summary = "specifies value propagation delay";
let description = [{
Expand Down
187 changes: 0 additions & 187 deletions lib/Dialect/LLHD/IR/LLHDOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,193 +351,6 @@ LogicalResult llhd::ConnectOp::canonicalize(llhd::ConnectOp op,
return success();
}

//===----------------------------------------------------------------------===//
// RegOp
//===----------------------------------------------------------------------===//

ParseResult llhd::RegOp::parse(OpAsmParser &parser, OperationState &result) {
OpAsmParser::UnresolvedOperand signal;
Type signalType;
SmallVector<OpAsmParser::UnresolvedOperand, 8> valueOperands;
SmallVector<OpAsmParser::UnresolvedOperand, 8> triggerOperands;
SmallVector<OpAsmParser::UnresolvedOperand, 8> delayOperands;
SmallVector<OpAsmParser::UnresolvedOperand, 8> gateOperands;
SmallVector<Type, 8> valueTypes;
llvm::SmallVector<int64_t, 8> modesArray;
llvm::SmallVector<int64_t, 8> gateMask;
int64_t gateCount = 0;

if (parser.parseOperand(signal))
return failure();
while (succeeded(parser.parseOptionalComma())) {
OpAsmParser::UnresolvedOperand value;
OpAsmParser::UnresolvedOperand trigger;
OpAsmParser::UnresolvedOperand delay;
OpAsmParser::UnresolvedOperand gate;
Type valueType;
StringAttr modeAttr;
NamedAttrList attrStorage;

if (parser.parseLParen())
return failure();
if (parser.parseOperand(value) || parser.parseComma())
return failure();
if (parser.parseAttribute(modeAttr, parser.getBuilder().getNoneType(),
"modes", attrStorage))
return failure();
auto attrOptional = llhd::symbolizeRegMode(modeAttr.getValue());
if (!attrOptional)
return parser.emitError(parser.getCurrentLocation(),
"invalid string attribute");
modesArray.push_back(static_cast<int64_t>(*attrOptional));
if (parser.parseOperand(trigger))
return failure();
if (parser.parseKeyword("after") || parser.parseOperand(delay))
return failure();
if (succeeded(parser.parseOptionalKeyword("if"))) {
gateMask.push_back(++gateCount);
if (parser.parseOperand(gate))
return failure();
gateOperands.push_back(gate);
} else {
gateMask.push_back(0);
}
if (parser.parseColon() || parser.parseType(valueType) ||
parser.parseRParen())
return failure();
valueOperands.push_back(value);
triggerOperands.push_back(trigger);
delayOperands.push_back(delay);
valueTypes.push_back(valueType);
}
if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
parser.parseType(signalType))
return failure();
if (parser.resolveOperand(signal, signalType, result.operands))
return failure();
if (parser.resolveOperands(valueOperands, valueTypes,
parser.getCurrentLocation(), result.operands))
return failure();
for (auto operand : triggerOperands)
if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
result.operands))
return failure();
for (auto operand : delayOperands)
if (parser.resolveOperand(
operand, llhd::TimeType::get(parser.getBuilder().getContext()),
result.operands))
return failure();
for (auto operand : gateOperands)
if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
result.operands))
return failure();
result.addAttribute("gateMask",
parser.getBuilder().getI64ArrayAttr(gateMask));
result.addAttribute("modes", parser.getBuilder().getI64ArrayAttr(modesArray));
llvm::SmallVector<int32_t, 5> operandSizes;
operandSizes.push_back(1);
operandSizes.push_back(valueOperands.size());
operandSizes.push_back(triggerOperands.size());
operandSizes.push_back(delayOperands.size());
operandSizes.push_back(gateOperands.size());
result.addAttribute("operandSegmentSizes",
parser.getBuilder().getDenseI32ArrayAttr(operandSizes));

return success();
}

void llhd::RegOp::print(OpAsmPrinter &printer) {
printer << " " << getSignal();
for (size_t i = 0, e = getValues().size(); i < e; ++i) {
std::optional<llhd::RegMode> mode = llhd::symbolizeRegMode(
cast<IntegerAttr>(getModes().getValue()[i]).getInt());
if (!mode) {
emitError("invalid RegMode");
return;
}
printer << ", (" << getValues()[i] << ", \""
<< llhd::stringifyRegMode(*mode) << "\" " << getTriggers()[i]
<< " after " << getDelays()[i];
if (hasGate(i))
printer << " if " << getGateAt(i);
printer << " : " << getValues()[i].getType() << ")";
}
printer.printOptionalAttrDict((*this)->getAttrs(),
{"modes", "gateMask", "operandSegmentSizes"});
printer << " : " << getSignal().getType();
}

LogicalResult llhd::RegOp::verify() {
// At least one trigger has to be present
if (getTriggers().size() < 1)
return emitError("At least one trigger quadruple has to be present.");

// Values variadic operand must have the same size as the triggers variadic
if (getValues().size() != getTriggers().size())
return emitOpError("Number of 'values' is not equal to the number of "
"'triggers', got ")
<< getValues().size() << " modes, but " << getTriggers().size()
<< " triggers!";

// Delay variadic operand must have the same size as the triggers variadic
if (getDelays().size() != getTriggers().size())
return emitOpError("Number of 'delays' is not equal to the number of "
"'triggers', got ")
<< getDelays().size() << " modes, but " << getTriggers().size()
<< " triggers!";

// Array Attribute of RegModes must have the same number of elements as the
// variadics
if (getModes().size() != getTriggers().size())
return emitOpError("Number of 'modes' is not equal to the number of "
"'triggers', got ")
<< getModes().size() << " modes, but " << getTriggers().size()
<< " triggers!";

// Array Attribute 'gateMask' must have the same number of elements as the
// triggers and values variadics
if (getGateMask().size() != getTriggers().size())
return emitOpError("Size of 'gateMask' is not equal to the size of "
"'triggers', got ")
<< getGateMask().size() << " modes, but " << getTriggers().size()
<< " triggers!";

// Number of non-zero elements in 'gateMask' has to be the same as the size
// of the gates variadic, also each number from 1 to size-1 has to occur
// only once and in increasing order
unsigned counter = 0;
unsigned prevElement = 0;
for (Attribute maskElem : getGateMask().getValue()) {
int64_t val = cast<IntegerAttr>(maskElem).getInt();
if (val < 0)
return emitError("Element in 'gateMask' must not be negative!");
if (val == 0)
continue;
if (val != ++prevElement)
return emitError(
"'gateMask' has to contain every number from 1 to the "
"number of gates minus one exactly once in increasing order "
"(may have zeros in-between).");
counter++;
}
if (getGates().size() != counter)
return emitError("The number of non-zero elements in 'gateMask' and the "
"size of the 'gates' variadic have to match.");

// Each value must be either the same type as the 'signal' or the underlying
// type of the 'signal'
for (auto val : getValues()) {
if (val.getType() != getSignal().getType() &&
val.getType() !=
cast<hw::InOutType>(getSignal().getType()).getElementType()) {
return emitOpError(
"type of each 'value' has to be either the same as the "
"type of 'signal' or the underlying type of 'signal'");
}
}
return success();
}

#include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"

#define GET_OP_CLASSES
Expand Down
20 changes: 0 additions & 20 deletions test/Dialect/LLHD/IR/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -199,23 +199,3 @@ hw.module @check_wait_3 (inout %arg0 : i64, inout %arg1 : i1) {
llhd.halt
}
}

// CHECK-LABEL: @check_reg
// CHECK-SAME: %[[IN64:.*]] : i64
hw.module @check_reg(inout %in64 : i64) {
// CHECK: %[[C1:.*]] = hw.constant
%c1 = hw.constant 0 : i1
// CHECK-NEXT: %[[C64:.*]] = hw.constant
%c64 = hw.constant 0 : i64
// CHECK-NEXT: %[[TIME:.*]] = llhd.constant_time
%time = llhd.constant_time #llhd.time<1ns, 0d, 0e>
// one trigger with optional gate
// CHECK-NEXT: llhd.reg %[[IN64]], (%[[C64]], "low" %[[C1]] after %[[TIME]] if %[[C1]] : i64) : !hw.inout<i64>
"llhd.reg"(%in64, %c64, %c1, %time, %c1) {modes=[0], gateMask=[1], operandSegmentSizes=array<i32: 1,1,1,1,1>} : (!hw.inout<i64>, i64, i1, !llhd.time, i1) -> ()
// two triggers with optional gates
// CHECK-NEXT: llhd.reg %[[IN64]], (%[[C64]], "low" %[[C1]] after %[[TIME]] if %[[C1]] : i64), (%[[IN64]], "high" %[[C1]] after %[[TIME]] if %[[C1]] : !hw.inout<i64>) : !hw.inout<i64>
"llhd.reg"(%in64, %c64, %in64, %c1, %c1, %time, %time, %c1, %c1) {modes=[0,1], gateMask=[1,2], operandSegmentSizes=array<i32: 1,2,2,2,2>} : (!hw.inout<i64>, i64, !hw.inout<i64>, i1, i1, !llhd.time, !llhd.time, i1, i1) -> ()
// two triggers with only one optional gate
// CHECK-NEXT: llhd.reg %[[IN64]], (%[[C64]], "low" %[[C1]] after %[[TIME]] : i64), (%[[IN64]], "high" %[[C1]] after %[[TIME]] if %[[C1]] : !hw.inout<i64>) : !hw.inout<i64>
"llhd.reg"(%in64, %c64, %in64, %c1, %c1, %time, %time, %c1) {modes=[0,1], gateMask=[0,1], operandSegmentSizes=array<i32: 1,2,2,2,1>} : (!hw.inout<i64>, i64, !hw.inout<i64>, i1, i1, !llhd.time, !llhd.time, i1) -> ()
}

0 comments on commit f3387df

Please sign in to comment.