Skip to content

Commit

Permalink
Fix memory model handling of different edge and latency situations (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Jan 16, 2024
1 parent c319f0f commit 77b1c9d
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 105 deletions.
8 changes: 7 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Some in-development items will have opened issues, as well. Feel free to create
- Count
- [Count bit occurence](./components/count.md)
- Count pattern occurence
- Detection
- Edge detection
- Sort
- [Bitonic sort](./components/sort.md#bitonic-sort)
- Arithmetic
Expand All @@ -45,6 +47,8 @@ Some in-development items will have opened issues, as well. Feel free to create
- BFloat16 (16-bit)
- BFloat8 (8-bit)
- BFloat4 (4-bit)
- Fixed point
- Binary-Coded Decimal (BCD)
- [Rotate](./components/rotate.md)
- Counters
- Binary counter
Expand Down Expand Up @@ -85,8 +89,10 @@ Some in-development items will have opened issues, as well. Feel free to create
- Models
- [APB](./components/apb_bfm.md)
- [Ready/Valid](./components/ready_valid_bfm.md)
- SPI
- CXL

----------------

Copyright (C) 2023 Intel Corporation
Copyright (C) 2023-2024 Intel Corporation
SPDX-License-Identifier: BSD-3-Clause
12 changes: 10 additions & 2 deletions lib/src/memory/memory.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// memory.dart
Expand Down Expand Up @@ -96,6 +96,8 @@ abstract class Memory extends Module {
final int dataWidth;

/// The number of cycles before data is returned after a read.
///
/// Must be non-negative.
int get readLatency;

/// Internal write ports.
Expand All @@ -115,10 +117,12 @@ abstract class Memory extends Module {
Logic get reset => input('reset');

/// Construct a new memory.
///
/// Must provide at least one port (read or write).
Memory(Logic clk, Logic reset, List<DataPortInterface> writePorts,
List<DataPortInterface> readPorts,
{super.name = 'memory'})
: assert(writePorts.isNotEmpty && readPorts.isNotEmpty,
: assert(!(writePorts.isEmpty && readPorts.isEmpty),
'Must specify at least one read port or one write port.'),
numWrites = writePorts.length,
numReads = readPorts.length,
Expand All @@ -129,6 +133,10 @@ abstract class Memory extends Module {
? writePorts[0].addrWidth
: readPorts[0].addrWidth // at least one of these must exist
{
if (readLatency < 0) {
throw RohdHclException('Read latency must be non-negative.');
}

// make sure widths of everything match expectations
for (final port in [...writePorts, ...readPorts]) {
if (port.addrWidth != addrWidth) {
Expand Down
8 changes: 5 additions & 3 deletions lib/src/models/apb_bfm/apb_tracker.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// apb_tracker.dart
Expand Down Expand Up @@ -44,13 +44,15 @@ class ApbTracker extends Tracker<ApbPacket> {
super.outputFolder,
int timeColumnWidth = 12,
int selectColumnWidth = 4,
int addrColumnWidth = 12,
int dataColumnWidth = 12,
}) : super(name, [
TrackerField(timeField, columnWidth: timeColumnWidth),
if (selectColumnWidth > 0)
TrackerField(selectField, columnWidth: selectColumnWidth),
const TrackerField(typeField, columnWidth: 1),
const TrackerField(addrField, columnWidth: 12),
const TrackerField(dataField, columnWidth: 12),
TrackerField(addrField, columnWidth: addrColumnWidth),
TrackerField(dataField, columnWidth: dataColumnWidth),
const TrackerField(strobeField, columnWidth: 4),
if (intf.includeSlvErr)
const TrackerField(slverrField, columnWidth: 1),
Expand Down
65 changes: 36 additions & 29 deletions lib/src/models/memory_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2023 Intel Corporation
// Copyright (C) 2021-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// memory_model.dart
Expand All @@ -7,8 +7,11 @@
// 2023 June 12
// Author: Max Korbel <[email protected]>

import 'dart:async';

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

/// A model of a [Memory] which uses a software-based [SparseMemoryStorage] to
/// store data.
Expand All @@ -19,9 +22,6 @@ class MemoryModel extends Memory {
/// The memory storage underlying this model.
late final MemoryStorage storage;

/// A pre-signal before the output flops of this memory.
late final List<Logic> _rdDataPre;

/// If true, a positive edge on reset will reset the memory asynchronously.
final bool asyncReset;

Expand All @@ -47,9 +47,6 @@ class MemoryModel extends Memory {
}

void _buildLogic() {
_rdDataPre = List.generate(
numReads, (index) => Logic(name: 'rdDataPre$index', width: dataWidth));

if (asyncReset) {
reset.posedge.listen((event) {
storage.reset();
Expand All @@ -63,8 +60,7 @@ class MemoryModel extends Memory {
return;
}
for (final wrPort in wrPorts) {
if (!(wrPort.en.previousValue?.isValid ?? wrPort.en.value.isValid) &&
!storage.isEmpty) {
if (!wrPort.en.previousValue!.isValid && !storage.isEmpty) {
// storage doesnt have access to `en`, so check ourselves
storage.invalidWrite();
return;
Expand All @@ -91,34 +87,45 @@ class MemoryModel extends Memory {
}
}
}
});

// on any glitch to read controls, change pre-flop version of read data
for (var i = 0; i < rdPorts.length; i++) {
clk.negedge.listen((event) => _updatePreRead(i));

// flop out the read data
var delayedData = _rdDataPre[i];
for (var delay = 0; delay < readLatency; delay++) {
delayedData = FlipFlop(clk, delayedData).q;
// if we have at least 1 cycle, then we wait to update the data
if (readLatency > 0) {
for (final rdPort in rdPorts) {
if (!rdPort.en.previousValue!.isValid ||
!rdPort.en.previousValue!.toBool() ||
!rdPort.addr.previousValue!.isValid) {
unawaited(_updateRead(
rdPort, LogicValue.filled(rdPort.dataWidth, LogicValue.x)));
} else {
unawaited(_updateRead(rdPort, storage.readData(rdPort.addr.value)));
}
}
}
});

rdPorts[i].data <= delayedData;
// if latency is 0, we need to update immediately
if (readLatency == 0) {
for (final rdPort in rdPorts) {
rdPort.en.glitch.listen((args) => _updateReadZeroLatency(rdPort));
rdPort.addr.glitch.listen((args) => _updateReadZeroLatency(rdPort));
}
}
}

void _updatePreRead(int rdIndex) {
final rdPort = rdPorts[rdIndex];
final rdPortPre = _rdDataPre[rdIndex];

/// Updates read data for [rdPort] immediately (as if combinationally).
void _updateReadZeroLatency(DataPortInterface rdPort) {
if (!rdPort.en.value.isValid ||
(rdPort.en.value == LogicValue.one && !rdPort.addr.value.isValid)) {
rdPortPre.put(LogicValue.x, fill: true);
return;
!rdPort.en.value.toBool() ||
!rdPort.addr.value.isValid) {
rdPort.data.put(LogicValue.x, fill: true);
} else {
rdPort.data.put(storage.readData(rdPort.addr.value));
}
}

if (rdPort.en.value == LogicValue.one) {
rdPortPre.put(storage.readData(rdPort.addr.value));
}
/// Updates read data for [rdPort] after [readLatency] time.
Future<void> _updateRead(DataPortInterface rdPort, LogicValue data) async {
await clk.waitCycles(readLatency - 1);
rdPort.data.inject(data);
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ environment:

dependencies:
meta: ^1.9.1
rohd: ^0.5.0
rohd: ^0.5.2
rohd_vf: ^0.5.0

dev_dependencies:
Expand Down
17 changes: 6 additions & 11 deletions test/arbiter_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// arbiter_test.dart
Expand Down Expand Up @@ -91,8 +91,7 @@ void main() {
await clk.nextNegedge;
expect(grants.value, LogicValue.ofString('00000010'));

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('dynamic request', () async {
Expand Down Expand Up @@ -142,8 +141,7 @@ void main() {
await clk.nextNegedge;
expect(grants.value, LogicValue.ofString('10000000'));

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('all reqs', () async {
Expand All @@ -167,8 +165,7 @@ void main() {
await clk.nextNegedge;
}

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('all reqs, non-power-of-2', () async {
Expand All @@ -193,8 +190,7 @@ void main() {
await clk.nextNegedge;
}

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('beat pattern 2 request maintain fairness', () async {
Expand Down Expand Up @@ -233,8 +229,7 @@ void main() {
await clk.nextPosedge;
}

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();

// expect an equal number of grants between the two
expect(grantCounts[0], grantCounts[1]);
Expand Down
26 changes: 9 additions & 17 deletions test/fifo_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// fifo_test.dart
Expand Down Expand Up @@ -123,8 +123,7 @@ void main() {

await clk.nextNegedge;

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo with depth 1', () async {
Expand Down Expand Up @@ -179,8 +178,7 @@ void main() {
expect(fifo.empty.value.toBool(), true);
expect(fifo.error!.value.toBool(), false);

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo underflow error without bypass', () async {
Expand Down Expand Up @@ -218,8 +216,7 @@ void main() {

expect(fifo.error!.value.toBool(), true);

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo underflow error with bypass', () async {
Expand Down Expand Up @@ -258,8 +255,7 @@ void main() {

expect(fifo.error!.value.toBool(), true);

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo overflow error', () async {
Expand Down Expand Up @@ -306,8 +302,7 @@ void main() {
expect(fifo.error!.value.toBool(), true);
await clk.nextPosedge;

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo empty bypass', () async {
Expand Down Expand Up @@ -361,8 +356,7 @@ void main() {

await clk.nextNegedge;

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

test('fifo full write and read simultaneously', () async {
Expand Down Expand Up @@ -422,8 +416,7 @@ void main() {
await clk.nextNegedge;
expect(fifo.error!.value.toBool(), false);

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
});

group('fifo peek', () {
Expand Down Expand Up @@ -474,8 +467,7 @@ void main() {
// peek at stable
expect(rdData.value.toInt(), 0xfeedbeef);

Simulator.endSimulation();
await Simulator.simulationEnded;
await Simulator.endSimulation();
}

test('no bypass', () async {
Expand Down
Loading

0 comments on commit 77b1c9d

Please sign in to comment.