diff --git a/doc/components/shift_register.md b/doc/components/shift_register.md index 634d8ff9..d73918e3 100644 --- a/doc/components/shift_register.md +++ b/doc/components/shift_register.md @@ -5,6 +5,6 @@ The `ShiftRegister` in ROHD-HCL is a configurable shift register including: - support for any width data - a configurable `depth` (which corresponds to the latency) - an optional `enable` -- an optional `reset` -- if `reset` is provided, an optional `resetValue` +- an optional `reset` (synchronous or asynchronous) +- if `reset` is provided, an optional `resetValue` for all stages or each stage indvidually - access to each of the `stages` output from each flop diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index e567ab6d..ac4acc26 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -10,6 +10,7 @@ import 'dart:collection'; import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; /// A shift register with configurable width and depth and optional enable and /// reset. @@ -37,14 +38,18 @@ class ShiftRegister extends Module { final String dataName; /// Creates a new shift register with specified [depth] which is only active - /// when [enable]d. If [reset] is provided, it will reset to a default of `0` - /// at all stages synchronously with [clk] or to the provided [resetValue]. + /// when [enable]d. If [reset] is provided, it will reset synchronously with + /// [clk] or aynchronously if [asyncReset] is true. The [reset] will reset all + /// stages to a default of `0` or to the provided [resetValue]. + /// If [resetValue] is a [List] the stages will reset to the corresponding + /// value in the list. ShiftRegister( Logic dataIn, { required Logic clk, required this.depth, Logic? enable, Logic? reset, + bool asyncReset = false, dynamic resetValue, this.dataName = 'data', }) : width = dataIn.width, @@ -55,6 +60,7 @@ class ShiftRegister extends Module { addOutput('${dataName}_out', width: width); Map? resetValues; + if (reset != null) { reset = addInput('reset', reset); @@ -63,6 +69,23 @@ class ShiftRegister extends Module { resetValue = addInput('resetValue', resetValue, width: resetValue.width); } + + if (resetValue is List) { + // Check if list length is equal to depth + if (resetValue.length != depth) { + throw RohdHclException( + 'ResetValue list length must equal shift register depth.'); + } + + for (var i = 0; i < resetValue.length; i++) { + final element = resetValue[i]; + if (element is Logic) { + resetValue[i] = + addInput('resetValue$i', element, width: element.width); + } + } + } + resetValues = {}; } } @@ -73,7 +96,8 @@ class ShiftRegister extends Module { for (var i = 0; i < depth; i++) { final stageI = addOutput(_stageName(i), width: width); conds.add(stageI < dataStage); - resetValues?[stageI] = resetValue; + + resetValues?[stageI] = resetValue is List ? resetValue[i] : resetValue; dataStage = stageI; } @@ -83,8 +107,8 @@ class ShiftRegister extends Module { conds = [If(enable, then: conds)]; } - Sequential( - clk, + Sequential.multi( + [clk, if (asyncReset && reset != null) reset], reset: reset, resetValues: resetValues, conds, diff --git a/test/shift_register_test.dart b/test/shift_register_test.dart index eef3ddc4..556c174e 100644 --- a/test/shift_register_test.dart +++ b/test/shift_register_test.dart @@ -209,4 +209,113 @@ void main() { await Simulator.endSimulation(); }); + + group('list reset value shift register', () { + Future listResetTest( + dynamic resetVal, void Function(Logic dataOut) check) async { + final dataIn = Logic(width: 8); + final clk = SimpleClockGenerator(10).clk; + const depth = 5; + final reset = Logic(); + final dataOut = ShiftRegister(dataIn, + clk: clk, depth: depth, reset: reset, resetValue: resetVal) + .dataOut; + + unawaited(Simulator.run()); + + dataIn.put(0x45); + reset.put(true); + + await clk.nextPosedge; + + reset.put(false); + + await clk.waitCycles(3); + + check(dataOut); + + await Simulator.endSimulation(); + } + + test('list of logics reset value', () async { + await listResetTest([ + Logic(width: 8)..put(0x2), + Logic(width: 8)..put(0x10), + Logic(width: 8)..put(0x22), + Logic(width: 8)..put(0x33), + Logic(width: 8)..put(0x42), + ], (dataOut) { + expect(dataOut.value.toInt(), 0x10); + }); + }); + + test('list of mixed reset value', () async { + await listResetTest([ + Logic(width: 8)..put(0x2), + 26, + Logic(width: 8)..put(0x22), + true, + Logic(width: 8)..put(0x42), + ], (dataOut) { + expect(dataOut.value.toInt(), 0x1A); + }); + }); + }); + + group('async reset shift register', () { + Future asyncResetTest( + dynamic resetVal, void Function(Logic dataOut) check) async { + final dataIn = Logic(width: 8); + final clk = SimpleClockGenerator(10).clk; + const depth = 5; + final reset = Logic(); + final dataOut = ShiftRegister(dataIn, + clk: Const(0), + depth: depth, + reset: reset, + resetValue: resetVal, + asyncReset: true) + .dataOut; + + unawaited(Simulator.run()); + + dataIn.put(0x42); + + reset.inject(false); + + await clk.waitCycles(1); + + reset.inject(true); + + await clk.waitCycles(1); + + check(dataOut); + + await Simulator.endSimulation(); + } + + test('async reset value', () async { + await asyncResetTest(Const(0x78, width: 8), (dataOut) { + expect(dataOut.value.toInt(), 0x78); + }); + }); + + test('async null reset value', () async { + await asyncResetTest(null, (dataOut) { + expect(dataOut.value.toInt(), 0); + }); + }); + + test('async reset with list mixed type', () async { + await asyncResetTest([ + Logic(width: 8)..put(0x2), + 59, + Const(0x78, width: 8), + Logic(width: 8)..put(0x33), + true, + ], (dataOut) { + expect(dataOut.value.toInt(), 0x1); + }); + }); + }); }