loadMemString method
Reads a verilog-compliant mem file and preloads memory with it.
The loaded address will increment each dataWidth bits of data between
address @
annotations. Data is parsed according to the provided
radix
, which must be a positive power of 2 up to 16. The address for
data will increment by 1 for each bitsPerAddress
bits of data, which
must be a power of 2 less than dataWidth.
Line comments (//
) are supported and any whitespace is supported as a
separator between data. Block comments (/* */
) are not supported.
Example input format:
@80000000
B3 02 00 00 33 05 00 00 B3 05 00 00 13 05 F5 1F
6F 00 40 00 93 02 10 00 17 03 00 00 13 03 83 02
23 20 53 00 6F 00 00 00
@80000040
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Implementation
void loadMemString(String memContents,
{int radix = 16, int bitsPerAddress = 8}) {
if (radix <= 0 || (radix & (radix - 1) != 0) || radix > 16) {
throw RohdHclException('Radix must be a positive power of 2 (max 16).');
}
if (dataWidth % bitsPerAddress != 0) {
throw RohdHclException('dataWidth must be a multiple of bitsPerAddress.');
}
final bitsPerChar = log2Ceil(radix);
final charsPerLine = dataWidth ~/ bitsPerChar;
final addrIncrPerLine = dataWidth ~/ bitsPerAddress;
var address = 0;
final chunks = <String>[];
var chunksLength = 0;
void addChunk([String? chunk]) {
if (chunk != null) {
// ignore: parameter_assignments
chunk = chunk.trim();
chunks.add(chunk);
chunksLength += chunk.length;
}
while (chunksLength >= charsPerLine) {
final pendingData = chunks.reversed.join();
final cutPoint = chunksLength - charsPerLine;
final thisData = pendingData.substring(cutPoint);
chunks.clear();
final remaining = pendingData.substring(0, cutPoint);
chunksLength = 0;
if (remaining.isNotEmpty) {
chunks.add(remaining);
chunksLength = remaining.length;
}
final lvData = LogicValue.ofBigInt(
BigInt.parse(thisData, radix: radix), dataWidth);
final addr =
LogicValue.ofInt(address - (address % addrIncrPerLine), addrWidth);
setData(addr, lvData);
address += addrIncrPerLine;
}
}
void padToAlignment() {
addChunk();
while (chunksLength != 0) {
addChunk('0');
}
}
for (var line in memContents.split('\n')) {
// if there's a `//` comment on this line, ditch everything after it
final commentIdx = line.indexOf('//');
if (commentIdx != -1) {
line = line.substring(0, commentIdx);
}
line = line.trim();
if (line.isEmpty) {
continue;
}
if (line.startsWith('@')) {
// pad out remaining bytes as 0
padToAlignment();
// if it doesn't match the format, throw
if (!RegExp('@([0-9a-fA-F]+)').hasMatch(line)) {
throw RohdHclException('Invalid address format: $line');
}
// check to see if this block already exists in memory
final lineAddr = int.parse(line.substring(1), radix: 16);
final lineAddrLv =
LogicValue.ofInt(lineAddr - lineAddr % addrIncrPerLine, addrWidth);
if (getData(lineAddrLv) != null) {
// must reconstruct the bytes array ending at the provided address
final endOff = lineAddr % addrIncrPerLine;
final origData = getData(lineAddrLv);
chunks.clear();
if (endOff != 0) {
chunks.add(origData!
.getRange(0, endOff * bitsPerAddress)
.toInt()
.toRadixString(radix));
}
}
address = lineAddrLv.toInt();
} else {
line.split(RegExp(r'\s')).forEach(addChunk);
}
}
// pad out remaining bytes as 0
padToAlignment();
}