[FIRRTL] Add Memory Annotation Parsing

Add parsing of the two memory initialization annotations which implement
the Chisel loadMemoryFromFile and loadMemoryFromFileInline APIs:

  - firrtl.annotations.LoadMemoryAnnotation
  - firrtl.annotations.MemoryFileInlineAnnotation

These are parsed into the optional MemoryInitAttr on CHIRRTL or FIRRTL
memory operations.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
This commit is contained in:
Schuyler Eldridge 2023-02-02 22:02:36 -05:00
parent 8a2d2bb83d
commit b387c034f0
No known key found for this signature in database
GPG Key ID: 50C5E9936AAD536D
3 changed files with 142 additions and 0 deletions

View File

@ -197,6 +197,12 @@ constexpr const char *addSeqMemPortAnnoClass =
constexpr const char *addSeqMemPortsFileAnnoClass =
"sifive.enterprise.firrtl.AddSeqMemPortsFileAnnotation";
// Memory file loading annotations.
constexpr const char *loadMemoryFromFileAnnoClass =
"firrtl.annotations.LoadMemoryAnnotation";
constexpr const char *loadMemoryFromFileInlineAnnoClass =
"firrtl.annotations.MemoryFileInlineAnnotation";
} // namespace firrtl
} // namespace circt

View File

@ -277,6 +277,62 @@ static LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno,
return success();
}
//===----------------------------------------------------------------------===//
// Customized Appliers
//===----------------------------------------------------------------------===//
/// Update a memory op with attributes about memory file loading.
template <bool isInline>
static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target,
DictionaryAttr anno,
ApplyState &state) {
if (!target.isLocal()) {
mlir::emitError(state.circuit.getLoc())
<< "has a " << anno.get("class")
<< " annotation which is non-local, but this annotation is not allowed "
"to be non-local";
return failure();
}
auto *op = target.ref.getOp();
if (!target.isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
mlir::emitError(op->getLoc())
<< "can only apply a load memory annotation to a memory";
return failure();
}
// The two annotations have different case usage in "filename".
StringAttr filename = tryGetAs<StringAttr>(
anno, anno, isInline ? "filename" : "fileName", op->getLoc(),
anno.getAs<StringAttr>("class").getValue());
if (!filename)
return failure();
auto hexOrBinary =
tryGetAs<StringAttr>(anno, anno, "hexOrBinary", op->getLoc(),
anno.getAs<StringAttr>("class").getValue());
if (!hexOrBinary)
return failure();
auto hexOrBinaryValue = hexOrBinary.getValue();
if (hexOrBinaryValue != "h" && hexOrBinaryValue != "b") {
auto diag = mlir::emitError(op->getLoc())
<< "has memory initialization annotation with invalid format, "
"'hexOrBinary' field must be either 'h' or 'b'";
diag.attachNote() << "the full annotation is: " << anno;
return failure();
}
op->setAttr("init",
MemoryInitAttr::get(
op->getContext(), filename,
BoolAttr::get(op->getContext(), hexOrBinaryValue == "b"),
BoolAttr::get(op->getContext(), isInline)));
return success();
}
//===----------------------------------------------------------------------===//
// Driving table
//===----------------------------------------------------------------------===//
@ -400,6 +456,9 @@ static const llvm::StringMap<AnnoRecord> annotationRecords{{
{blackBoxTargetDirAnnoClass, NoTargetAnnotation},
{traceNameAnnoClass, {stdResolve, applyTraceName}},
{traceAnnoClass, {stdResolve, applyWithoutTarget<true>}},
{loadMemoryFromFileAnnoClass, {stdResolve, applyLoadMemoryAnno<false>}},
{loadMemoryFromFileInlineAnnoClass,
{stdResolve, applyLoadMemoryAnno<true>}},
}};

View File

@ -1749,3 +1749,80 @@ firrtl.circuit "Top" attributes {rawAnnotations = [{
// CHECK: firrtl.ref.resolve %[[localparam__gen_ref]] : !firrtl.ref<uint<1>>
}
}
// -----
// Test memory initialization setting.
// CHECK-LABEL: firrtl.circuit "MemoryInitializationAnnotations"
firrtl.circuit "MemoryInitializationAnnotations" attributes {
rawAnnotations = [
{
class = "firrtl.annotations.LoadMemoryAnnotation",
fileName = "mem1.txt",
hexOrBinary = "b",
originalMemoryNameOpt = "m",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m1"
},
{
class = "firrtl.annotations.MemoryFileInlineAnnotation",
filename = "mem2.txt",
hexOrBinary = "h",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m2"
},
{
class = "firrtl.annotations.LoadMemoryAnnotation",
fileName = "mem3.txt",
hexOrBinary = "b",
originalMemoryNameOpt = "m",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m3"
},
{
class = "firrtl.annotations.MemoryFileInlineAnnotation",
filename = "mem4.txt",
hexOrBinary = "h",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m4"
},
{
class = "firrtl.annotations.LoadMemoryAnnotation",
fileName = "mem5.txt",
hexOrBinary = "b",
originalMemoryNameOpt = "m",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m5"
},
{
class = "firrtl.annotations.MemoryFileInlineAnnotation",
filename = "mem6.txt",
hexOrBinary = "h",
target = "~MemoryInitializationAnnotations|MemoryInitializationAnnotations>m6"
}
]
} {
firrtl.module @MemoryInitializationAnnotations() {
// CHECK: %m1_r = firrtl.mem
// CHECK-SAME: #firrtl.meminit<"mem1.txt", true, false>
%m1_r = firrtl.mem Undefined {
depth = 2 : i64,
name = "m1",
portNames = ["r"],
readLatency = 1 : i32,
writeLatency = 1 : i32
} : !firrtl.bundle<addr: uint<1>, en: uint<1>, clk: clock, data flip: uint<8>>
// CHECK-NEXT: %m2_r = firrtl.mem
// CHECK-SAME: #firrtl.meminit<"mem2.txt", false, true>
%m2_r = firrtl.mem Undefined {
depth = 2 : i64,
name = "m2",
portNames = ["r"],
readLatency = 1 : i32,
writeLatency = 1 : i32
} : !firrtl.bundle<addr: uint<1>, en: uint<1>, clk: clock, data flip: uint<8>>
// CHECK-NEXT: %m3 = chirrtl.seqmem Undefined {init = #firrtl.meminit<"mem3.txt", true, false>}
%m3 = chirrtl.seqmem Undefined : !chirrtl.cmemory<uint<8>, 32>
// CHECK-NEXT: %m4 = chirrtl.seqmem Undefined {init = #firrtl.meminit<"mem4.txt", false, true>}
%m4 = chirrtl.seqmem Undefined : !chirrtl.cmemory<uint<8>, 32>
// CHECK-NEXT: %m5 = chirrtl.combmem {init = #firrtl.meminit<"mem5.txt", true, false>}
%m5 = chirrtl.combmem : !chirrtl.cmemory<uint<8>, 32>
// CHECK-NEXT: %m6 = chirrtl.combmem {init = #firrtl.meminit<"mem6.txt", false, true>}
%m6 = chirrtl.combmem : !chirrtl.cmemory<uint<8>, 32>
}
}