Skip to content

Test fails in rti.dart due to failed null check when run on node, passes in VM #54329

@mkorbel1

Description

@mkorbel1

I'm working on upgrading the ROHD package to run properly (passing test suite) when compiled to JavaScript (dart test -p node) (intel/rohd#445)

There's one test in the suite failing when run on node with what appears to be a null check somewhere inside Dart's js libraries. It's not really doing something significantly different than many other tests in the suite, so I'm not sure how to reduce it to a smaller reproduction. The same test passes fine in the Dart VM.

I'm running on WSL2 Ubuntu 22, with Dart version Dart SDK version: 3.2.3 (stable) (None) on "linux_x64" and node version v20.10.0.

The failure signature is:

  Null check operator used on a null value
  org-dartlang-sdk:///lib/_internal/js_shared/lib/rti.dart 883:10  Object._instanceType
  package:rohd/src/modules/conditional.dart 782:5                  If._driveX
  package:rohd/src/modules/conditional.dart 1443:9                 If.execute
  package:rohd/src/modules/conditional.dart 1438:23                If.execute
  package:rohd/src/modules/conditional.dart 572:19                 Sequential._execute
  package:rohd/src/modules/conditional.dart 524:13                 Sequential._setup.<fn>.<fn>
  org-dartlang-sdk:///lib/async/zone.dart 1407:46                  StaticClosure._rootRunUnary
  org-dartlang-sdk:///lib/async/zone.dart 1307:34                  _CustomZone.runUnary
  org-dartlang-sdk:///lib/async/future_impl.dart 127:29            _Future._propagateToListeners.handleValueCallback
  org-dartlang-sdk:///lib/async/future_impl.dart 875:13            Object._Future._propagateToListeners

To reproduce, include this version of rohd in pubspec.yaml:

  rohd:
    git:
      url: https://github.com/mkorbel1/rohd.git
      ref: js

Then run the following test (dart test -p node translationjs.dart)

// Copyright (C) 2021-2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

// ignore_for_file: avoid_multiple_declarations_per_line

import 'package:rohd/rohd.dart';
import 'package:rohd/src/utilities/simcompare.dart';
import 'package:test/test.dart';

class FlopArrayPort {
  final Logic en, ptr, data;
  FlopArrayPort(this.en, this.ptr, this.data);
}

class FlopArray extends Module {
  // for convenience, make it easier to access inputs and outputs of this module
  Logic get lclk => input('lclk');
  Logic get lrst => input('lrst');
  Logic rdData(int idx) => _rdPorts[idx].data;

  final List<FlopArrayPort> _wrPorts = [], _rdPorts = [];

  final int numWrites, numReads, awidth, dwidth, numEntries;
  FlopArray(Logic lclk, Logic lrst, List<Logic> rdEn, List<Logic> rdPtr,
      List<Logic> wrEn, List<Logic> wrPtr, List<Logic> wrData,
      {this.numEntries = 8})
      : numWrites = wrEn.length,
        numReads = rdEn.length,
        dwidth = wrData[0].width,
        awidth = rdPtr[0].width,
        super(name: 'floparray_${wrEn.length}w_${rdEn.length}r') {
    // make sure widths of everything match expectations
    if (rdPtr.length != numReads) {
      throw Exception('Read pointer length must match number of read enables.');
    }
    if (wrPtr.length != numWrites) {
      throw Exception(
          'Write pointer length must match number of write enables');
    }
    if (wrData.length != numWrites) {
      throw Exception('Write data length must match number of write enables');
    }

    // register inputs and outputs with ROHD
    addInput('lclk', lclk);
    addInput('lrst', lrst);
    for (var i = 0; i < numReads; i++) {
      _rdPorts.add(FlopArrayPort(
          addInput('rdEn$i', rdEn[i]),
          addInput('rdPtr$i', rdPtr[i], width: awidth),
          addOutput('rdData$i', width: dwidth)));
    }
    for (var i = 0; i < numWrites; i++) {
      _wrPorts.add(FlopArrayPort(
          addInput('wrEn$i', wrEn[i]),
          addInput('wrPtr$i', wrPtr[i], width: awidth),
          addInput('wrData$i', wrData[i], width: dwidth)));
    }

    _buildLogic();
  }

  void _buildLogic() {
    // create local storage bank
    final storageBank = List<Logic>.generate(
        numEntries, (i) => Logic(name: 'storageBank_$i', width: dwidth));

    // Sequential(lclk, [  // normally this should be here
    Sequential(SimpleClockGenerator(10).clk, [
      //for testing purposes, easier to just plug a clock in here
      If(lrst, then: [
        ...storageBank
            .map((e) => e < 0) // zero out entire storage bank on reset
      ], orElse: [
        ...List.generate(
            numEntries,
            (entry) => [
                  ..._wrPorts.map((wrPort) =>
                      // set storage bank if write enable and pointer matches
                      If(wrPort.en & wrPort.ptr.eq(entry),
                          then: [storageBank[entry] < wrPort.data])),
                  ..._rdPorts.map((rdPort) =>
                      // read storage bank if read enable and pointer matches
                      If(rdPort.en & rdPort.ptr.eq(entry),
                          then: [rdPort.data < storageBank[entry]])),
                ]).expand((e) => e) // flatten
      ]),
    ]);
  }
}

void main() {
  tearDown(() async {
    await Simulator.reset();
  });

  group('simcompare', () {
    test('translation', () async {
      const numRdPorts = 2;
      const numWrPorts = 2;
      final ftm = FlopArray(
        Logic(),
        Logic(),
        List<Logic>.generate(numRdPorts, (index) => Logic()),
        List<Logic>.generate(numRdPorts, (index) => Logic(width: 6)),
        List<Logic>.generate(numWrPorts, (index) => Logic()),
        List<Logic>.generate(numWrPorts, (index) => Logic(width: 6)),
        List<Logic>.generate(numWrPorts, (index) => Logic(width: 16)),
      );
      await ftm.build();

      final vectors = [
        Vector({'lrst': 0}, {}),
        Vector({'lrst': 1}, {}),
        Vector({'lrst': 1, 'wrEn0': 0, 'rdEn0': 0, 'wrEn1': 0, 'rdEn1': 0}, {}),
        Vector({'lrst': 0}, {}),
        Vector({'wrEn1': 1, 'wrPtr1': 4, 'wrData1': 0xf}, {}),
        Vector({'wrEn1': 0, 'rdEn0': 1, 'rdPtr0': 4}, {}),
        // Vector({'wrEn1': 0, 'rdEn0': 0}, {'rdData0': 0xf}),
      ];
      await SimCompare.checkFunctionalVector(ftm, vectors);
    });
  });
}

Metadata

Metadata

Assignees

Labels

P1A high priority bug; for example, a single project is unusable or has many test failuresarea-web-jsIssues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop.type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)web-dart2js

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions