mirror of https://github.com/llvm/circt.git
[HandshakeToHW] Add `handshake.memory` lowering (#4175)
This commit is contained in:
parent
7567eead7a
commit
a428ac7626
|
@ -35,14 +35,14 @@
|
||||||
namespace circt {
|
namespace circt {
|
||||||
namespace handshake {
|
namespace handshake {
|
||||||
|
|
||||||
struct ExtMemLoadInterface {
|
struct MemLoadInterface {
|
||||||
unsigned index;
|
unsigned index;
|
||||||
mlir::Value addressIn;
|
mlir::Value addressIn;
|
||||||
mlir::Value dataOut;
|
mlir::Value dataOut;
|
||||||
mlir::Value doneOut;
|
mlir::Value doneOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExtMemStoreInterface {
|
struct MemStoreInterface {
|
||||||
unsigned index;
|
unsigned index;
|
||||||
mlir::Value addressIn;
|
mlir::Value addressIn;
|
||||||
mlir::Value dataIn;
|
mlir::Value dataIn;
|
||||||
|
|
|
@ -679,6 +679,12 @@ def MemoryOp : Handshake_Op<"memory", [
|
||||||
ins "ValueRange":$operands, "int":$outputs, "int":$control_outputs, "bool":$lsq,
|
ins "ValueRange":$operands, "int":$outputs, "int":$control_outputs, "bool":$lsq,
|
||||||
"int":$id, "Value":$memref)>
|
"int":$id, "Value":$memref)>
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let extraClassDeclaration = [{
|
||||||
|
llvm::SmallVector<handshake::MemLoadInterface> getLoadPorts();
|
||||||
|
llvm::SmallVector<handshake::MemStoreInterface> getStorePorts();
|
||||||
|
}];
|
||||||
|
|
||||||
let assemblyFormat = "`[` `ld` `=` $ldCount `,` `st` `=` $stCount `]` `(` $inputs `)` attr-dict `:` $memRefType `,` functional-type($inputs, $outputs)";
|
let assemblyFormat = "`[` `ld` `=` $ldCount `,` `st` `=` $stCount `]` `(` $inputs `)` attr-dict `:` $memRefType `,` functional-type($inputs, $outputs)";
|
||||||
let hasVerifier = 1;
|
let hasVerifier = 1;
|
||||||
}
|
}
|
||||||
|
@ -726,8 +732,8 @@ def ExternalMemoryOp : Handshake_Op<"extmemory", [
|
||||||
return getMemref().getType().cast<mlir::MemRefType>();
|
return getMemref().getType().cast<mlir::MemRefType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::SmallVector<handshake::ExtMemLoadInterface> getLoadPorts();
|
llvm::SmallVector<handshake::MemLoadInterface> getLoadPorts();
|
||||||
llvm::SmallVector<handshake::ExtMemStoreInterface> getStorePorts();
|
llvm::SmallVector<handshake::MemStoreInterface> getStorePorts();
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,11 @@ def HLMemOp : SeqOp<"hlmem", [
|
||||||
HLMemType getMemType() { return getHandle().getType().cast<HLMemType>(); }
|
HLMemType getMemType() { return getHandle().getType().cast<HLMemType>(); }
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
let builders = [
|
||||||
|
OpBuilder<(ins "Value":$clk, "Value":$rst, "StringRef":$symName,
|
||||||
|
"llvm::ArrayRef<int64_t>":$shape, "Type":$elementType)>
|
||||||
|
];
|
||||||
|
|
||||||
let assemblyFormat = "$sym_name $clk `,` $rst attr-dict `:` type($handle)";
|
let assemblyFormat = "$sym_name $clk `,` $rst attr-dict `:` type($handle)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +161,9 @@ def ReadPortOp : SeqOp<"read", [
|
||||||
);
|
);
|
||||||
let results = (outs AnyType:$readData);
|
let results = (outs AnyType:$readData);
|
||||||
let hasCustomAssemblyFormat = 1;
|
let hasCustomAssemblyFormat = 1;
|
||||||
|
let builders = [OpBuilder<(
|
||||||
|
ins "Value":$memory, "ValueRange":$addresses, "Value":$rdEn, "unsigned":$latency)>
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
def WritePortOp : SeqOp<"write", [
|
def WritePortOp : SeqOp<"write", [
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
// RUN: hlstool %s --dynamic-firrtl --buffering-strategy=cycles --verilog --lowering-options=disallowLocalVariables > %t.sv && \
|
// RUN: hlstool %s --dynamic-firrtl --buffering-strategy=cycles --verilog --lowering-options=disallowLocalVariables > %t.sv && \
|
||||||
// RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%T --topLevel=top --pythonModule=tp_memory --pythonFolder=%S %t.sv 2>&1 | FileCheck %s
|
// RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%T --topLevel=top --pythonModule=tp_memory --pythonFolder=%S %t.sv 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
// RUN: hlstool %s --sv-trace-iverilog --dynamic-hw --buffering-strategy=cycles --verilog --lowering-options=disallowLocalVariables > %t.sv && \
|
||||||
|
// RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%T --topLevel=top --pythonModule=tp_memory --pythonFolder=%S %t.sv 2>&1 | FileCheck %s
|
||||||
|
|
||||||
// Locking the circt should yield the same result
|
// Locking the circt should yield the same result
|
||||||
// RUN: hlstool %s --dynamic-firrtl --buffering-strategy=all --dynamic-parallelism=locking --verilog --lowering-options=disallowLocalVariables > %t.sv && \
|
// RUN: hlstool %s --dynamic-firrtl --buffering-strategy=all --dynamic-parallelism=locking --verilog --lowering-options=disallowLocalVariables > %t.sv && \
|
||||||
// RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%T --topLevel=top --pythonModule=tp_memory --pythonFolder=%S %t.sv 2>&1 | FileCheck %s
|
// RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%T --topLevel=top --pythonModule=tp_memory --pythonFolder=%S %t.sv 2>&1 | FileCheck %s
|
||||||
|
|
|
@ -528,6 +528,16 @@ struct RTLBuilder {
|
||||||
// Bitwise 'not'.
|
// Bitwise 'not'.
|
||||||
Value bNot(Value value, std::optional<StringRef> name = {}) {
|
Value bNot(Value value, std::optional<StringRef> name = {}) {
|
||||||
auto allOnes = constant(value.getType().getIntOrFloatBitWidth(), -1);
|
auto allOnes = constant(value.getType().getIntOrFloatBitWidth(), -1);
|
||||||
|
std::string inferedName;
|
||||||
|
if (!name) {
|
||||||
|
// Try to create a name from the input value.
|
||||||
|
if (auto valueName =
|
||||||
|
value.getDefiningOp()->getAttrOfType<StringAttr>("sv.namehint")) {
|
||||||
|
inferedName = ("not_" + valueName.getValue()).str();
|
||||||
|
name = inferedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return buildNamedOp(
|
return buildNamedOp(
|
||||||
[&]() { return b.create<comb::XorOp>(loc, value, allOnes); }, name);
|
[&]() { return b.create<comb::XorOp>(loc, value, allOnes); }, name);
|
||||||
|
|
||||||
|
@ -1452,6 +1462,111 @@ public:
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MemoryConversionPattern
|
||||||
|
: public HandshakeConversionPattern<handshake::MemoryOp> {
|
||||||
|
public:
|
||||||
|
using HandshakeConversionPattern<
|
||||||
|
handshake::MemoryOp>::HandshakeConversionPattern;
|
||||||
|
void buildModule(handshake::MemoryOp op, BackedgeBuilder &bb, RTLBuilder &s,
|
||||||
|
hw::HWModulePortAccessor &ports) const override {
|
||||||
|
auto loc = op.getLoc();
|
||||||
|
|
||||||
|
// Gather up the load and store ports.
|
||||||
|
auto unwrappedIO = this->unwrapIO(s, bb, ports);
|
||||||
|
struct LoadPort {
|
||||||
|
InputHandshake &addr;
|
||||||
|
OutputHandshake &data;
|
||||||
|
OutputHandshake &done;
|
||||||
|
};
|
||||||
|
struct StorePort {
|
||||||
|
InputHandshake &addr;
|
||||||
|
InputHandshake &data;
|
||||||
|
OutputHandshake &done;
|
||||||
|
};
|
||||||
|
SmallVector<LoadPort, 4> loadPorts;
|
||||||
|
SmallVector<StorePort, 4> storePorts;
|
||||||
|
|
||||||
|
unsigned stCount = op.getStCount();
|
||||||
|
unsigned ldCount = op.getLdCount();
|
||||||
|
for (unsigned i = 0, e = ldCount; i != e; ++i) {
|
||||||
|
LoadPort port = {unwrappedIO.inputs[stCount * 2 + i],
|
||||||
|
unwrappedIO.outputs[i],
|
||||||
|
unwrappedIO.outputs[ldCount + stCount + i]};
|
||||||
|
loadPorts.push_back(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0, e = stCount; i != e; ++i) {
|
||||||
|
StorePort port = {unwrappedIO.inputs[i * 2 + 1],
|
||||||
|
unwrappedIO.inputs[i * 2],
|
||||||
|
unwrappedIO.outputs[ldCount + i]};
|
||||||
|
storePorts.push_back(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// used to drive the data wire of the control-only channels.
|
||||||
|
auto c0I0 = s.constant(0, 0);
|
||||||
|
|
||||||
|
auto cl2dim = llvm::Log2_64_Ceil(op.getMemRefType().getShape()[0]);
|
||||||
|
auto hlmem = s.b.create<seq::HLMemOp>(
|
||||||
|
loc, s.clk, s.rst, "_handshake_memory_" + std::to_string(op.getId()),
|
||||||
|
op.getMemRefType().getShape(), op.getMemRefType().getElementType());
|
||||||
|
|
||||||
|
// Create load ports...
|
||||||
|
for (auto &ld : loadPorts) {
|
||||||
|
llvm::SmallVector<Value> addresses = {s.truncate(ld.addr.data, cl2dim)};
|
||||||
|
auto readData = s.b.create<seq::ReadPortOp>(loc, hlmem.getHandle(),
|
||||||
|
addresses, ld.addr.valid,
|
||||||
|
/*latency=*/0);
|
||||||
|
ld.data.data->setValue(readData);
|
||||||
|
ld.done.data->setValue(c0I0);
|
||||||
|
// Create control fork for the load address valid and ready signals.
|
||||||
|
buildForkLogic(s, bb, ld.addr, {ld.data, ld.done});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create store ports...
|
||||||
|
for (auto &st : storePorts) {
|
||||||
|
// Create a register to buffer the valid path by 1 cycle, to match the
|
||||||
|
// write latency of 1.
|
||||||
|
auto writeValidBufferMuxBE = bb.get(s.b.getI1Type());
|
||||||
|
auto writeValidBuffer =
|
||||||
|
s.reg("writeValidBuffer", writeValidBufferMuxBE, s.constant(1, 0));
|
||||||
|
st.done.valid->setValue(writeValidBuffer);
|
||||||
|
st.done.data->setValue(c0I0);
|
||||||
|
|
||||||
|
// Create the logic for when both the buffered write valid signal and the
|
||||||
|
// store complete ready signal are asserted.
|
||||||
|
auto storeCompleted =
|
||||||
|
s.bAnd({st.done.ready, writeValidBuffer}, "storeCompleted");
|
||||||
|
|
||||||
|
// Create a signal for when the write valid buffer is empty or the output
|
||||||
|
// is ready.
|
||||||
|
auto notWriteValidBuffer = s.bNot(writeValidBuffer);
|
||||||
|
auto emptyOrComplete =
|
||||||
|
s.bOr({notWriteValidBuffer, storeCompleted}, "emptyOrComplete");
|
||||||
|
|
||||||
|
// Connect the gate to both the store address ready and store data ready
|
||||||
|
st.addr.ready->setValue(emptyOrComplete);
|
||||||
|
st.data.ready->setValue(emptyOrComplete);
|
||||||
|
|
||||||
|
// Create a wire for when both the store address and data are valid.
|
||||||
|
auto writeValid = s.bAnd({st.addr.valid, st.data.valid}, "writeValid");
|
||||||
|
|
||||||
|
// Create a mux that drives the buffer input. If the emptyOrComplete
|
||||||
|
// signal is asserted, the mux selects the writeValid signal. Otherwise,
|
||||||
|
// it selects the buffer output, keeping the output registered until the
|
||||||
|
// emptyOrComplete signal is asserted.
|
||||||
|
writeValidBufferMuxBE.setValue(
|
||||||
|
s.mux(emptyOrComplete, {writeValidBuffer, writeValid}));
|
||||||
|
|
||||||
|
// Instantiate the write port operation - truncate address width to memory
|
||||||
|
// width.
|
||||||
|
llvm::SmallVector<Value> addresses = {s.truncate(st.addr.data, cl2dim)};
|
||||||
|
s.b.create<seq::WritePortOp>(loc, hlmem.getHandle(), addresses,
|
||||||
|
st.data.data, writeValid,
|
||||||
|
/*latency=*/1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
class SinkConversionPattern : public HandshakeConversionPattern<SinkOp> {
|
class SinkConversionPattern : public HandshakeConversionPattern<SinkOp> {
|
||||||
public:
|
public:
|
||||||
using HandshakeConversionPattern<SinkOp>::HandshakeConversionPattern;
|
using HandshakeConversionPattern<SinkOp>::HandshakeConversionPattern;
|
||||||
|
@ -1792,7 +1907,7 @@ static LogicalResult convertFuncOp(ESITypeConverter &typeConverter,
|
||||||
ComparisonConversionPattern, BufferConversionPattern,
|
ComparisonConversionPattern, BufferConversionPattern,
|
||||||
SourceConversionPattern, SinkConversionPattern, ConstantConversionPattern,
|
SourceConversionPattern, SinkConversionPattern, ConstantConversionPattern,
|
||||||
MergeConversionPattern, ControlMergeConversionPattern,
|
MergeConversionPattern, ControlMergeConversionPattern,
|
||||||
LoadConversionPattern, StoreConversionPattern,
|
LoadConversionPattern, StoreConversionPattern, MemoryConversionPattern,
|
||||||
// Arith operations.
|
// Arith operations.
|
||||||
ExtendConversionPattern<arith::ExtUIOp, /*signExtend=*/false>,
|
ExtendConversionPattern<arith::ExtUIOp, /*signExtend=*/false>,
|
||||||
ExtendConversionPattern<arith::ExtSIOp, /*signExtend=*/true>,
|
ExtendConversionPattern<arith::ExtSIOp, /*signExtend=*/true>,
|
||||||
|
|
|
@ -105,6 +105,49 @@ static void printOp(OpAsmPrinter &p, Operation *op, bool explicitSize) {
|
||||||
}
|
}
|
||||||
} // namespace sost
|
} // namespace sost
|
||||||
|
|
||||||
|
template <typename TMemOp>
|
||||||
|
llvm::SmallVector<handshake::MemLoadInterface> getLoadPorts(TMemOp op) {
|
||||||
|
llvm::SmallVector<MemLoadInterface> ports;
|
||||||
|
// Memory interface refresher:
|
||||||
|
// Operands:
|
||||||
|
// all stores (stdata1, staddr1, stdata2, staddr2, ...)
|
||||||
|
// then all loads (ldaddr1, ldaddr2,...)
|
||||||
|
// Outputs: load addresses (lddata1, lddata2, ...), followed by all none
|
||||||
|
// outputs, ordered as operands(stnone1, stnone2, ... ldnone1, ldnone2, ...)
|
||||||
|
unsigned stCount = op.getStCount();
|
||||||
|
unsigned ldCount = op.getLdCount();
|
||||||
|
for (unsigned i = 0, e = ldCount; i != e; ++i) {
|
||||||
|
MemLoadInterface ldif;
|
||||||
|
ldif.index = i;
|
||||||
|
ldif.addressIn = op.getInputs()[stCount * 2 + i];
|
||||||
|
ldif.dataOut = op.getResult(i);
|
||||||
|
ldif.doneOut = op.getResult(ldCount + stCount + i);
|
||||||
|
ports.push_back(ldif);
|
||||||
|
}
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TMemOp>
|
||||||
|
llvm::SmallVector<handshake::MemStoreInterface> getStorePorts(TMemOp op) {
|
||||||
|
llvm::SmallVector<MemStoreInterface> ports;
|
||||||
|
// Memory interface refresher:
|
||||||
|
// Operands:
|
||||||
|
// all stores (stdata1, staddr1, stdata2, staddr2, ...)
|
||||||
|
// then all loads (ldaddr1, ldaddr2,...)
|
||||||
|
// Outputs: load data (lddata1, lddata2, ...), followed by all none
|
||||||
|
// outputs, ordered as operands(stnone1, stnone2, ... ldnone1, ldnone2, ...)
|
||||||
|
unsigned ldCount = op.getLdCount();
|
||||||
|
for (unsigned i = 0, e = op.getStCount(); i != e; ++i) {
|
||||||
|
MemStoreInterface stif;
|
||||||
|
stif.index = i;
|
||||||
|
stif.dataIn = op.getInputs()[i * 2];
|
||||||
|
stif.addressIn = op.getInputs()[i * 2 + 1];
|
||||||
|
stif.doneOut = op.getResult(ldCount + i);
|
||||||
|
ports.push_back(stif);
|
||||||
|
}
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
|
||||||
void ForkOp::build(OpBuilder &builder, OperationState &result, Value operand,
|
void ForkOp::build(OpBuilder &builder, OperationState &result, Value operand,
|
||||||
int outputs) {
|
int outputs) {
|
||||||
auto type = operand.getType();
|
auto type = operand.getType();
|
||||||
|
@ -1230,6 +1273,16 @@ void ExternalMemoryOp::build(OpBuilder &builder, OperationState &result,
|
||||||
result.addAttribute("stCount", builder.getIntegerAttr(i32Type, stCount));
|
result.addAttribute("stCount", builder.getIntegerAttr(i32Type, stCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::SmallVector<handshake::MemLoadInterface>
|
||||||
|
ExternalMemoryOp::getLoadPorts() {
|
||||||
|
return ::getLoadPorts(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::SmallVector<handshake::MemStoreInterface>
|
||||||
|
ExternalMemoryOp::getStorePorts() {
|
||||||
|
return ::getStorePorts(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryOp::build(OpBuilder &builder, OperationState &result,
|
void MemoryOp::build(OpBuilder &builder, OperationState &result,
|
||||||
ValueRange operands, int outputs, int controlOutputs,
|
ValueRange operands, int outputs, int controlOutputs,
|
||||||
bool lsq, int id, Value memref) {
|
bool lsq, int id, Value memref) {
|
||||||
|
@ -1256,47 +1309,12 @@ void MemoryOp::build(OpBuilder &builder, OperationState &result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::SmallVector<handshake::ExtMemLoadInterface>
|
llvm::SmallVector<handshake::MemLoadInterface> MemoryOp::getLoadPorts() {
|
||||||
ExternalMemoryOp::getLoadPorts() {
|
return ::getLoadPorts(*this);
|
||||||
llvm::SmallVector<ExtMemLoadInterface> ports;
|
|
||||||
// Extmem interface refresher:
|
|
||||||
// Operands:
|
|
||||||
// all stores (stdata1, staddr1, stdata2, staddr2, ...)
|
|
||||||
// then all loads (ldaddr1, ldaddr2,...)
|
|
||||||
// Outputs: load addresses (lddata1, lddata2, ...), followed by all none
|
|
||||||
// outputs, ordered as operands(stnone1, stnone2, ... ldnone1, ldnone2, ...)
|
|
||||||
unsigned stCount = getStCount();
|
|
||||||
unsigned ldCount = getLdCount();
|
|
||||||
for (unsigned i = 0, e = ldCount; i != e; ++i) {
|
|
||||||
ExtMemLoadInterface ldif;
|
|
||||||
ldif.index = i;
|
|
||||||
ldif.addressIn = getInputs()[stCount * 2 + i];
|
|
||||||
ldif.dataOut = getResult(i);
|
|
||||||
ldif.doneOut = getResult(ldCount + stCount + i);
|
|
||||||
ports.push_back(ldif);
|
|
||||||
}
|
|
||||||
return ports;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::SmallVector<handshake::ExtMemStoreInterface>
|
llvm::SmallVector<handshake::MemStoreInterface> MemoryOp::getStorePorts() {
|
||||||
ExternalMemoryOp::getStorePorts() {
|
return ::getStorePorts(*this);
|
||||||
llvm::SmallVector<ExtMemStoreInterface> ports;
|
|
||||||
// Extmem interface refresher:
|
|
||||||
// Operands:
|
|
||||||
// all stores (stdata1, staddr1, stdata2, staddr2, ...)
|
|
||||||
// then all loads (ldaddr1, ldaddr2,...)
|
|
||||||
// Outputs: load data (lddata1, lddata2, ...), followed by all none
|
|
||||||
// outputs, ordered as operands(stnone1, stnone2, ... ldnone1, ldnone2, ...)
|
|
||||||
unsigned ldCount = getLdCount();
|
|
||||||
for (unsigned i = 0, e = getStCount(); i != e; ++i) {
|
|
||||||
ExtMemStoreInterface stif;
|
|
||||||
stif.index = i;
|
|
||||||
stif.dataIn = getInputs()[i * 2];
|
|
||||||
stif.addressIn = getInputs()[i * 2 + 1];
|
|
||||||
stif.doneOut = getResult(ldCount + i);
|
|
||||||
ports.push_back(stif);
|
|
||||||
}
|
|
||||||
return ports;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handshake::MemoryOp::allocateMemory(
|
bool handshake::MemoryOp::allocateMemory(
|
||||||
|
|
|
@ -267,7 +267,7 @@ static Value truncateToMemoryWidth(Location loc, OpBuilder &b, Value v,
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value plumbLoadPort(Location loc, OpBuilder &b,
|
static Value plumbLoadPort(Location loc, OpBuilder &b,
|
||||||
const handshake::ExtMemLoadInterface &ldif,
|
const handshake::MemLoadInterface &ldif,
|
||||||
Value loadData, MemRefType memrefType) {
|
Value loadData, MemRefType memrefType) {
|
||||||
// We need to feed both the load data and the load done outputs.
|
// We need to feed both the load data and the load done outputs.
|
||||||
// Fork the extracted load data into two, and 'join' the second one to
|
// Fork the extracted load data into two, and 'join' the second one to
|
||||||
|
@ -287,7 +287,7 @@ static Value plumbLoadPort(Location loc, OpBuilder &b,
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value plumbStorePort(Location loc, OpBuilder &b,
|
static Value plumbStorePort(Location loc, OpBuilder &b,
|
||||||
const handshake::ExtMemStoreInterface &stif,
|
const handshake::MemStoreInterface &stif,
|
||||||
Value done, Type outType, MemRefType memrefType) {
|
Value done, Type outType, MemRefType memrefType) {
|
||||||
stif.doneOut.replaceAllUsesWith(done);
|
stif.doneOut.replaceAllUsesWith(done);
|
||||||
// Return the store address and data to be fed to the top-level output.
|
// Return the store address and data to be fed to the top-level output.
|
||||||
|
|
|
@ -130,6 +130,13 @@ void ReadPortOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
|
||||||
setNameFn(getReadData(), (memName + "_rdata").str());
|
setNameFn(getReadData(), (memName + "_rdata").str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReadPortOp::build(OpBuilder &builder, OperationState &result, Value memory,
|
||||||
|
ValueRange addresses, Value rdEn, unsigned latency) {
|
||||||
|
auto memType = memory.getType().cast<seq::HLMemType>();
|
||||||
|
ReadPortOp::build(builder, result, memType.getElementType(), memory,
|
||||||
|
addresses, rdEn, latency);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// WritePortOp
|
// WritePortOp
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -181,6 +188,13 @@ void HLMemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
|
||||||
setNameFn(getHandle(), getName());
|
setNameFn(getHandle(), getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HLMemOp::build(OpBuilder &builder, OperationState &result, Value clk,
|
||||||
|
Value rst, StringRef symName, llvm::ArrayRef<int64_t> shape,
|
||||||
|
Type elementType) {
|
||||||
|
HLMemType t = HLMemType::get(builder.getContext(), shape, elementType);
|
||||||
|
HLMemOp::build(builder, result, t, clk, rst, symName);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// CompRegOp
|
// CompRegOp
|
||||||
|
|
||||||
|
|
|
@ -255,6 +255,7 @@ static void loadFIRRTLLoweringPipeline(OpPassManager &pm) {
|
||||||
|
|
||||||
static void loadHWLoweringPipeline(OpPassManager &pm) {
|
static void loadHWLoweringPipeline(OpPassManager &pm) {
|
||||||
pm.addPass(createSimpleCanonicalizerPass());
|
pm.addPass(createSimpleCanonicalizerPass());
|
||||||
|
pm.nest<hw::HWModuleOp>().addPass(circt::seq::createLowerSeqHLMemPass());
|
||||||
pm.nest<hw::HWModuleOp>().addPass(seq::createSeqFIRRTLLowerToSVPass());
|
pm.nest<hw::HWModuleOp>().addPass(seq::createSeqFIRRTLLowerToSVPass());
|
||||||
pm.addPass(sv::createHWMemSimImplPass(false, false));
|
pm.addPass(sv::createHWMemSimImplPass(false, false));
|
||||||
pm.addPass(seq::createSeqLowerToSVPass());
|
pm.addPass(seq::createSeqLowerToSVPass());
|
||||||
|
|
Loading…
Reference in New Issue