loadMemString method

void loadMemString(
  1. String memContents, {
  2. int radix = 16,
  3. int bitsPerAddress = 8,
})

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();
}