[FIRRTL] Create memory metadata (#1923)

Add memory metadata emission to `CreatesifiveMetadata`.
The metadata is used for memory macro substitution by memory generator tools.
A subsequent checkin will delay the `CreatesifiveMetadata` pass in 
the FIRRTL pipeline.
This commit is contained in:
Prithayan Barua 2021-10-08 13:08:33 -04:00 committed by GitHub
parent 55182f1797
commit b3e68a9370
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 576 additions and 17 deletions

View File

@ -471,6 +471,32 @@ Example:
}
```
### SeqMemInstanceMetadataAnnotation
| Property | Type | Description |
| ---------- | ------ | ------------- |
| class | string | `sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation` |
| target | string | Reference target |
This annotation attaches metadata to the firrtl.mem operation. The `data` is
emitted onto the `seq_mems.json` and `tb_seq_mems.json` file. It is required
for verification only and used by memory generator tools for simulation.
Example:
```json
{
"class":"sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation",
"data":{
"baseAddress":2147483648,
"eccScheme":"none",
"eccBits":0,
"dataBits":8,
"eccIndices":[ ]
},
"target":"~CoreIPSubsystemVerifTestHarness|TLRAM>mem"
}
```
### ScalaClassAnnotation
| Property | Type | Description |

View File

@ -225,6 +225,9 @@ def MemOp : FIRRTLOp<"mem"> {
/// Hooks for port annotations.
ArrayAttr getPortAnnotation(unsigned portIdx);
void setAllPortAnnotations(ArrayRef<Attribute> annotations);
/// Get the number of read ports, write ports and read write ports.
void getNumPorts(size_t &numReadPorts, size_t &numWritePorts, size_t &numReadWritePorts);
}];
}

View File

@ -1023,6 +1023,23 @@ void MemOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
ArrayAttr::get(getContext(), annotations));
}
// Get the number of read, write and read-write ports.
void MemOp::getNumPorts(size_t &numReadPorts, size_t &numWritePorts,
size_t &numReadWritePorts) {
numReadPorts = 0;
numWritePorts = 0;
numReadWritePorts = 0;
for (size_t i = 0, e = getNumResults(); i != e; ++i) {
auto portKind = getPortKind(i);
if (portKind == MemOp::PortKind::Read)
++numReadPorts;
else if (portKind == MemOp::PortKind::Write) {
++numWritePorts;
} else
++numReadWritePorts;
}
}
/// Verify the correctness of a MemOp.
static LogicalResult verifyMemOp(MemOp mem) {

View File

@ -25,16 +25,213 @@
using namespace circt;
using namespace firrtl;
static const char seqMemAnnoClass[] =
"sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation";
static const char dutAnnoClass[] = "sifive.enterprise.firrtl.MarkDUTAnnotation";
/// Attribute that indicates where some json files should be dumped.
static const char metadataDirectoryAnnoClass[] =
"sifive.enterprise.firrtl.MetadataDirAnnotation";
namespace {
class CreateSiFiveMetadataPass
: public CreateSiFiveMetadataBase<CreateSiFiveMetadataPass> {
LogicalResult emitRetimeModulesMetadata();
LogicalResult emitSitestBlackboxMetadata();
LogicalResult emitMemoryMetadata();
void getDependentDialects(mlir::DialectRegistry &registry) const override;
void runOnOperation() override;
// The set of all modules underneath the design under test module.
DenseSet<Operation *> dutModuleSet;
// The design under test module.
FModuleOp dutMod;
};
} // end anonymous namespace
/// This function collects all the firrtl.mem ops and creates a verbatim op with
/// the relevant memory attributes.
LogicalResult CreateSiFiveMetadataPass::emitMemoryMetadata() {
// Lambda to get the number of read, write and read-write ports corresponding
// to a MemOp.
CircuitOp circuitOp = getOperation();
// The instance graph analysis will be required to print the hierarchy names
// of the memory.
auto instancePathCache = InstancePathCache(getAnalysis<InstanceGraph>());
auto printAttr = [&](llvm::json::OStream &jsonStream, Attribute &verifValAttr,
std::string &id) {
if (auto intV = verifValAttr.dyn_cast<IntegerAttr>())
jsonStream.attribute(id, (int64_t)intV.getValue().getZExtValue());
else if (auto strV = verifValAttr.dyn_cast<StringAttr>())
jsonStream.attribute(id, strV.getValue().str());
else if (auto arrV = verifValAttr.dyn_cast<ArrayAttr>()) {
std::string indices;
jsonStream.attributeArray(id, [&] {
for (auto arrI : llvm::enumerate(arrV)) {
auto i = arrI.value();
if (auto intV = i.dyn_cast<IntegerAttr>())
jsonStream.value(std::to_string(intV.getValue().getZExtValue()));
else if (auto strV = i.dyn_cast<StringAttr>())
jsonStream.value(strV.getValue().str());
}
});
}
};
// This lambda, writes to the given Json stream all the relevant memory
// attributes. Also adds the memory attrbutes to the string for creating the
// memmory conf file.
auto createMemMetadata = [&](MemOp memOp, llvm::json::OStream &jsonStream,
std::string &seqMemConfStr) {
size_t numReadPorts = 0, numWritePorts = 0, numReadWritePorts = 0;
// Get the number of read,write ports.
memOp.getNumPorts(numReadPorts, numWritePorts, numReadWritePorts);
// Get the memory data width.
auto width = memOp.getDataType().getBitWidthOrSentinel();
// Metadata needs to be printed for memories which are candidates for macro
// replacement. The requirements for macro replacement::
// 1. read latency and write latency of one.
// 2. only one readwrite port or write port.
// 3. zero or one read port.
// 4. undefined read-under-write behavior.
if (!((memOp.readLatency() == 1 && memOp.writeLatency() == 1) &&
(numWritePorts + numReadWritePorts == 1) && (numReadPorts <= 1) &&
width > 0))
return;
// Get the absolute path for the parent memory, to create the hierarchy
// names.
auto paths =
instancePathCache.getAbsolutePaths(memOp->getParentOfType<FModuleOp>());
AnnotationSet anno = AnnotationSet(memOp);
DictionaryAttr verifData = {};
// Get the verification data attached with the memory op, if any.
if (auto v = anno.getAnnotation(seqMemAnnoClass)) {
verifData = v.get("data").cast<DictionaryAttr>();
AnnotationSet::removeAnnotations(memOp, seqMemAnnoClass);
}
// Compute the mask granularity.
auto maskGran = width / memOp.getMaskBits();
// Now create the config string for the memory.
std::string portStr;
if (numWritePorts)
portStr += "mwrite";
if (numReadPorts) {
if (!portStr.empty())
portStr += ",";
portStr += "read";
}
if (numReadWritePorts)
portStr = "mrw";
seqMemConfStr += "name " + memOp.name().str() + " depth " +
std::to_string(memOp.depth()) + " width " +
std::to_string(width) + " ports " + portStr +
" mask_gran " + std::to_string(maskGran) + "\n";
// This adds a Json array element entry corresponding to this memory.
jsonStream.object([&] {
jsonStream.attribute("module_name", memOp.name());
jsonStream.attribute("depth", (int64_t)memOp.depth());
jsonStream.attribute("width", (int64_t)width);
jsonStream.attribute("masked", "true");
jsonStream.attribute("read", numReadPorts ? "true" : "false");
jsonStream.attribute("write", numWritePorts ? "true" : "false");
jsonStream.attribute("readwrite", numReadWritePorts ? "true" : "false");
jsonStream.attribute("mask_granularity", (int64_t)maskGran);
jsonStream.attributeArray("extra_ports", [&] {});
// Record all the hierarchy names.
SmallVector<std::string> hierNames;
jsonStream.attributeArray("hierarchy", [&] {
for (auto p : paths) {
const InstanceOp &x = p.front();
std::string hierName =
x->getParentOfType<FModuleOp>().getName().str();
for (InstanceOp inst : p) {
hierName = hierName + "." + inst.name().str();
}
hierNames.push_back(hierName);
jsonStream.value(hierName);
}
});
// If verification annotation added to the memory op then print the data.
if (verifData)
jsonStream.attributeObject("verification_only_data", [&] {
for (auto name : hierNames) {
jsonStream.attributeObject(name, [&] {
for (auto data : verifData) {
// Id for the memory verification property.
std::string id = data.first.strref().str();
// Value for the property.
auto verifValAttr = data.second;
// Now print the value attribute based on its type.
printAttr(jsonStream, verifValAttr, id);
}
});
}
});
});
};
std::string testBenchJsonBuffer;
llvm::raw_string_ostream testBenchOs(testBenchJsonBuffer);
llvm::json::OStream testBenchJson(testBenchOs);
std::string dutJsonBuffer;
llvm::raw_string_ostream dutOs(dutJsonBuffer);
llvm::json::OStream dutJson(dutOs);
SmallVector<MemOp> dutMems;
SmallVector<MemOp> tbMems;
for (auto mod : circuitOp.getOps<FModuleOp>()) {
bool isDut = dutModuleSet.contains(mod);
for (auto memOp : mod.getBody()->getOps<MemOp>())
if (isDut)
dutMems.push_back(memOp);
else
tbMems.push_back(memOp);
}
std::string seqMemConfStr, tbConfStr;
dutJson.array([&] {
for (auto memOp : dutMems)
createMemMetadata(memOp, dutJson, seqMemConfStr);
});
testBenchJson.array([&] {
// The tbConfStr is populated here, but unused, it will not be printed to
// file.
for (auto memOp : tbMems)
createMemMetadata(memOp, testBenchJson, tbConfStr);
});
auto *context = &getContext();
auto builder = OpBuilder::atBlockEnd(circuitOp.getBody());
AnnotationSet annos(circuitOp);
auto dirAnno = annos.getAnnotation(metadataDirectoryAnnoClass);
StringRef metadataDir = "metadata";
if (dirAnno)
if (auto dir = dirAnno.getAs<StringAttr>("dirname"))
metadataDir = dir.getValue();
// Use unknown loc to avoid printing the location in the metadata files.
auto tbVerbatimOp = builder.create<sv::VerbatimOp>(builder.getUnknownLoc(),
testBenchJsonBuffer);
auto dutVerbatimOp =
builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), dutJsonBuffer);
auto confVerbatimOp =
builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), seqMemConfStr);
auto fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
context, metadataDir, "seq_mems.json", /*excludeFromFilelist=*/true);
dutVerbatimOp->setAttr("output_file", fileAttr);
fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
context, metadataDir, "tb_seq_mems.json", /*excludeFromFilelist=*/true);
tbVerbatimOp->setAttr("output_file", fileAttr);
StringRef confFile = "memory";
if (dutMod)
confFile = dutMod.getName();
fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
context, metadataDir, confFile + ".conf", /*excludeFromFilelist=*/true);
confVerbatimOp->setAttr("output_file", fileAttr);
return success();
}
/// This will search for a target annotation and remove it from the operation.
/// If the annotation has a filename, it will be returned in the output
/// argument. If the annotation is missing the filename member, or if more than
@ -142,7 +339,6 @@ LogicalResult CreateSiFiveMetadataPass::emitSitestBlackboxMetadata() {
"sifive.enterprise.firrtl.SitestBlackBoxAnnotation";
auto *testBlackboxAnnoClass =
"sifive.enterprise.firrtl.SitestTestHarnessBlackBoxAnnotation";
auto *dutAnnoClass = "sifive.enterprise.firrtl.MarkDUTAnnotation";
// Any extmodule with these annotations or one of these ScalaClass classes
// should be excluded from the blackbox list.
@ -172,21 +368,6 @@ LogicalResult CreateSiFiveMetadataPass::emitSitestBlackboxMetadata() {
if (dutFilename.empty() && testFilename.empty())
return success();
auto *body = circuitOp.getBody();
// Find the device under test and create a set of all modules underneath it.
DenseSet<Operation *> dutModuleSet;
auto it = llvm::find_if(*body, [&](Operation &op) -> bool {
return AnnotationSet(&op).hasAnnotation(dutAnnoClass);
});
if (it != body->end()) {
auto instanceGraph = getAnalysis<InstanceGraph>();
auto *node = instanceGraph.lookup(&(*it));
llvm::for_each(llvm::depth_first(node), [&](InstanceGraphNode *node) {
dutModuleSet.insert(node->getModule());
});
}
// Find all extmodules in the circuit. Check if they are black-listed from
// being included in the list. If they are not, separate them into two groups
// depending on if theyre in the DUT or the test harness.
@ -242,6 +423,7 @@ LogicalResult CreateSiFiveMetadataPass::emitSitestBlackboxMetadata() {
j.value(name);
});
auto *body = circuitOp.getBody();
// Put the information in a verbatim operation.
auto builder = OpBuilder::atBlockEnd(body);
auto verbatimOp =
@ -263,8 +445,23 @@ void CreateSiFiveMetadataPass::getDependentDialects(
}
void CreateSiFiveMetadataPass::runOnOperation() {
auto circuitOp = getOperation();
auto *body = circuitOp.getBody();
// Find the device under test and create a set of all modules underneath it.
auto it = llvm::find_if(*body, [&](Operation &op) -> bool {
return AnnotationSet(&op).hasAnnotation(dutAnnoClass);
});
if (it != body->end()) {
dutMod = dyn_cast<FModuleOp>(*it);
auto instanceGraph = getAnalysis<InstanceGraph>();
auto *node = instanceGraph.lookup(&(*it));
llvm::for_each(llvm::depth_first(node), [&](InstanceGraphNode *node) {
dutModuleSet.insert(node->getModule());
});
}
if (failed(emitRetimeModulesMetadata()) ||
failed(emitSitestBlackboxMetadata()))
failed(emitSitestBlackboxMetadata()) || failed(emitMemoryMetadata()))
return signalPassFailure();
// This pass does not modify the hierarchy.

View File

@ -102,3 +102,26 @@ firrtl.circuit "BasicBlackboxes" attributes { annotations = [{
firrtl.extmodule @DUTBlackbox_2() attributes {defname = "DUTBlackbox1"}
// CHECK: sv.verbatim "[\22DUTBlackbox1\22,\22DUTBlackbox2\22]" {output_file = #hw.output_file<"dut_blackboxes.json", excludeFromFileList>, symbols = []}
}
// CHECK-LABEL: firrtl.circuit "top"
firrtl.circuit "top"
{
firrtl.module @top() {
firrtl.instance @dut {name = "dut"}
firrtl.instance @Mem1 {name = "mem1"}
}
firrtl.module @Mem1() {
%head_MPORT_2 = firrtl.mem Undefined {depth = 20 : i64, name = "head", portNames = ["MPORT_2", "MPORT_6"], readLatency = 1 : i32, writeLatency = 1 : i32} : !firrtl.bundle<addr: uint<5>, en: uint<1>, clk: clock, data: uint<5>, mask: uint<1>>
}
firrtl.module @dut()attributes {annotations = [
{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
firrtl.instance @Mem {name = "mem1"}
}
firrtl.module @Mem() {
%memory_rw, %memory_rw_r = firrtl.mem Undefined {annotations = [{class = "sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation", data = {baseAddress = 2147483648 : i64, dataBits = 8 : i64, eccBits = 0 : i64, eccIndices = [], eccScheme = "none"}}], depth = 16 : i64, name = "memory", portNames = ["rw", "rw_r", "rw_w"], readLatency = 1 : i32, writeLatency = 1 : i32} : !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, rdata flip: uint<8>, wmode: uint<1>, wdata: uint<8>, wmask: uint<1>>, !firrtl.bundle<addr: uint<4>, en: uint<1>, clk: clock, data flip: uint<8>>
%head_MPORT_2, %head_MPORT_6 = firrtl.mem Undefined {depth = 20 : i64, name = "dumm", portNames = ["MPORT_2", "MPORT_6"], readLatency = 0 : i32, writeLatency = 1 : i32} : !firrtl.bundle<addr: uint<5>, en: uint<1>, clk: clock, data: uint<5>, mask: uint<1>>, !firrtl.bundle<addr: uint<5>, en: uint<1>, clk: clock, data: uint<5>, mask: uint<1>>
}
// CHECK: sv.verbatim "[{\22module_name\22:\22head\22,\22depth\22:20,\22width\22:5,\22masked\22:\22true\22,\22read\22:\22false\22,\22write\22:\22true\22,\22readwrite\22:\22false\22,\22mask_granularity\22:5,\22extra_ports\22:[],\22hierarchy\22:[\22top.mem1\22]}]" {output_file = #hw.output_file<"metadata/tb_seq_mems.json", excludeFromFileList>, symbols = []}
// CHECK: sv.verbatim "[{\22module_name\22:\22memory\22,\22depth\22:16,\22width\22:8,\22masked\22:\22true\22,\22read\22:\22true\22,\22write\22:\22false\22,\22readwrite\22:\22true\22,\22mask_granularity\22:8,\22extra_ports\22:[],\22hierarchy\22:[\22top.dut.mem1\22],\22verification_only_data\22:{\22top.dut.mem1\22:{\22baseAddress\22:2147483648,\22dataBits\22:8,\22eccBits\22:0,\22eccIndices\22:[],\22eccScheme\22:\22none\22}}}]" {output_file = #hw.output_file<"metadata/seq_mems.json", excludeFromFileList>, symbols = []}
// CHECK: sv.verbatim "name memory depth 16 width 8 ports mrw mask_gran 8\0A" {output_file = #hw.output_file<"metadata/dut.conf", excludeFromFileList>, symbols = []}
}

View File

@ -0,0 +1,130 @@
; RUN: firtool %s --format=fir --annotation-file %s.anno.json -emit-metadata --verilog | FileCheck %s
circuit test:
module memoryTest1:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
mem memory:
data-type => UInt<8>
depth => 16
reader => r
writer => w
read-latency => 1
write-latency => 1
read-under-write => undefined
; All of these are unified together
memory.r.clk <= clock
memory.r.en <= rEn
memory.r.addr <= rAddr
rData <= memory.r.data
memory.w.clk <= clock
memory.w.en <= rEn
memory.w.addr <= rAddr
; These two are split
memory.w.mask <= wMask
memory.w.data <= wData
module memoryTest2:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
mem memory:
data-type => UInt<8>
depth => 16
reader => r
writer => w
read-latency => 1
write-latency => 1
read-under-write => undefined
; All of these are unified together
memory.r.clk <= clock
memory.r.en <= rEn
memory.r.addr <= rAddr
rData <= memory.r.data
memory.w.clk <= clock
memory.w.en <= rEn
memory.w.addr <= rAddr
; These two are split
memory.w.mask <= wMask
memory.w.data <= wData
module test:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
inst m of memoryTest1
m.clock <= clock
m.rAddr <= rAddr
m.rEn <= rEn
rData <= m.rData
m.wMask <= wMask
m.wData <= wData
inst m2 of memoryP
m2.clock <= clock
m2.rAddr <= rAddr
m2.rEn <= rEn
rData <= m2.rData
m2.wMask <= wMask
m2.wData <= wData
module memoryP:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
inst m of memoryTest2
m.clock <= clock
m.rAddr <= rAddr
m.rEn <= rEn
rData <= m.rData
m.wMask <= wMask
m.wData <= wData
; CHECK-LABEL: module test
; CHECK: memoryP [[m2:.+]] (
; CHECK-LABEL: module memoryP
; CHECK: memoryTest2 [[m:.+]] (
; CHECK: FILE "metadata/tb_seq_mems.json"
; CHECK: [{"module_name":"memory","depth":16,"width":8,"masked":"true",
; CHECK-SAME: "read":"true","write":"true","readwrite":"false",
; CHECK_SAME: "mask_granularity":8,"extra_ports":[],
; CHECK-SAME: "hierarchy":["test.[[m]]"]}]
; CHECK: FILE "metadata/seq_mems.json"
; CHECK: [{"module_name":"memory","depth":16,"width":8,"masked":"true",
; CHECK-SAME: "read":"true","write":"true","readwrite":"false",
; CHECK_SAME: "mask_granularity":8,"extra_ports":[],
; CHECK-SAME: "hierarchy":["test.[[m2]].[[m]]"],
; CHECK-SAME: "verification_only_data":{
; CHECK-SAME: "test.[[m2]].[[m]]":{"baseAddress":1073741824,
; CHECK-SAME: "dataBits":8,"eccBits":0,"eccIndices":[],"eccScheme":"none"}}}]
; CHECK: FILE "metadata/memoryP.conf"
; CHECK: name memory depth 16 width 8 ports mwrite,read mask_gran 8

View File

@ -0,0 +1,17 @@
[
{
"class":"sifive.enterprise.firrtl.MarkDUTAnnotation",
"target": "test.memoryP"
},
{
"class":"sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation",
"data":{
"baseAddress":1073741824,
"eccScheme":"none",
"eccBits":0,
"dataBits":8,
"eccIndices":[]
},
"target":"~test|memoryTest2>memory"
}
]

View File

@ -0,0 +1,129 @@
; RUN: firtool %s --format=fir --annotation-file %s.anno.json -emit-metadata --verilog | FileCheck %s
; XFAIL: true
circuit test:
module memoryTest1:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
mem memory:
data-type => UInt<8>
depth => 16
reader => r
writer => w
read-latency => 1
write-latency => 1
read-under-write => undefined
; All of these are unified together
memory.r.clk <= clock
memory.r.en <= rEn
memory.r.addr <= rAddr
rData <= memory.r.data
memory.w.clk <= clock
memory.w.en <= rEn
memory.w.addr <= rAddr
; These two are split
memory.w.mask <= wMask
memory.w.data <= wData
module memoryTest2:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
mem memory:
data-type => UInt<8>
depth => 16
reader => r
writer => w
read-latency => 1
write-latency => 1
read-under-write => undefined
; All of these are unified together
memory.r.clk <= clock
memory.r.en <= rEn
memory.r.addr <= rAddr
rData <= memory.r.data
memory.w.clk <= clock
memory.w.en <= rEn
memory.w.addr <= rAddr
; These two are split
memory.w.mask <= wMask
memory.w.data <= wData
module test:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
inst m of memoryTest1
m.clock <= clock
m.rAddr <= rAddr
m.rEn <= rEn
rData <= m.rData
m.wMask <= wMask
m.wData <= wData
inst signed of memoryP
signed.clock <= clock
signed.rAddr <= rAddr
signed.rEn <= rEn
rData <= signed.rData
signed.wMask <= wMask
signed.wData <= wData
module memoryP:
input clock: Clock
input rAddr: UInt<4>
input rEn: UInt<1>
output rData: UInt<8>
input wMask: UInt<1>
input wData: UInt<8>
inst m of memoryTest2
m.clock <= clock
m.rAddr <= rAddr
m.rEn <= rEn
rData <= m.rData
m.wMask <= wMask
m.wData <= wData
; CHECK-LABEL: module test
; CHECK: memoryP [[signed:.+]] (
; CHECK-LABEL: module memoryP
; CHECK: memoryTest2 [[m:.+]] (
; CHECK: FILE "metadata/tb_seq_mems.json"
; CHECK: [{"module_name":"memory","depth":16,"width":8,"masked":"true",
; CHECK-SAME: "read":"true","write":"true","readwrite":"false",
; CHECK_SAME: "mask_granularity":8,"extra_ports":[],
; CHECK-SAME: "hierarchy":["test.[[m]]"]}]
; CHECK: FILE "metadata/seq_mems.json"
; CHECK: [{"module_name":"memory","depth":16,"width":8,"masked":"true",
; CHECK-SAME: "read":"true","write":"true","readwrite":"false",
; CHECK_SAME: "mask_granularity":8,"extra_ports":[],
; CHECK-SAME: "hierarchy":["test.[[signed]].[[m]]"],
; CHECK-SAME: "verification_only_data":{
; CHECK-SAME: "test.[[signed]].[[m]]":{"baseAddress":1073741824,
; CHECK-SAME: "dataBits":8,"eccBits":0,"eccIndices":[],"eccScheme":"none"}}}]

View File

@ -0,0 +1,17 @@
[
{
"class":"sifive.enterprise.firrtl.MarkDUTAnnotation",
"target": "test.memoryP"
},
{
"class":"sifive.enterprise.firrtl.SeqMemInstanceMetadataAnnotation",
"data":{
"baseAddress":1073741824,
"eccScheme":"none",
"eccBits":0,
"dataBits":8,
"eccIndices":[]
},
"target":"~test|memoryTest2>memory"
}
]