Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AXI4 Interface and Functional Modeling #159

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
643 changes: 643 additions & 0 deletions lib/src/interfaces/axi4.dart

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/src/interfaces/interfaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: BSD-3-Clause
kimmeljo marked this conversation as resolved.
Show resolved Hide resolved

export 'apb.dart';
export 'axi4.dart';
10 changes: 10 additions & 0 deletions lib/src/models/axi4_bfm/axi4_bfm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'axi4_compliance_checker.dart';
export 'axi4_main.dart';
export 'axi4_main_driver.dart';
export 'axi4_monitor.dart';
export 'axi4_packet.dart';
export 'axi4_subordinate.dart';
export 'axi4_tracker.dart';
128 changes: 128 additions & 0 deletions lib/src/models/axi4_bfm/axi4_compliance_checker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// axi4_compliance_checker.dart
// Compliance checking for AXI4.
//
// 2025 January
// Author: Josh Kimmel <joshua1.kimmel@intel.com>

import 'dart:async';

import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_vf/rohd_vf.dart';

/// A checker for some of the rules defined in the AXI4 interface specification.
///
/// This does not necessarily cover all rules defined in the spec.
class Axi4ComplianceChecker extends Component {
/// AXI4 System Interface.
final Axi4SystemInterface sIntf;

/// AXI4 Read Interface.
final Axi4ReadInterface rIntf;

/// AXI4 Write Interface.
final Axi4WriteInterface wIntf;

/// Creates a new compliance checker for AXI4.
Axi4ComplianceChecker(
this.sIntf,
this.rIntf,
this.wIntf, {
required Component parent,
String name = 'axi4ComplianceChecker',
}) : super(name, parent);

@override
Future<void> run(Phase phase) async {
unawaited(super.run(phase));

// wait for reset to complete
await sIntf.resetN.nextPosedge;

// checks to run
// READ REQUESTS
// number of flits returned matches ARLEN if no error
// if RLAST is present, asserted on the final flit only
// if RID is present, every read response should match
// a pending request ARID
// WRITE REQUESTS
// number of flits sent matches AWLEN
// WLAST is asserted on the final flit only
// if BID is present, every write response should match
// a pending request AWID

final rLastPresent = rIntf.rLast != null;

final readReqMap = <int, List<int>>{};
final writeReqMap = <int, List<int>>{};
var lastWriteReqId = -1;

sIntf.clk.posedge.listen((event) {
// capture read requests for counting
if (rIntf.arValid.previousValue!.isValid &&
rIntf.arValid.previousValue!.toBool()) {
final id = rIntf.arId?.previousValue?.toInt() ?? 0;
final len = (rIntf.arLen?.previousValue?.toInt() ?? 0) + 1;
readReqMap[id] = [len, 0];
}

// track read response flits
if (rIntf.rValid.previousValue!.isValid &&
rIntf.rValid.previousValue!.toBool()) {
final id = rIntf.rId?.previousValue?.toInt() ?? 0;
if (!readReqMap.containsKey(id)) {
logger.severe(
'Cannot match a read response to any pending read request. '
'ID captured by the response was $id.');
}

readReqMap[id]![1] = readReqMap[id]![1] + 1;
final len = readReqMap[id]![0];
final currCount = readReqMap[id]![1];
if (currCount > len) {
logger.severe(
'Received more read response data flits than indicated by the '
'request with ID $id ARLEN. Expected $len but got $currCount');
} else if (currCount == len &&
rLastPresent &&
!rIntf.rLast!.previousValue!.toBool()) {
logger.severe('Received the final flit in the read response data per '
'the request with ID $id ARLEN but RLAST is not asserted.');
}
}

// track write requests
if (wIntf.awValid.previousValue!.isValid &&
wIntf.awValid.previousValue!.toBool()) {
final id = wIntf.awId?.previousValue?.toInt() ?? 0;
final len = (wIntf.awLen?.previousValue?.toInt() ?? 0) + 1;
writeReqMap[id] = [len, 0];
lastWriteReqId = id;
}

// track write data flits
if (wIntf.wValid.previousValue!.isValid &&
wIntf.wValid.previousValue!.toBool()) {
final id = lastWriteReqId;
if (!writeReqMap.containsKey(id)) {
logger.severe('There is no pending write request '
'to associate with valid write data.');
}

writeReqMap[id]![1] = writeReqMap[id]![1] + 1;
final len = writeReqMap[id]![0];
final currCount = writeReqMap[id]![1];
if (currCount > len) {
logger.severe(
'Sent more write data flits than indicated by the request '
'with ID $id AWLEN. Expected $len but sent $currCount');
} else if (currCount == len && !wIntf.wLast.previousValue!.toBool()) {
logger.severe('Sent the final flit in the write data per the request '
'with ID $id AWLEN but WLAST is not asserted.');
}
}
});
}
}
62 changes: 62 additions & 0 deletions lib/src/models/axi4_bfm/axi4_main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// axi4_main.dart
// An agent sending for AXI4 requests.
//
// 2025 January
// Author: Josh Kimmel <joshua1.kimmel@intel.com>

import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_hcl/src/models/axi4_bfm/axi4_bfm.dart';
import 'package:rohd_vf/rohd_vf.dart';

/// An agent for sending requests on [Axi4ReadInterface]s and [Axi4WriteInterface]s.
///
/// Driven read packets will update the returned data into the same packet.
class Axi4MainAgent extends Agent {
/// AXI4 System Interface.
final Axi4SystemInterface sIntf;

/// AXI4 Read Interface.
final Axi4ReadInterface rIntf;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there always a pair of 1 write and 1 read interface in AXI? Are there cases where someone might have variable numbers of both/either?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually a good question. I definitely know of instances where the block has a vectorized number of AXI interfaces. I can't think of an instance where the number of read instances doesn't match the number of write instances but maybe the spec further clarifies this?

Say we just support a vectorized number. Would we just instantiate that many drivers and sequencers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you could perhaps group things (e.g. a "write agent" has a driver and sequencer bundled together). I think a configurable number makes a lot of sense independently for reads and writes unless the spec specifically calls out that they must be the same number.


/// AXI4 Write Interface.
final Axi4WriteInterface wIntf;

/// The sequencer where requests should be sent.
late final Sequencer<Axi4RequestPacket> sequencer;

/// The driver that sends the requests over the interface.
late final Axi4MainDriver driver;

/// The number of cycles before timing out if no transactions can be sent.
final int timeoutCycles;

/// The number of cycles before an objection will be dropped when there are
/// no pending packets to send.
final int dropDelayCycles;

/// Constructs a new [Axi4MainAgent].
Axi4MainAgent({
required this.sIntf,
required this.rIntf,
required this.wIntf,
required Component parent,
String name = 'axiMainAgent',
this.timeoutCycles = 500,
this.dropDelayCycles = 30,
}) : super(name, parent) {
sequencer = Sequencer<Axi4RequestPacket>('sequencer', this);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the main agent come with a monitor included?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess yeah it probably should, right? I was just copying what APB did here (it also doesn't have a monitor).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the bad example haha, i think it makes sense probably to have a monitor included

driver = Axi4MainDriver(
parent: this,
sIntf: sIntf,
rIntf: rIntf,
wIntf: wIntf,
sequencer: sequencer,
timeoutCycles: timeoutCycles,
dropDelayCycles: dropDelayCycles,
);
}
}
Loading
Loading