Skip to content

Commit

Permalink
Support compiling ROHD to JavaScript (#445)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Dec 13, 2023
1 parent 7d29852 commit 5db4b7c
Show file tree
Hide file tree
Showing 24 changed files with 542 additions and 227 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/general.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ jobs:
- name: Setup Dart
uses: dart-lang/setup-dart@v1

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install project dependencies
run: tool/gh_actions/install_dependencies.sh

Expand Down
9 changes: 1 addition & 8 deletions lib/src/modules/gates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,7 @@ abstract class _TwoInputBitwiseGate extends Module with InlineSystemVerilog {

/// Executes the functional behavior of this gate.
void _execute() {
dynamic toPut;
try {
toPut = _op(_in0.value, _in1.value);
} on Exception {
// in case of things like divide by 0
toPut = LogicValue.x;
}
out.put(toPut);
out.put(_op(_in0.value, _in1.value));
}

@override
Expand Down
6 changes: 6 additions & 0 deletions lib/src/utilities/simcompare.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'dart:io';

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

/// Represents a single test case to check in a single clock cycle.
Expand Down Expand Up @@ -242,6 +243,11 @@ abstract class SimCompare {
bool maskKnownWarnings = true,
bool buildOnly = false,
}) {
if (kIsWeb) {
// if running in web mode, then we can't run icarus verilog
return true;
}

String signalDeclaration(String signalName) {
final signal = module.signals.firstWhere((e) => e.name == signalName);

Expand Down
39 changes: 39 additions & 0 deletions lib/src/utilities/web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (C) 2021-2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// web.dart
// Utilities for running ROHD safely on the web or in JavaScript.
//
// 2023 December 8
// Author: Max Korbel <[email protected]>

import 'dart:math';

import 'package:rohd/rohd.dart';

/// Borrowed from Flutter's implementation to determine whether Dart is
/// compiled to run on the web. This is relevant for ROHD because when the
/// code is compiled to JavaScript, it affects the ability for [LogicValue]
/// to store different sizes of data in different implementations.
///
/// See more details here:
/// https://api.flutter.dev/flutter/foundation/kIsWeb-constant.html
// ignore: do_not_use_environment
const bool kIsWeb = bool.fromEnvironment('dart.library.js_util');

/// The number of bits in an int.
// ignore: constant_identifier_names
const int INT_BITS = kIsWeb ? 32 : 64;

/// Calculates the `int` result of `1 << shamt` in a safe way considering
/// whether it is run in JavaScript or native Dart.
///
/// In JavaScript, the shift amount is `&`ed with `0x1f`, so `1 << 32 == 0`.
int oneSllBy(int shamt) {
if (kIsWeb) {
assert(shamt <= 52, 'Loss of precision in JavaScript beyond 53 bits.');
return pow(2, shamt) as int;
} else {
return 1 << shamt;
}
}
23 changes: 15 additions & 8 deletions lib/src/values/big_logic_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ extension BigLogicValueBigIntUtilities on BigInt {
///
/// Always interprets the number as unsigned, and thus never clamps to fit.
int toIntUnsigned(int width) {
if (width > LogicValue._INT_BITS) {
if (width > INT_BITS) {
throw Exception('Cannot convert Int when width $width'
' is greater than ${LogicValue._INT_BITS}');
} else if (width == LogicValue._INT_BITS) {
' is greater than $INT_BITS');
} else if (width == INT_BITS && INT_BITS > 32) {
assert(!kIsWeb, 'This code is not set up to work with JS.');

// When width is 64, `BigInt.toInt()` will clamp values assuming that
// it's a signed number. To avoid that, if the width is 64, then do the
// conversion in two 32-bit chunks and bitwise-or them together.
Expand All @@ -28,7 +30,9 @@ extension BigLogicValueBigIntUtilities on BigInt {
return (this & mask).toInt() |
(((this >> maskWidth) & mask).toInt() << maskWidth);
} else {
return toInt();
return (this & _BigLogicValue._maskOfWidth(width))
.toInt()
.toSigned(INT_BITS);
}
}
}
Expand All @@ -52,13 +56,13 @@ class _BigLogicValue extends LogicValue {
}

/// Constructs a new [_BigLogicValue], intended to hold values
/// with more than [_INT_BITS] bits.
/// with more than [INT_BITS] bits.
///
/// Set [allowInefficientRepresentation] to `true` to bypass
/// inefficient representation assertions.
_BigLogicValue(BigInt value, BigInt invalid, int width,
{bool allowInefficientRepresentation = false})
: assert(width > LogicValue._INT_BITS,
: assert(width > INT_BITS,
'_BigLogicValue should only be used for large values'),
super._(width) {
_value = (_mask & value).toUnsigned(width);
Expand Down Expand Up @@ -95,7 +99,7 @@ class _BigLogicValue extends LogicValue {
@override
LogicValue _getRange(int start, int end) {
final newWidth = end - start;
if (newWidth > LogicValue._INT_BITS) {
if (newWidth > INT_BITS) {
return LogicValue._bigLogicValueOrFilled(
(_value >> start) & _maskOfWidth(newWidth),
(_invalid >> start) & _maskOfWidth(newWidth),
Expand Down Expand Up @@ -130,7 +134,7 @@ class _BigLogicValue extends LogicValue {
int toInt() {
final bigInt = toBigInt();
if (bigInt.isValidInt) {
return bigInt.toIntUnsigned(LogicValue._INT_BITS);
return bigInt.toIntUnsigned(INT_BITS).toSigned(INT_BITS);
} else {
throw InvalidTruncationException(
'LogicValue $this is too long to convert to int.'
Expand Down Expand Up @@ -241,4 +245,7 @@ class _BigLogicValue extends LogicValue {

@override
int get _intValue => _value.toIntUnsigned(width);

@override
late final bool isZero = _value == BigInt.zero && _invalid == BigInt.zero;
}
5 changes: 4 additions & 1 deletion lib/src/values/filled_logic_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class _FilledLogicValue extends LogicValue {
return 0;
} else if (!isValid) {
throw InvalidValueOperationException(this, 'toInt');
} else if (width > LogicValue._INT_BITS) {
} else if (width > INT_BITS) {
throw InvalidTruncationException(
'LogicValue $this is too long to convert to int.'
' Use toBigInt() instead.');
Expand Down Expand Up @@ -340,4 +340,7 @@ class _FilledLogicValue extends LogicValue {
(_value == _LogicValueEnum.one || _value == _LogicValueEnum.z)
? _SmallLogicValue._maskOfWidth(width)
: 0;

@override
bool get isZero => _value == _LogicValueEnum.zero;
}
Loading

0 comments on commit 5db4b7c

Please sign in to comment.