From 968b7931b1f278fb6d173c580584721c5ff5fcc5 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Fri, 2 Jan 2026 21:57:36 -0800 Subject: [PATCH 1/4] feat(river_hdl): adding microcode execution --- flake.nix | 21 + packages/riscv/lib/src/ops.dart | 143 +- packages/river_hdl/bin/river_hdlgen.dart | 3 + packages/river_hdl/lib/river_hdl.dart | 1 + packages/river_hdl/lib/src/core.dart | 54 +- packages/river_hdl/lib/src/core/csr.dart | 55 +- packages/river_hdl/lib/src/core/decoder.dart | 51 +- packages/river_hdl/lib/src/core/exec.dart | 2942 +++++++++++++---- packages/river_hdl/lib/src/core/pipeline.dart | 495 ++- packages/river_hdl/lib/src/soc.dart | 5 +- packages/river_hdl/pubspec.yaml | 4 + packages/river_hdl/test/core/exec_test.dart | 529 +-- .../river_hdl/test/core/pipeline_test.dart | 1 + pubspec.lock | 61 +- pubspec.lock.json | 65 +- 15 files changed, 3351 insertions(+), 1079 deletions(-) diff --git a/flake.nix b/flake.nix index 53503d1..b620281 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,7 @@ gitHashes = { rohd_hcl = "sha256-YobXIH2PTUXxp6MfcAIJG8aXhkc1MZOLthOEaQUJxOM="; + z3 = "sha256-B1gmCsFivLDKCJcBmdf2JOJqDvn6bEawqJ8QLix5Ils="; }; buildDartTest = @@ -120,6 +121,10 @@ src = ./.; + buildInputs = [ + pkgs.z3 + ]; + dartEntryPoints = { "bin/river-emulator" = "packages/river_emulator/bin/river_emulator.dart"; "bin/river-hdlgen" = "packages/river_hdl/bin/river_hdlgen.dart"; @@ -128,6 +133,10 @@ preBuild = '' mkdir -p bin ''; + + postBuild = '' + patchelf bin/river-hdlgen --add-needed libz3.so + ''; }; emulator = buildDartApplication { pname = "river-emulator"; @@ -146,6 +155,10 @@ pname = "river-hdl"; inherit version pubspecLock gitHashes; + buildInputs = [ + pkgs.z3 + ]; + src = ./.; packageRoot = "packages/river_hdl"; @@ -154,10 +167,16 @@ preBuild = '' mkdir -p bin ''; + + postBuild = '' + patchelf bin/river-hdlgen --add-needed libz3.so + ''; }; }; devShells.default = pkgs.mkShell { + LD_LIBRARY_PATH = "${pkgs.z3.lib}/lib"; + packages = with pkgs; ( @@ -167,7 +186,9 @@ yosys icestorm nextpnr + z3 gtkwave + surfer pkgsCross.riscv32-embedded.stdenv.cc pkgsCross.riscv64-embedded.stdenv.cc ] diff --git a/packages/riscv/lib/src/ops.dart b/packages/riscv/lib/src/ops.dart index bcf0c4f..d343984 100644 --- a/packages/riscv/lib/src/ops.dart +++ b/packages/riscv/lib/src/ops.dart @@ -127,9 +127,10 @@ class MicroOpEncoding { required this.constructor, }); - int encode(T op, Mxlen mxlen) => struct(mxlen).encode(op.toMap()); + BigInt encode(T op, Mxlen mxlen) => struct(mxlen).bigEncode(op.toMap()); - T decode(int value, Mxlen mxlen) => constructor(struct(mxlen).decode(value)); + T decode(BigInt value, Mxlen mxlen) => + constructor(struct(mxlen).bigDecode(value)); } /// {@category microcode} @@ -270,6 +271,8 @@ enum MicroOpMemSize { final int bytes; int get bits => bytes * 8; + + static const int width = 2; } /// {@category microcode} @@ -300,9 +303,9 @@ class WriteCsrMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'field': const BitRange(5, 8), - 'source': const BitRange(9, 12), - 'offset': BitRange(13, 13 + mxlen.size), + 'field': const BitRange(5, 7), + 'source': const BitRange(8, 10), + 'offset': BitRange(11, 11 + mxlen.size), }); } @@ -339,9 +342,9 @@ class ReadRegisterMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'source': const BitRange(5, 8), - 'offset': BitRange(9, 9 + mxlen.size), - 'valueOffset': BitRange(9 + mxlen.size, 9 + (mxlen.size * 2)), + 'source': const BitRange(5, 7), + 'offset': BitRange(8, 8 + mxlen.size - 1), + 'valueOffset': BitRange(8 + mxlen.size, 8 + (mxlen.size * 2) - 1), }); } @@ -382,10 +385,10 @@ class WriteRegisterMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'field': const BitRange(5, 8), - 'source': const BitRange(9, 12), - 'offset': BitRange(13, 13 + mxlen.size), - 'valueOffset': BitRange(13 + mxlen.size, 13 + (mxlen.size * 2)), + 'field': const BitRange(5, 7), + 'source': const BitRange(8, 10), + 'offset': BitRange(11, 11 + mxlen.size - 1), + 'valueOffset': BitRange(11 + mxlen.size, 11 + (mxlen.size * 2) - 1), }); } @@ -417,9 +420,9 @@ class ModifyLatchMicroOp extends MicroOp { static BitStruct struct(Mxlen _) => BitStruct({ 'funct': MicroOp.functRange, - 'field': const BitRange(5, 8), - 'source': const BitRange(9, 12), - 'replace': BitRange.single(13), + 'field': const BitRange(5, 7), + 'source': const BitRange(8, 10), + 'replace': BitRange.single(11), }); } @@ -451,9 +454,9 @@ class AluMicroOp extends MicroOp { static BitStruct struct(Mxlen _) => BitStruct({ 'funct': MicroOp.functRange, - 'alu': const BitRange(5, 10), - 'a': const BitRange(11, 14), - 'b': const BitRange(15, 18), + 'alu': const BitRange(5, 9), + 'a': const BitRange(10, 12), + 'b': const BitRange(13, 15), }); } @@ -497,11 +500,11 @@ class BranchIfMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'condition': const BitRange(5, 10), - 'target': const BitRange(11, 14), - 'hasField': const BitRange.single(15), - 'offsetField': const BitRange(16, 19), - 'offset': BitRange(20, 20 + mxlen.size), + 'condition': const BitRange(5, 7), + 'target': const BitRange(8, 10), + 'hasField': const BitRange.single(11), + 'offsetField': const BitRange(12, 14), + 'offset': BitRange(15, 15 + mxlen.size - 1), }); } @@ -559,11 +562,11 @@ class UpdatePCMicroOp extends MicroOp { 'source': const BitRange(5, 8), 'hasSource': const BitRange.single(9), 'hasField': const BitRange.single(10), - 'offsetField': const BitRange(11, 14), - 'offsetSource': const BitRange(15, 17), - 'absolute': const BitRange.single(18), - 'align': const BitRange.single(19), - 'offset': BitRange(20, 20 + mxlen.size), + 'offsetField': const BitRange(11, 13), + 'offsetSource': const BitRange(14, 16), + 'absolute': const BitRange.single(17), + 'align': const BitRange.single(18), + 'offset': BitRange(19, 19 + mxlen.size - 1), }); } @@ -604,10 +607,10 @@ class MemLoadMicroOp extends MicroOp { static BitStruct struct(Mxlen _) => BitStruct({ 'funct': MicroOp.functRange, - 'base': const BitRange(5, 8), - 'dest': const BitRange(9, 12), - 'size': const BitRange(13, 14), - 'unsigned': BitRange.single(15), + 'base': const BitRange(5, 7), + 'dest': const BitRange(8, 10), + 'size': const BitRange(11, 12), + 'unsigned': BitRange.single(13), }); } @@ -643,9 +646,9 @@ class MemStoreMicroOp extends MicroOp { static BitStruct struct(Mxlen _) => BitStruct({ 'funct': MicroOp.functRange, - 'base': const BitRange(5, 8), - 'src': const BitRange(9, 12), - 'size': const BitRange(13, 14), + 'base': const BitRange(5, 7), + 'src': const BitRange(8, 10), + 'size': const BitRange(11, 12), }); } @@ -672,10 +675,8 @@ class TrapMicroOp extends MicroOp { Map toMap() => { 'funct': funct, 'machine': kindMachine.index, - 'hasSupervisor': kindSupervisor != null ? 1 : 0, - 'supervisor': kindSupervisor?.index ?? 0, - 'hasUser': kindUser != null ? 1 : 0, - 'user': kindUser?.index ?? 0, + 'supervisor': kindSupervisor?.index ?? kindMachine.index, + 'user': kindSupervisor?.index ?? kindMachine.index, }; @override @@ -685,12 +686,9 @@ class TrapMicroOp extends MicroOp { static BitStruct struct(Mxlen _) => BitStruct({ 'funct': MicroOp.functRange, - // 8 bits per trap kind - 'machine': const BitRange(5, 12), - 'supervisor': const BitRange(13, 20), - 'user': const BitRange(21, 28), - 'hasSupervisor': BitRange.single(29), - 'hasUser': BitRange.single(30), + 'machine': const BitRange(5, 9), + 'supervisor': const BitRange(10, 14), + 'user': const BitRange(15, 19), }); } @@ -808,8 +806,8 @@ class WriteLinkRegisterMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'link': const BitRange(5, 7), - 'pcOffset': BitRange(8, 8 + mxlen.size), + 'link': const BitRange.single(5), + 'pcOffset': BitRange(6, 6 + mxlen.size - 1), }); } @@ -984,9 +982,9 @@ class ValidateFieldMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'condition': const BitRange(5, 8), - 'field': const BitRange(9, 12), - 'value': BitRange(12, 12 + mxlen.size), + 'condition': const BitRange(5, 7), + 'field': const BitRange(8, 10), + 'value': BitRange(10, 10 + mxlen.size - 1), }); } @@ -1015,8 +1013,8 @@ class SetFieldMicroOp extends MicroOp { static BitStruct struct(Mxlen mxlen) => BitStruct({ 'funct': MicroOp.functRange, - 'field': const BitRange(5, 8), - 'value': BitRange(9, 9 + mxlen.size), + 'field': const BitRange(5, 7), + 'value': BitRange(8, 8 + mxlen.size - 1), }); } @@ -1035,11 +1033,10 @@ class ReadCsrMicroOp extends MicroOp { @override String toString() => 'ReadCsrMicroOp($source)'; - // NOTE: must be unique, SetField uses 21 static const int funct = 22; static BitStruct struct(Mxlen _) => - BitStruct({'funct': MicroOp.functRange, 'source': const BitRange(5, 8)}); + BitStruct({'funct': MicroOp.functRange, 'source': const BitRange(5, 7)}); } class OperationDecodePattern { @@ -1268,21 +1265,25 @@ class Operation { bool matches(InstructionType instr) => _mapMatch(instr.toMap()); - int microcodeWidth(Mxlen mxlen) => microcode + int mopWidth(Mxlen mxlen) => microcode .map((mop) { final m = mop.toMap(); final funct = m['funct']!; final e = kMicroOpTable.firstWhere((e) => e.funct == funct); - final s = e.struct(mxlen); - - for (final field in s.mapping.entries) { - m[field.key] = field.value.mask; - } - - return s.encode(m).bitLength; + return e.struct(mxlen).width; }) .fold(0, (a, b) => a > b ? a : b); + List mopEncode(Mxlen mxlen) => [ + BigInt.from(microcode.length), + ...microcode.map((mop) { + final m = mop.toMap(); + final funct = m['funct']!; + final e = kMicroOpTable.firstWhere((e) => e.funct == funct); + return e.struct(mxlen).bigEncode(m); + }), + ]; + @override String toString() => 'Operation(mnemonic: $mnemonic, opcode: $opcode, funct2: $funct2,' @@ -1385,12 +1386,19 @@ class Microcode { fieldIndices, ).width; - int get opIndexWidth => decodeLookup.keys.fold(0, (a, b) => a > b ? a : b); + int get opIndexWidth => + decodeLookup.keys.fold(0, (a, b) => a > b ? a : b).bitLength; - int opWidth(Mxlen mxlen) => map.values - .map((op) => op.microcodeWidth(mxlen)) + int mopWidth(Mxlen mxlen) => map.values + .map((op) => op.mopWidth(mxlen)) .fold(0, (a, b) => a > b ? a : b); + int mopIndexWidth(Mxlen mxlen) => encodedMops(mxlen).length.bitLength; + + List encodedMops(Mxlen mxlen) => map.values + .map((m) => m.mopEncode(mxlen)) + .fold([], (a, b) => [...a, ...b]); + Map get decodeLookup { Map result = {}; var i = 0; @@ -1621,4 +1629,9 @@ class Microcode { final name = i.runtimeType.toString(); return name.substring(10, name.length - 1); } + + static String mopType(MicroOpEncoding i) { + final name = i.runtimeType.toString(); + return name.substring(16, name.length - 8); + } } diff --git a/packages/river_hdl/bin/river_hdlgen.dart b/packages/river_hdl/bin/river_hdlgen.dart index 1229b3d..b606a68 100644 --- a/packages/river_hdl/bin/river_hdlgen.dart +++ b/packages/river_hdl/bin/river_hdlgen.dart @@ -123,6 +123,8 @@ Future main(List arguments) async { Logger.root.finest('River SoC configured: $socConfig'); + List staticInstructions = []; + final ip = RiverSoCIP( socConfig, deviceOptions: Map.fromEntries( @@ -162,6 +164,7 @@ Future main(List arguments) async { ), ), ), + staticInstructions: staticInstructions, ); Logger.root.finest('River SoC module created: $ip'); diff --git a/packages/river_hdl/lib/river_hdl.dart b/packages/river_hdl/lib/river_hdl.dart index 5b4c033..f12e301 100644 --- a/packages/river_hdl/lib/river_hdl.dart +++ b/packages/river_hdl/lib/river_hdl.dart @@ -10,5 +10,6 @@ export 'src/core/pipeline.dart'; export 'src/core.dart'; export 'src/dev.dart'; export 'src/devices.dart'; + export 'src/memory/port.dart'; export 'src/soc.dart'; diff --git a/packages/river_hdl/lib/src/core.dart b/packages/river_hdl/lib/src/core.dart index 1d81089..0cdceb7 100644 --- a/packages/river_hdl/lib/src/core.dart +++ b/packages/river_hdl/lib/src/core.dart @@ -24,6 +24,7 @@ class RiverCoreIP extends BridgeModule { RiverCoreIP( this.config, { Map srcIrqs = const {}, + List staticInstructions = const [], super.name = 'river_core', }) : super('RiverCore') { createPort('clk', PortDirection.input); @@ -239,13 +240,31 @@ class RiverCoreIP extends BridgeModule { ) : null; - if (csrs != null) { + if (csrs != null && csrs.satp != null) { + pagingMode <= + (((csrs.satp! >> + Const( + config.mxlen.satpModeShift, + width: config.mxlen.size, + )) & + Const(config.mxlen.satpModeMask, width: config.mxlen.size))) + .slice(pagingMode.width - 1, 0); + pageTableAddress <= + (csrs.satp! & + Const(config.mxlen.satpPpnMask, width: config.mxlen.size)); + } else { pagingMode <= Const(0, width: pagingMode.width); pageTableAddress <= Const(0, width: config.mxlen.size); - // TODO: drive pagingMode, pageTableAddress, mxr, and sum from CSRs } - final microcodeRead = DataPortInterface( + if (csrs != null) { + enableMxr <= + ((csrs.mstatus >> 19) & Const(1, width: config.mxlen.size)).neq(0); + enableSum <= + ((csrs.mstatus >> 18) & Const(1, width: config.mxlen.size)).neq(0); + } + + final microcodeDecodeRead = DataPortInterface( config.microcode.patternWidth, config.microcode.map.length.bitLength, ); @@ -255,13 +274,32 @@ class RiverCoreIP extends BridgeModule { clk, reset, [], - [microcodeRead], + [microcodeDecodeRead], numEntries: config.microcode.map.length, resetValue: config.microcode.encodedPatterns, definitionName: 'RiverMicrocodeLookup', ); } + final microcodeExecRead = DataPortInterface( + config.microcode.mopWidth(config.mxlen), + config.microcode.mopIndexWidth(config.mxlen), + ); + + if (config.microcodeMode.onExec != MicrocodePipelineMode.none) { + final mops = config.microcode.encodedMops(config.mxlen); + + RegisterFile( + clk, + reset, + [], + [microcodeExecRead], + numEntries: mops.length, + resetValue: mops, + definitionName: 'RiverMicrocodeOperations', + ); + } + pipeline = RiverPipeline( clk, reset, @@ -279,10 +317,15 @@ class RiverCoreIP extends BridgeModule { rs2Read, rdWrite, config.microcodeMode.onDecoder != MicrocodePipelineMode.none - ? microcodeRead + ? microcodeDecodeRead + : null, + config.microcodeMode.onExec != MicrocodePipelineMode.none + ? microcodeExecRead : null, useMixedDecoders: config.microcodeMode.onDecoder == MicrocodePipelineMode.in_parallel, + useMixedExecution: + config.microcodeMode.onExec == MicrocodePipelineMode.in_parallel, microcode: config.microcode, mxlen: config.mxlen, hasSupervisor: config.hasSupervisor, @@ -292,6 +335,7 @@ class RiverCoreIP extends BridgeModule { medeleg: csrs?.medeleg, mtvec: csrs?.mtvec, stvec: csrs?.stvec, + staticInstructions: staticInstructions, ); Sequential(clk, [ diff --git a/packages/river_hdl/lib/src/core/csr.dart b/packages/river_hdl/lib/src/core/csr.dart index 5476666..6f4c25c 100644 --- a/packages/river_hdl/lib/src/core/csr.dart +++ b/packages/river_hdl/lib/src/core/csr.dart @@ -119,7 +119,11 @@ class RiscVCsrFile extends Module { addOutput('mideleg', width: mxlen.size); addOutput('medeleg', width: mxlen.size); addOutput('mtvec', width: mxlen.size); - addOutput('stvec', width: mxlen.size); + + if (hasSupervisor) { + addOutput('stvec', width: mxlen.size); + addOutput('satp', width: mxlen.size); + } void _checkFits(String n, int v) { final max = (mxlen.size >= 63) ? null : (1 << mxlen.size); @@ -197,10 +201,15 @@ class RiscVCsrFile extends Module { _csrTop.getBackdoorPortsByAddr(0, CsrAddress.medeleg.address).rdData!; mtvec <= _csrTop.getBackdoorPortsByAddr(0, CsrAddress.mtvec.address).rdData!; - if (hasSupervisor) + + if (hasSupervisor) { stvec! <= _csrTop.getBackdoorPortsByAddr(0, CsrAddress.stvec.address).rdData!; + satp! <= + _csrTop.getBackdoorPortsByAddr(0, CsrAddress.satp.address).rdData!; + } + if (externalPending != null) { final mip = _csrTop.getBackdoorPortsByAddr(0, CsrAddress.mip.address); mip.wrEn! <= Const(1); @@ -256,6 +265,7 @@ class RiscVCsrFile extends Module { addr: CsrAddress.mstatus.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('mie'), @@ -275,6 +285,7 @@ class RiscVCsrFile extends Module { addr: CsrAddress.mtvec.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('mscratch'), @@ -288,18 +299,21 @@ class RiscVCsrFile extends Module { addr: CsrAddress.mepc.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('mcause'), addr: CsrAddress.mcause.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('mtval'), addr: CsrAddress.mtval.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('medeleg'), @@ -312,6 +326,7 @@ class RiscVCsrFile extends Module { addr: CsrAddress.mideleg.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), if (hasSupervisor) ...[ @@ -320,54 +335,63 @@ class RiscVCsrFile extends Module { addr: CsrAddress.sstatus.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('sie'), addr: CsrAddress.sie.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('sip'), addr: CsrAddress.sip.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('stvec'), addr: CsrAddress.stvec.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('sscratch'), addr: CsrAddress.sscratch.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('sepc'), addr: CsrAddress.sepc.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('scause'), addr: CsrAddress.scause.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('stval'), addr: CsrAddress.stval.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('satp'), addr: CsrAddress.satp.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), ], @@ -377,48 +401,56 @@ class RiscVCsrFile extends Module { addr: CsrAddress.ustatus.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('uie'), addr: CsrAddress.uie.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('uip'), addr: CsrAddress.uip.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('utvec'), addr: CsrAddress.utvec.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('uscratch'), addr: CsrAddress.uscratch.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('uepc'), addr: CsrAddress.uepc.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('ucause'), addr: CsrAddress.ucause.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), CsrInstanceConfig( arch: SimpleRwCsr('utval'), addr: CsrAddress.utval.address, resetValue: 0, width: mxlen.size, + isBackdoorWritable: false, ), ], @@ -568,17 +600,17 @@ class RiscVCsrFile extends Module { _fdRead.addr <= rdAddr12; _fdRead.en <= csrRead.en & rdLegal; csrRead.data <= _fdRead.data; - csrRead.done <= _fdRead.done | csrRead.en; - csrRead.valid <= _fdRead.valid & rdLegal; + csrRead.done <= _fdRead.done & csrRead.en; + csrRead.valid <= _fdRead.valid & csrRead.en & rdLegal; _fdWrite.addr <= wrAddr12; final maskedWriteData = _maskWriteData(wrAddr12, csrWrite.data); _fdWrite.data <= maskedWriteData; - _fdWrite.en <= csrWrite.en; - csrWrite.done <= _fdWrite.done; - csrWrite.valid <= _fdWrite.valid & wrLegal; + _fdWrite.en <= csrWrite.en & wrLegal; + csrWrite.done <= _fdWrite.done & csrWrite.en; + csrWrite.valid <= _fdWrite.valid & csrWrite.en & wrLegal; } void _bindBackdoorForCounters() { @@ -635,6 +667,12 @@ class RiscVCsrFile extends Module { return _csrTop.getBackdoorPortsByAddr(0, address.toInt()).rdData?.value; } + CsrBackdoorInterface getBackdoor(LogicValue address) { + assert(address.width == 12); + + return _csrTop.getBackdoorPortsByAddr(0, address.toInt()); + } + Logic get mvendorid => _csrTop.getBackdoorPortsByAddr(0, CsrAddress.mvendorid.address).rdData!; Logic get marchid => @@ -668,6 +706,5 @@ class RiscVCsrFile extends Module { _csrTop.getBackdoorPortsByAddr(0, CsrAddress.scause.address).rdData!; Logic get stval => _csrTop.getBackdoorPortsByAddr(0, CsrAddress.stval.address).rdData!; - Logic get satp => - _csrTop.getBackdoorPortsByAddr(0, CsrAddress.satp.address).rdData!; + Logic? get satp => hasSupervisor ? output('satp') : null; } diff --git a/packages/river_hdl/lib/src/core/decoder.dart b/packages/river_hdl/lib/src/core/decoder.dart index 6057e0f..0366039 100644 --- a/packages/river_hdl/lib/src/core/decoder.dart +++ b/packages/river_hdl/lib/src/core/decoder.dart @@ -5,10 +5,12 @@ import 'package:riscv/riscv.dart'; abstract class InstructionDecoder extends Module { final Mxlen mxlen; final Microcode microcode; + final List staticInstructions; Logic get done => output('done'); Logic get valid => output('valid'); Logic get index => output('index'); + Logic get counter => output('counter'); Map get fields => Map.fromEntries( fieldWidths.entries.map( @@ -24,10 +26,11 @@ abstract class InstructionDecoder extends Module { Logic reset, Logic enable, Logic input, { + int counterWidth = 32, DataPortInterface? microcodeRead, required this.microcode, required this.mxlen, - List staticInstructions = const [], + this.staticInstructions = const [], super.name = 'river_instruction_decoder', }) { clk = addInput('clk', clk); @@ -50,6 +53,7 @@ abstract class InstructionDecoder extends Module { addOutput('valid'); addOutput('index', width: microcode.opIndexWidth); addOutput('imm', width: mxlen.size); + addOutput('counter', width: counterWidth); for (final entry in fieldWidths.entries) { if (entry.key == 'imm') continue; @@ -70,6 +74,7 @@ abstract class InstructionDecoder extends Module { valid < 0, index < 0, done < 0, + counter < 0, if (microcodeRead != null) ...[ microcodeRead!.en < 0, microcodeRead!.addr < 0, @@ -82,6 +87,7 @@ abstract class InstructionDecoder extends Module { If( enable, then: [ + counter < (counter + 1), ...decode(input), if (microcodeRead != null) ...decodeMicrocode(input, microcodeRead!), @@ -185,6 +191,7 @@ class DynamicInstructionDecoder extends InstructionDecoder { DataPortInterface microcodeRead, { required Microcode microcode, required Mxlen mxlen, + int counterWidth = 32, List staticInstructions = const [], String name = 'river_dynamic_instruction_decoder', }) : super( @@ -195,6 +202,7 @@ class DynamicInstructionDecoder extends InstructionDecoder { microcodeRead: microcodeRead, microcode: microcode, mxlen: mxlen, + counterWidth: counterWidth, staticInstructions: staticInstructions, name: name, ); @@ -330,6 +338,7 @@ class StaticInstructionDecoder extends InstructionDecoder { required super.microcode, required super.mxlen, super.staticInstructions, + super.counterWidth = 32, super.name = 'river_static_instruction_decoder', }); @@ -392,21 +401,29 @@ class StaticInstructionDecoder extends InstructionDecoder { Map lookupDecode(Logic input) => Map.fromEntries( - microcode.decodeLookup.entries.map((entry) { - final nzfMatch = entry.value.nzfMask == 0 - ? Const(1) - : (input & Const(entry.value.nzfMask, width: 32)).neq(0); - final zfMatch = entry.value.zfMask == 0 - ? Const(1) - : (input & Const(entry.value.zfMask, width: 32)).eq(0); - - final mask = Const(entry.value.mask, width: 32); - final value = Const(entry.value.value, width: 32); - - return MapEntry( - entry.value, - (input & mask).eq(value) & nzfMatch & zfMatch, - ); - }), + microcode.decodeLookup.entries + .where( + (entry) => staticInstructions.isNotEmpty + ? staticInstructions.contains( + microcode.execLookup[entry.key]!.mnemonic, + ) + : true, + ) + .map((entry) { + final nzfMatch = entry.value.nzfMask == 0 + ? Const(1) + : (input & Const(entry.value.nzfMask, width: 32)).neq(0); + final zfMatch = entry.value.zfMask == 0 + ? Const(1) + : (input & Const(entry.value.zfMask, width: 32)).eq(0); + + final mask = Const(entry.value.mask, width: 32); + final value = Const(entry.value.value, width: 32); + + return MapEntry( + entry.value, + (input & mask).eq(value) & nzfMatch & zfMatch, + ); + }), ); } diff --git a/packages/river_hdl/lib/src/core/exec.dart b/packages/river_hdl/lib/src/core/exec.dart index 5c0f13c..35468e3 100644 --- a/packages/river_hdl/lib/src/core/exec.dart +++ b/packages/river_hdl/lib/src/core/exec.dart @@ -2,11 +2,26 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:riscv/riscv.dart'; -class ExecutionUnit extends Module { +abstract class ExecutionUnit extends Module { final Microcode microcode; final Mxlen mxlen; + final bool hasSupervisor; + final bool hasUser; + final List staticInstructions; + + late final Logic clk; + late final Logic currentSp; + late final Logic currentPc; + late final Logic currentMode; + late final DataPortInterface? csrRead; + late final DataPortInterface? csrWrite; + late final Logic? mideleg; + late final Logic? medeleg; + late final Logic? mtvec; + late final Logic? stvec; Logic get done => output('done'); + Logic get valid => output('valid'); Logic get nextSp => output('nextSp'); Logic get nextPc => output('nextPc'); Logic get nextMode => output('nextMode'); @@ -15,6 +30,7 @@ class ExecutionUnit extends Module { Logic get trapTval => output('trapTval'); Logic get fence => output('fence'); Logic get interruptHold => output('interruptHold'); + Logic get counter => output('counter'); ExecutionUnit( Logic clk, @@ -33,24 +49,33 @@ class ExecutionUnit extends Module { DataPortInterface rs1Read, DataPortInterface rs2Read, DataPortInterface rdWrite, { - bool hasSupervisor = false, - bool hasUser = false, + DataPortInterface? microcodeRead, + this.hasSupervisor = false, + this.hasUser = false, required this.microcode, required this.mxlen, Logic? mideleg, Logic? medeleg, Logic? mtvec, Logic? stvec, - List staticInstructions = const [], + int counterWidth = 32, + this.staticInstructions = const [], super.name = 'river_execution_unit', }) { clk = addInput('clk', clk); + this.clk = clk; + reset = addInput('reset', reset); enable = addInput('enable', enable); - currentSp = addInput('currentSp', currentSp, width: mxlen.size); - currentPc = addInput('currentPc', currentPc, width: mxlen.size); - currentMode = addInput('currentMode', currentMode, width: 3); + this.currentSp = addInput('currentSp', currentSp, width: mxlen.size); + currentSp = this.currentSp; + + this.currentPc = addInput('currentPc', currentPc, width: mxlen.size); + currentPc = this.currentPc; + + this.currentMode = addInput('currentMode', currentMode, width: 3); + currentMode = this.currentMode; instrIndex = addInput( 'instrIndex', @@ -75,7 +100,7 @@ class ExecutionUnit extends Module { ); if (csrRead != null) { - csrRead = csrRead!.clone() + this.csrRead = csrRead!.clone() ..connectIO( this, csrRead!, @@ -83,10 +108,13 @@ class ExecutionUnit extends Module { inputTags: {DataPortGroup.data, DataPortGroup.integrity}, uniquify: (og) => 'csrRead_$og', ); + csrRead = this.csrRead; + } else { + this.csrRead = null; } if (csrWrite != null) { - csrWrite = csrWrite!.clone() + this.csrWrite = csrWrite!.clone() ..connectIO( this, csrWrite!, @@ -94,6 +122,9 @@ class ExecutionUnit extends Module { inputTags: {DataPortGroup.integrity}, uniquify: (og) => 'csrWrite_$og', ); + csrWrite = this.csrWrite; + } else { + this.csrWrite = null; } memRead = memRead.clone() @@ -138,14 +169,36 @@ class ExecutionUnit extends Module { uniquify: (og) => 'rdWrite_$og', ); + if (microcodeRead != null) { + microcodeRead = microcodeRead!.clone() + ..connectIO( + this, + microcodeRead!, + outputTags: {DataPortGroup.control}, + inputTags: {DataPortGroup.data, DataPortGroup.integrity}, + uniquify: (og) => 'microcodeRead_$og', + ); + } + if (mideleg != null) - mideleg = addInput('mideleg', mideleg, width: mxlen.size); + this.mideleg = addInput('mideleg', mideleg, width: mxlen.size); + else + this.mideleg = null; if (medeleg != null) - medeleg = addInput('medeleg', medeleg, width: mxlen.size); - if (mtvec != null) mtvec = addInput('mtvec', mtvec, width: mxlen.size); - if (stvec != null) stvec = addInput('stvec', stvec, width: mxlen.size); + this.medeleg = addInput('medeleg', medeleg, width: mxlen.size); + else + this.medeleg = null; + if (mtvec != null) + this.mtvec = addInput('mtvec', mtvec, width: mxlen.size); + else + this.mtvec = null; + if (stvec != null) + this.stvec = addInput('stvec', stvec, width: mxlen.size); + else + this.stvec = null; addOutput('done'); + addOutput('valid'); addOutput('nextSp', width: mxlen.size); addOutput('nextPc', width: mxlen.size); addOutput('nextMode', width: 3); @@ -154,6 +207,7 @@ class ExecutionUnit extends Module { addOutput('trapTval', width: mxlen.size); addOutput('fence'); addOutput('interruptHold'); + addOutput('counter', width: counterWidth); final opIndices = microcode.opIndices; @@ -169,176 +223,6 @@ class ExecutionUnit extends Module { final rd = Logic(name: 'rdState', width: mxlen.size); final imm = Logic(name: 'immState', width: mxlen.size); - Logic readSource(MicroOpSource source) { - switch (source) { - case MicroOpSource.imm: - return imm; - case MicroOpSource.alu: - return alu; - case MicroOpSource.rs1: - return rs1; - case MicroOpSource.rs2: - return rs2; - case MicroOpSource.rd: - return rd; - case MicroOpSource.pc: - return nextPc; - default: - throw 'Invalid source $source'; - } - } - - Logic readField(MicroOpField field, {bool register = true}) { - switch (field) { - case MicroOpField.rd: - return (register ? rd : fields['rd']!).zeroExtend(mxlen.size); - case MicroOpField.rs1: - return (register ? rs1 : fields['rs1']!).zeroExtend(mxlen.size); - case MicroOpField.rs2: - return (register ? rs2 : fields['rs2']!).zeroExtend(mxlen.size); - case MicroOpField.imm: - return register ? imm : fields['imm']!; - case MicroOpField.pc: - return nextPc; - case MicroOpField.sp: - return nextSp; - default: - throw 'Invalid field $field'; - } - } - - Conditional writeField(MicroOpField field, Logic value) { - switch (field) { - case MicroOpField.rd: - return rd < value.zeroExtend(mxlen.size); - case MicroOpField.rs1: - return rs1 < value.zeroExtend(mxlen.size); - case MicroOpField.rs2: - return rs2 < value.zeroExtend(mxlen.size); - case MicroOpField.imm: - return imm < value.zeroExtend(mxlen.size); - case MicroOpField.sp: - return nextSp < value.zeroExtend(mxlen.size); - default: - throw 'Invalid field $field'; - } - } - - Conditional clearField(MicroOpField field) => - writeField(field, fields[field.name]!.zeroExtend(mxlen.size)); - - Logic compareCurrentMode(PrivilegeMode target) => - currentMode.eq(Const(target.id, width: 3)); - - Logic selectTrapTargetMode( - Logic trapInterrupt, - Logic causeCode, - Logic mode, - Logic mideleg, - Logic medeleg, - ) { - final machine = Const(PrivilegeMode.machine.id, width: 3); - if (csrRead == null || csrWrite == null) return machine; - - final supervisor = Const(PrivilegeMode.supervisor.id, width: 3); - - final isMachine = mode.eq(machine); - final noSup = ~Const(hasSupervisor ? 1 : 0); - - final delegatedInterrupt = mideleg[causeCode]; - final delegatedException = medeleg[causeCode]; - - final goesToSupervisor = mux( - trapInterrupt, - delegatedInterrupt, - delegatedException, - ); - - final notMachineAndHasSup = ~isMachine & Const(hasSupervisor ? 1 : 0); - - return mux( - notMachineAndHasSup, - mux(goesToSupervisor, supervisor, machine), - machine, - ); - } - - Logic encodeCause(Logic trapInterrupt, Logic causeCode) => - (trapInterrupt.zeroExtend(mxlen.size) << (mxlen.size - 1)) | - causeCode.zeroExtend(mxlen.size); - - Logic computeTrapVectorPc( - Logic tvec, - Logic causeCode, - Logic trapInterrupt, - ) { - final base = (tvec & Const(~0x3, width: mxlen.size)).named('trapBase'); - final mode = tvec.slice(1, 0).named('trapMode'); - - final isVectored = mode.eq(Const(1, width: 2)).named('isVectored'); - - final vecOffset = (causeCode << 2) - .zeroExtend(mxlen.size) - .named('tvecOffset'); - - return mux( - isVectored & trapInterrupt, - base + vecOffset, - base, - ).named('tvecPc'); - } - - List rawTrap( - Logic trapInterrupt, - Logic causeCode, [ - Logic? tval, - ]) { - if (csrRead == null || csrWrite == null) { - return [ - trapCause < encodeCause(trapInterrupt, causeCode).slice(5, 0), - trapTval < (tval ?? Const(0, width: mxlen.size)), - output('trap') < 1, - done < 1, - ]; - } - - final tvec = Logic(name: 'tvec', width: mxlen.size); - - final newMode = selectTrapTargetMode( - trapInterrupt, - causeCode, - currentMode, - mideleg!, - medeleg!, - ); - - return [ - nextMode < newMode, - trapCause < encodeCause(trapInterrupt, causeCode).slice(5, 0), - trapTval < (tval ?? Const(0, width: mxlen.size)), - - tvec < - ((stvec != null) - ? mux( - newMode.eq(Const(PrivilegeMode.machine.id, width: 3)), - mtvec!, - stvec!, - ) - : mtvec!), - - nextPc < computeTrapVectorPc(tvec, causeCode, trapInterrupt), - - output('trap') < 1, - done < 1, - ]; - } - - List trap(Trap t, [Logic? tval]) { - final trapInterrupt = Const(t.interrupt ? 1 : 0); - final causeCode = Const(t.mcauseCode, width: 6); - return rawTrap(trapInterrupt, causeCode, tval); - } - Sequential(clk, [ If( reset, @@ -358,498 +242,62 @@ class ExecutionUnit extends Module { memWrite.en < 0, memWrite.addr < 0, memWrite.data < 0, - if (csrRead != null) ...[csrRead.en < 0, csrRead.addr < 0], - if (csrWrite != null) ...[ - csrWrite.en < 0, - csrWrite.addr < 0, - csrWrite.data < 0, + if (microcodeRead != null) ...[ + microcodeRead.en < 0, + microcodeRead.addr < 0, + ], + if (this.csrRead != null) ...[ + this.csrRead!.en < 0, + this.csrRead!.addr < 0, + ], + if (this.csrWrite != null) ...[ + this.csrWrite!.en < 0, + this.csrWrite!.addr < 0, + this.csrWrite!.data < 0, ], fence < 0, interruptHold < 0, nextPc < currentPc, nextSp < currentSp, + counter < 0, ], orElse: [ If( enable, then: [ - Case( - instrIndex, - microcode.execLookup.entries.map((entry) { - final op = entry.value; - final steps = []; - - for (final mop in op.indexedMicrocode.values) { - final i = steps.length + 1; - - if (mop is ReadRegisterMicroOp) { - final addr = - (readField(mop.source) + - Const(mop.offset, width: mxlen.size)) - .slice(4, 0); - final port = mop.source == MicroOpSource.rs2 - ? rs2Read - : rs1Read; - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - addr.eq(Const(Register.x2.value, width: 5)), - then: [ - writeField(mop.source, currentSp), - mopStep < mopStep + 2, - ], - orElse: [ - port.addr < addr, - port.en < 1, - mopStep < mopStep + 1, - ], - ), - ]), - ); - - steps.add( - CaseItem(Const(i + 1, width: maxLen.bitLength), [ - writeField( - mop.source, - port.data + - Const(mop.valueOffset, width: mxlen.size), - ), - If( - port.done & port.valid, - then: [mopStep < mopStep + 1], - ), - ]), - ); - } else if (mop is WriteRegisterMicroOp) { - final addr = - (readField(mop.field) + - Const(mop.offset, width: mxlen.size)) - .slice(4, 0); - - final value = - (readSource(mop.source) + - Const(mop.valueOffset, width: mxlen.size)); - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - addr.eq(Const(Register.x2.value, width: 5)), - then: [nextSp < value], - ), - rdWrite.addr < addr, - rdWrite.data < value, - rdWrite.en < addr.gt(0), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is AluMicroOp) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - alu < - (switch (mop.alu) { - MicroOpAluFunct.add => - readField(mop.a) + readField(mop.b), - MicroOpAluFunct.sub => - readField(mop.a) - readField(mop.b), - MicroOpAluFunct.and => - readField(mop.a) & readField(mop.b), - MicroOpAluFunct.or => - readField(mop.a) | readField(mop.b), - MicroOpAluFunct.xor => - readField(mop.a) ^ readField(mop.b), - MicroOpAluFunct.sll => - readField(mop.a) << readField(mop.b), - MicroOpAluFunct.srl => - readField(mop.a) >> readField(mop.b), - MicroOpAluFunct.sra => - readField(mop.a) >> readField(mop.b), - MicroOpAluFunct.slt => readField( - mop.a, - ).lte(readField(mop.b)).zeroExtend(mxlen.size), - MicroOpAluFunct.sltu => - (readField(mop.a) - - readField(mop.b))[mxlen.size - 1] - .zeroExtend(mxlen.size), - MicroOpAluFunct.masked => - readField(mop.a) & ~readField(mop.b), - MicroOpAluFunct.mul => - readField(mop.a) * readField(mop.b), - MicroOpAluFunct.mulw => - readField(mop.a) * readField(mop.b), - MicroOpAluFunct.mulh => - readField(mop.a) * readField(mop.b), - MicroOpAluFunct.mulhsu => - readField(mop.a) * readField(mop.b), - MicroOpAluFunct.mulhu => - readField(mop.a) * readField(mop.b), - MicroOpAluFunct.div => - readField(mop.a) / readField(mop.b), - MicroOpAluFunct.divu => - readField(mop.a) / readField(mop.b), - MicroOpAluFunct.divuw => - readField(mop.a) / readField(mop.b), - MicroOpAluFunct.divw => - readField(mop.a) / readField(mop.b), - MicroOpAluFunct.rem => - readField(mop.a) % readField(mop.b), - MicroOpAluFunct.remu => - readField(mop.a) % readField(mop.b), - MicroOpAluFunct.remuw => - readField(mop.a) % readField(mop.b), - MicroOpAluFunct.remw => - readField(mop.a) % readField(mop.b), - _ => throw 'Invalid ALU function ${mop.alu}', - }).named( - 'alu_${op.mnemonic}_${mop.alu.name}_${mop.a.name}_${mop.b.name}', - ), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is UpdatePCMicroOp) { - Logic value = Const(mop.offset, width: mxlen.size); - if (mop.offsetField != null) - value = readField(mop.offsetField!); - if (mop.offsetSource != null) - value = readSource(mop.offsetSource!); - if (mop.align) value &= ~Const(1, width: mxlen.size); - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - nextPc < (mop.absolute ? value : (currentPc + value)), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is MemLoadMicroOp) { - final base = readField(mop.base); - final addr = base + imm; - - final unaligned = - (addr & Const(mop.size.bytes - 1, width: mxlen.size)) - .neq(0); - - final raw = memRead.data.slice(mop.size.bits - 1, 0); - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - unaligned, - then: trap(Trap.misalignedLoad, addr), - orElse: [ - memRead.en < 1, - memRead.addr < addr, - If( - memRead.done & memRead.valid, - then: [ - writeField( - mop.dest, - mop.unsigned - ? raw.zeroExtend(mxlen.size) - : raw.signExtend(mxlen.size), - ), - mopStep < mopStep + 1, - ], - ), - If( - memRead.done & ~memRead.valid, - then: trap(Trap.loadAccess, addr), - ), - ], - ), - ]), - ); - } else if (mop is MemStoreMicroOp) { - final base = readField(mop.base); - final value = readField(mop.src); - final addr = base + imm; - - final unaligned = - (addr & Const(mop.size.bytes - 1, width: mxlen.size)) - .neq(0); - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - unaligned, - then: trap(Trap.misalignedStore, addr), - orElse: [ - memWrite.en < 1, - memWrite.addr < addr, - memWrite.data < - [ - Const(mop.size.bits, width: 7), - value, - ].swizzle(), - If( - memWrite.done & memWrite.valid, - then: [memWrite.en < 0, mopStep < mopStep + 1], - ), - If( - memWrite.done & ~memWrite.valid, - then: [ - memWrite.en < 0, - ...trap(Trap.storeAccess, addr), - ], - ), - ], - ), - ]), - ); - } else if (mop is TrapMicroOp) { - final kindMachine = mop.kindMachine; - final kindSupervisor = mop.kindSupervisor ?? kindMachine; - final kindUser = mop.kindUser ?? kindSupervisor; - - Logic computeKind( - PrivilegeMode expectedMode, - Logic a, - Logic b, [ - Logic? fallback, - ]) { - final value = a == b - ? a - : mux( - currentMode.eq( - Const(expectedMode.id, width: 3), - ), - a, - b, - ); - return switch (expectedMode) { - PrivilegeMode.machine => value, - PrivilegeMode.supervisor => - hasSupervisor ? value : (fallback ?? b), - PrivilegeMode.user => - hasUser ? value : (fallback ?? b), - }; - } - - steps.add( - CaseItem( - Const(i, width: maxLen.bitLength), - rawTrap( - computeKind( - PrivilegeMode.machine, - Const(kindMachine.interrupt), - computeKind( - PrivilegeMode.supervisor, - Const(kindSupervisor.interrupt), - computeKind( - PrivilegeMode.user, - Const(kindUser.interrupt), - Const(kindMachine.interrupt), - ), - Const(kindMachine.interrupt), - ), - ), - computeKind( - PrivilegeMode.machine, - Const(kindMachine.mcauseCode, width: 6), - computeKind( - PrivilegeMode.supervisor, - Const(kindSupervisor.mcauseCode, width: 6), - computeKind( - PrivilegeMode.user, - Const(kindUser.mcauseCode, width: 6), - Const(kindMachine.mcauseCode, width: 6), - ), - Const(kindMachine.mcauseCode, width: 6), - ), - ), - ), - ), - ); - } else if (mop is BranchIfMicroOp) { - final target = readSource(mop.target); - - final value = mop.offsetField != null - ? readField(mop.offsetField!) - : Const(mop.offset, width: mxlen.size); - - final condition = switch (mop.condition) { - MicroOpCondition.eq => target.eq(0), - MicroOpCondition.ne => target.neq(0), - MicroOpCondition.lt => target.lt(0), - MicroOpCondition.gt => target.gt(0), - MicroOpCondition.ge => target.gte(0), - MicroOpCondition.le => target.lte(0), - }; - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - condition, - then: [nextPc < value, done < 1], - orElse: [mopStep < mopStep + 1], - ), - ]), - ); - } else if (mop is WriteLinkRegisterMicroOp) { - final value = - nextPc + Const(mop.pcOffset, width: mxlen.size); - - Logic reg = Const(Register.x0.value, width: 5); - if (mop.link.reg != null) { - reg = Const(mop.link.reg!.value, width: 5); - } else if (mop.link.source != null) { - reg = readSource(mop.link.source!); - } - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - reg.neq(Register.x0.value), - then: [ - rdWrite.addr < reg.slice(4, 0), - rdWrite.data < value, - rdWrite.en < 1, - ], - ), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is FenceMicroOp) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - rs1Read.en < 0, - rs2Read.en < 0, - if (csrRead != null) csrRead.en < 0, - if (csrWrite != null) csrWrite.en < 0, - memRead.en < 0, - memWrite.en < 0, - rdWrite.en < 0, - fence < 1, - mopStep < mopStep + 1, - ]), - ); - } else if (mop is ValidateFieldMicroOp) { - final field = readField(mop.field); - final value = Const(mop.value, width: mxlen.size); - - final condition = switch (mop.condition) { - MicroOpCondition.eq => field.eq(value), - MicroOpCondition.ne => field.neq(value), - MicroOpCondition.lt => field.lt(value), - MicroOpCondition.gt => field.gt(value), - MicroOpCondition.ge => field.gte(value), - MicroOpCondition.le => field.lte(value), - _ => throw 'Invalid condition: ${mop.condition}', - }; - - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - condition, - then: [mopStep < mopStep + 1], - orElse: trap(Trap.illegal), - ), - ]), - ); - } else if (mop is ModifyLatchMicroOp) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - if (mop.replace) - writeField(mop.field, readSource(mop.source)) - else - clearField(mop.field), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is SetFieldMicroOp) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - writeField( - mop.field, - Const(mop.value, width: mxlen.size), - ), - mopStep < mopStep + 1, - ]), - ); - } else if (mop is InterruptHoldMicroOp) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - interruptHold < 1, - mopStep < mopStep + 1, - ]), - ); - } else if (mop is ReadCsrMicroOp && csrRead != null) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - currentMode.eq( - Const(PrivilegeMode.user.id, width: 3), - ), - then: trap(Trap.illegal), - orElse: [ - csrRead.en < 1, - csrRead.addr < readField(mop.source).slice(11, 0), - If( - csrRead.done & csrRead.valid, - then: [ - writeField(mop.source, csrRead.data), - mopStep < mopStep + 1, - ], - ), - If( - csrRead.done & ~csrRead.valid, - then: trap(Trap.illegal), - ), - ], - ), - ]), - ); - } else if (mop is WriteCsrMicroOp && csrWrite != null) { - steps.add( - CaseItem(Const(i, width: maxLen.bitLength), [ - If( - currentMode.eq( - Const(PrivilegeMode.user.id, width: 3), - ), - then: trap(Trap.illegal), - orElse: [ - csrWrite.en < 1, - csrWrite.addr < readField(mop.field).slice(11, 0), - csrWrite.data < readSource(mop.source), - If( - csrWrite.done & csrWrite.valid, - then: [mopStep < mopStep + 1], - ), - If( - csrWrite.done & ~csrWrite.valid, - then: trap(Trap.illegal), - ), - ], - ), - ]), - ); - } else if (mop is TlbFenceMicroOp) { - // TODO: once MMU has a TLB - } else if (mop is TlbInvalidateMicroOp) { - // TODO: once MMU has a TLB - } else { - print(mop); - } - } - - return CaseItem(Const(entry.key, width: instrIndex.width), [ - Case(mopStep, [ - CaseItem(Const(0, width: maxLen.bitLength), [ - alu < 0, - fence < 0, - rs1 < fields['rs1']!.zeroExtend(mxlen.size), - rs2 < fields['rs2']!.zeroExtend(mxlen.size), - rd < fields['rd']!.zeroExtend(mxlen.size), - imm < fields['imm']!.zeroExtend(mxlen.size), - mopStep < 1, - ]), - ...steps, - CaseItem( - Const(steps.length + 1, width: maxLen.bitLength), - [done < 1], - ), - ]), - ]); - }).toList(), - ), + counter < (counter + 1), + ...(microcodeRead != null + ? cycleMicrocode( + instrIndex, + mopStep, + microcodeRead!, + alu: alu, + rs1: rs1, + rs2: rs2, + rd: rd, + imm: imm, + fields: fields, + memRead: memRead, + memWrite: memWrite, + rs1Read: rs1Read, + rs2Read: rs2Read, + rdWrite: rdWrite, + ) + : cycle( + instrIndex, + mopStep, + alu: alu, + rs1: rs1, + rs2: rs2, + rd: rd, + imm: imm, + fields: fields, + memRead: memRead, + memWrite: memWrite, + rs1Read: rs1Read, + rs2Read: rs2Read, + rdWrite: rdWrite, + )), ], orElse: [ alu < 0, @@ -867,18 +315,2170 @@ class ExecutionUnit extends Module { memWrite.en < 0, memWrite.addr < 0, memWrite.data < 0, - if (csrRead != null) ...[csrRead.en < 0, csrRead.addr < 0], - if (csrWrite != null) ...[ - csrWrite.en < 0, - csrWrite.addr < 0, - csrWrite.data < 0, + if (microcodeRead != null) ...[ + microcodeRead.en < 0, + microcodeRead.addr < 0, + ], + if (this.csrRead != null) ...[ + this.csrRead!.en < 0, + this.csrRead!.addr < 0, + ], + if (this.csrWrite != null) ...[ + this.csrWrite!.en < 0, + this.csrWrite!.addr < 0, + this.csrWrite!.data < 0, ], fence < 0, interruptHold < 0, + nextPc < currentPc, + nextSp < currentSp, ], ), ], ), - ], reset: reset); + ]); + } + + List cycle( + Logic instrIndex, + Logic mopStep, { + required Logic alu, + required Logic rs1, + required Logic rs2, + required Logic rd, + required Logic imm, + required Map fields, + required DataPortInterface memRead, + required DataPortInterface memWrite, + required DataPortInterface rs1Read, + required DataPortInterface rs2Read, + required DataPortInterface rdWrite, + }) => []; + + List cycleMicrocode( + Logic instrIndex, + Logic mopStep, + DataPortInterface microcodeRead, { + required Logic alu, + required Logic rs1, + required Logic rs2, + required Logic rd, + required Logic imm, + required Map fields, + required DataPortInterface memRead, + required DataPortInterface memWrite, + required DataPortInterface rs1Read, + required DataPortInterface rs2Read, + required DataPortInterface rdWrite, + }) => []; + + Logic compareCurrentMode(PrivilegeMode target) => + currentMode.eq(Const(target.id, width: 3)); + + Logic selectTrapTargetMode( + Logic trapInterrupt, + Logic causeCode, + Logic mode, + Logic? mideleg, + Logic? medeleg, { + String? suffix, + }) { + suffix ??= ''; + + final machine = Const(PrivilegeMode.machine.id, width: 3); + if (csrRead == null || csrWrite == null) return machine; + + final supervisor = Const(PrivilegeMode.supervisor.id, width: 3); + + final isMachine = mode.eq(machine); + final noSup = ~Const(hasSupervisor ? 1 : 0); + + final delegatedInterrupt = mideleg == null ? Const(0) : mideleg[causeCode]; + final delegatedException = medeleg == null ? Const(0) : medeleg[causeCode]; + + final goesToSupervisor = mux( + trapInterrupt, + delegatedInterrupt, + delegatedException, + ); + + final notMachineAndHasSup = ~isMachine & Const(hasSupervisor ? 1 : 0); + + return mux( + notMachineAndHasSup, + mux(goesToSupervisor, supervisor, machine), + machine, + ); + } + + Logic encodeCause(Logic trapInterrupt, Logic causeCode) => + (trapInterrupt.zeroExtend(mxlen.size) << (mxlen.size - 1)) | + causeCode.zeroExtend(mxlen.size); + + Logic computeTrapVectorPc( + Logic tvec, + Logic causeCode, + Logic trapInterrupt, { + String? suffix, + }) { + suffix ??= ''; + final base = (tvec & Const(~0x3, width: mxlen.size)).named( + 'trapBase$suffix', + ); + final mode = tvec.slice(1, 0).named('trapMode$suffix'); + + final isVectored = mode.eq(Const(1, width: 2)).named('isVectored$suffix'); + + final vecOffset = (causeCode << 2) + .zeroExtend(mxlen.size) + .named('tvecOffset$suffix'); + + return mux( + isVectored & trapInterrupt, + base + vecOffset, + base, + ).named('tvecPc$suffix'); + } + + List rawTrap( + Logic trapInterrupt, + Logic causeCode, [ + Logic? tval, + String? suffix, + ]) { + suffix ??= ''; + + if (csrRead == null || csrWrite == null) { + return [ + trapCause < encodeCause(trapInterrupt, causeCode).slice(5, 0), + trapTval < (tval ?? Const(0, width: mxlen.size)), + output('trap') < 1, + done < 1, + valid < 1, + ]; + } + + final tvec = Logic(name: 'tvec$suffix', width: mxlen.size); + + final newMode = selectTrapTargetMode( + trapInterrupt, + causeCode, + currentMode, + mideleg, + medeleg, + suffix: suffix, + ); + + return [ + nextMode < newMode, + trapCause < + encodeCause( + trapInterrupt, + causeCode, + ).slice(5, 0).named('cause$suffix'), + trapTval < (tval ?? Const(0, width: mxlen.size)), + + tvec < + ((stvec != null) + ? mux( + newMode.eq(Const(PrivilegeMode.machine.id, width: 3)), + mtvec ?? Const(0, width: mxlen.size), + stvec ?? Const(0, width: mxlen.size), + ) + : (mtvec ?? Const(0, width: mxlen.size))), + + nextPc < + computeTrapVectorPc( + ((stvec != null) + ? mux( + newMode.eq(Const(PrivilegeMode.machine.id, width: 3)), + mtvec ?? Const(0, width: mxlen.size), + stvec ?? Const(0, width: mxlen.size), + ) + : (mtvec ?? Const(0, width: mxlen.size))), + causeCode, + trapInterrupt, + suffix: suffix, + ), + + output('trap') < 1, + done < 1, + valid < 1, + ]; + } + + List doTrap(Trap t, [Logic? tval, String? suffix]) { + final trapInterrupt = Const(t.interrupt ? 1 : 0); + final causeCode = Const(t.mcauseCode, width: 6); + return rawTrap(trapInterrupt, causeCode, tval, suffix); + } +} + +class DynamicExecutionUnit extends ExecutionUnit { + DynamicExecutionUnit( + Logic clk, + Logic reset, + Logic enable, + Logic currentSp, + Logic currentPc, + Logic currentMode, + Logic instrIndex, + Map instrTypeMap, + Map fields, + DataPortInterface? csrRead, + DataPortInterface? csrWrite, + DataPortInterface memRead, + DataPortInterface memWrite, + DataPortInterface rs1Read, + DataPortInterface rs2Read, + DataPortInterface rdWrite, + DataPortInterface microcodeRead, { + bool hasSupervisor = false, + bool hasUser = false, + required Microcode microcode, + required Mxlen mxlen, + Logic? mideleg, + Logic? medeleg, + Logic? mtvec, + Logic? stvec, + int counterWidth = 32, + List staticInstructions = const [], + String name = 'river_dynamic_execution_unit', + }) : super( + clk, + reset, + enable, + currentSp, + currentPc, + currentMode, + instrIndex, + instrTypeMap, + fields, + csrRead, + csrWrite, + memRead, + memWrite, + rs1Read, + rs2Read, + rdWrite, + microcodeRead: microcodeRead, + hasSupervisor: hasSupervisor, + hasUser: hasUser, + microcode: microcode, + mxlen: mxlen, + mideleg: mideleg, + medeleg: medeleg, + mtvec: mtvec, + stvec: stvec, + counterWidth: counterWidth, + staticInstructions: staticInstructions, + name: name, + ); + + @override + List cycleMicrocode( + Logic instrIndex, + Logic mopStep, + DataPortInterface microcodeRead, { + required Logic alu, + required Logic rs1, + required Logic rs2, + required Logic rd, + required Logic imm, + required Map fields, + required DataPortInterface memRead, + required DataPortInterface memWrite, + required DataPortInterface rs1Read, + required DataPortInterface rs2Read, + required DataPortInterface rdWrite, + }) { + final csrRead = this.csrRead; + final csrWrite = this.csrWrite; + + final mopCount = Logic(name: 'mopCount', width: mopStep.width); + + final mopTable = Map.fromEntries( + kMicroOpTable + .where((mop) { + if (mop.funct == ReadCsrMicroOp.funct && csrRead == null) + return false; + if (mop.funct == WriteCsrMicroOp.funct && csrWrite == null) + return false; + return true; + }) + .map((mop) => MapEntry(Microcode.mopType(mop), mop)), + ); + + final mop = mopTable.map( + (k, mop) => MapEntry( + k, + Map.fromEntries( + mop.struct(mxlen).mapping.entries.map((entry) { + final fieldName = entry.key; + final range = entry.value; + final value = microcodeRead.data + .getRange(range.start, range.end + 1) + .named('mop${k}_$fieldName'); + return MapEntry(fieldName, value); + }), + ), + ), + ); + + final funct = microcodeRead.data + .slice(MicroOp.functRange.end, MicroOp.functRange.start) + .named('mopFunct'); + + Logic readSource(Logic source) => mux( + source.eq(Const(MicroOpSource.imm.value, width: MicroOpSource.width)), + imm, + mux( + source.eq(Const(MicroOpSource.alu.value, width: MicroOpSource.width)), + alu, + mux( + source.eq(Const(MicroOpSource.rs1.value, width: MicroOpSource.width)), + rs1, + mux( + source.eq( + Const(MicroOpSource.rs2.value, width: MicroOpSource.width), + ), + rs2, + mux( + source.eq( + Const(MicroOpSource.rd.value, width: MicroOpSource.width), + ), + rd, + nextPc, + ), + ), + ), + ), + ); + + Logic readField(Logic field, {bool register = true}) => mux( + field.eq(Const(MicroOpField.rd.value, width: MicroOpField.width)), + (register ? rd : fields['rd']!).zeroExtend(mxlen.size), + mux( + field.eq(Const(MicroOpField.rs1.value, width: MicroOpField.width)), + (register ? rs1 : fields['rs1']!).zeroExtend(mxlen.size), + mux( + field.eq(Const(MicroOpField.rs2.value, width: MicroOpField.width)), + (register ? rs2 : fields['rs2']!).zeroExtend(mxlen.size), + mux( + field.eq(Const(MicroOpField.imm.value, width: MicroOpField.width)), + register ? imm : fields['imm']!, + mux( + field.eq(Const(MicroOpField.pc.value, width: MicroOpField.width)), + nextPc, + nextSp, + ), + ), + ), + ), + ); + + Conditional writeField(Logic field, Logic value) => Case( + field, + [ + CaseItem(Const(MicroOpField.rd.value, width: MicroOpField.width), [ + rd < value.zeroExtend(mxlen.size), + ]), + CaseItem(Const(MicroOpField.rs1.value, width: MicroOpField.width), [ + rs1 < value.zeroExtend(mxlen.size), + ]), + CaseItem(Const(MicroOpField.rs2.value, width: MicroOpField.width), [ + rs2 < value.zeroExtend(mxlen.size), + ]), + CaseItem(Const(MicroOpField.imm.value, width: MicroOpField.width), [ + imm < value.zeroExtend(mxlen.size), + ]), + CaseItem(Const(MicroOpField.sp.value, width: MicroOpField.width), [ + nextSp < value.zeroExtend(mxlen.size), + ]), + ], + defaultItem: [done < 1, valid < 0], + ); + + Conditional clearField(Logic field) => + writeField(field, readField(field, register: false)); + + return [ + If.block([ + Iff(mopStep.eq(0), [ + microcodeRead.en < 1, + microcodeRead.addr < + (instrIndex.zeroExtend(microcodeRead.addr.width) + + mopStep.zeroExtend(microcodeRead.addr.width)), + done < 0, + valid < 0, + If( + microcodeRead.done & microcodeRead.valid, + then: [ + mopCount < microcodeRead.data.slice(mopCount.width - 1, 0), + alu < 0, + fence < 0, + rs1 < fields['rs1']!.zeroExtend(mxlen.size), + rs2 < fields['rs2']!.zeroExtend(mxlen.size), + rd < fields['rd']!.zeroExtend(mxlen.size), + imm < fields['imm']!.zeroExtend(mxlen.size), + mopStep < 1, + microcodeRead.en < 0, + ], + ), + If( + microcodeRead.done & ~microcodeRead.valid, + then: [done < 1, valid < 0, microcodeRead.en < 0], + ), + ]), + Iff(rs1Read.en, [ + Case(funct, [ + CaseItem(Const(ReadRegisterMicroOp.funct, width: funct.width), [ + If( + rs1Read.done & rs1Read.valid, + then: [ + writeField( + mop['ReadRegister']!['source']!, + rs1Read.data + mop['ReadRegister']!['valueOffset']!, + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + rs1Read.en < 0, + ], + ), + ]), + ]), + ]), + Iff(rs2Read.en, [ + Case(funct, [ + CaseItem(Const(ReadRegisterMicroOp.funct, width: funct.width), [ + If( + rs2Read.done & rs2Read.valid, + then: [ + writeField( + mop['ReadRegister']!['source']!, + rs2Read.data + mop['ReadRegister']!['valueOffset']!, + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + rs2Read.en < 0, + ], + ), + ]), + ]), + ]), + Iff(rdWrite.en, [ + Case(funct, [ + CaseItem(Const(WriteRegisterMicroOp.funct, width: funct.width), [ + If( + rdWrite.done & rdWrite.valid, + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + rdWrite.en < 0, + ], + ), + ]), + ]), + ]), + Iff(memRead.en, [ + Case( + funct, + [ + CaseItem(Const(MemLoadMicroOp.funct, width: funct.width), [ + If( + memRead.done & memRead.valid, + then: [ + Case(mop['MemLoad']!['size']!, [ + for (final size in MicroOpMemSize.values.where( + (s) => s.bytes <= mxlen.width, + )) + CaseItem( + Const(size.value, width: MicroOpMemSize.width), + [ + writeField( + mop['MemLoad']!['dest']!, + mux( + mop['MemLoad']!['unsigned']!, + memRead.data + .slice(size.bits - 1, 0) + .zeroExtend(mxlen.size), + memRead.data + .slice(size.bits - 1, 0) + .signExtend(mxlen.size), + ), + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + memRead.en < 0, + ], + ), + ]), + ], + ), + If( + memRead.done & ~memRead.valid, + then: doTrap( + Trap.loadAccess, + readField(mop['MemLoad']!['base']!) + imm, + ), + ), + ]), + ], + defaultItem: [ + microcodeRead.en < 1, + microcodeRead.addr < + (instrIndex.zeroExtend(microcodeRead.addr.width) + + mopStep.zeroExtend(microcodeRead.addr.width)), + ], + ), + ]), + Iff(memWrite.en, [ + Case(funct, [ + CaseItem(Const(MemStoreMicroOp.funct, width: funct.width), [ + If( + memWrite.done & memWrite.valid, + then: [ + memWrite.en < 0, + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + If( + memWrite.done & ~memWrite.valid, + then: [ + memWrite.en < 0, + ...doTrap( + Trap.storeAccess, + readField(mop['MemStore']!['base']!) + imm, + ), + ], + ), + ]), + ]), + ]), + if (csrRead != null) + Iff(csrRead.en, [ + Case(funct, [ + CaseItem(Const(ReadCsrMicroOp.funct, width: funct.width), [ + If( + csrRead.done & csrRead.valid, + then: [ + writeField(mop['ReadCsr']!['source']!, csrRead.data), + mopStep < mopStep + 1, + microcodeRead.en < 0, + csrRead.en < 0, + ], + ), + If(csrRead.done & ~csrRead.valid, then: doTrap(Trap.illegal)), + ]), + ]), + ]), + if (csrWrite != null) + Iff(csrWrite.en, [ + Case(funct, [ + CaseItem(Const(WriteCsrMicroOp.funct, width: funct.width), [ + If( + csrWrite.done & csrWrite.valid, + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + csrWrite.en < 0, + ], + ), + If(csrWrite.done & ~csrWrite.valid, then: doTrap(Trap.illegal)), + ]), + ]), + ]), + Iff((mopStep - 1).lt(mopCount), [ + If( + microcodeRead.done & microcodeRead.valid, + then: [ + Case( + funct, + [ + CaseItem( + Const(ReadRegisterMicroOp.funct, width: funct.width), + [ + If.block([ + Iff( + (readField( + mop['ReadRegister']!['source']!, + ).zeroExtend(mxlen.size) + + mop['ReadRegister']!['offset']!) + .slice(4, 0) + .eq(Const(Register.x0.value, width: 5)), + [mopStep < mopStep + 1, microcodeRead.en < 0], + ), + Iff( + (readField( + mop['ReadRegister']!['source']!, + ).zeroExtend(mxlen.size) + + mop['ReadRegister']!['offset']!) + .slice(4, 0) + .eq(Const(Register.x2.value, width: 5)), + [ + writeField( + mop['ReadRegister']!['source']!, + currentSp, + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + Else([ + If( + mop['ReadRegister']!['source']!.eq( + Const( + MicroOpSource.rs2.value, + width: MicroOpSource.width, + ), + ), + then: [ + rs2Read.en < 1, + rs2Read.addr < + (readField( + mop['ReadRegister']!['source']!, + ).zeroExtend(mxlen.size) + + mop['ReadRegister']!['offset']!) + .slice(4, 0), + ], + orElse: [ + rs1Read.en < 1, + rs1Read.addr < + (readField( + mop['ReadRegister']!['source']!, + ).zeroExtend(mxlen.size) + + mop['ReadRegister']!['offset']!) + .slice(4, 0), + ], + ), + ]), + ]), + ], + ), + CaseItem( + Const(WriteRegisterMicroOp.funct, width: funct.width), + [ + If( + (readField( + mop['WriteRegister']!['field']!, + ).zeroExtend(mxlen.size) + + mop['WriteRegister']!['offset']!) + .slice(4, 0) + .eq(Const(Register.x0.value, width: 5)), + then: [mopStep < mopStep + 1, microcodeRead.en < 0], + orElse: [ + If( + (readField( + mop['WriteRegister']!['field']!, + ).zeroExtend(mxlen.size) + + mop['WriteRegister']!['offset']!) + .slice(4, 0) + .eq(Const(Register.x2.value, width: 5)), + then: [ + nextSp < + (readSource( + mop['WriteRegister']!['source']!, + ) + + mop['WriteRegister']!['valueOffset']!), + microcodeRead.en < 0, + ], + orElse: [ + rdWrite.en < 1, + rdWrite.addr < + (readField( + mop['WriteRegister']!['field']!, + ).zeroExtend(mxlen.size) + + mop['WriteRegister']!['offset']!) + .slice(4, 0), + rdWrite.data < + (readSource( + mop['WriteRegister']!['source']!, + ) + + mop['WriteRegister']!['valueOffset']!), + ], + ), + ], + ), + ], + ), + CaseItem(Const(AluMicroOp.funct, width: funct.width), [ + Case(mop['Alu']!['alu']!, [ + CaseItem( + Const( + MicroOpAluFunct.add.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) + + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.sub.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) - + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.and.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) & + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.or.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) | + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.xor.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) ^ + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.sll.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) << + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.srl.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) >> + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.sra.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) >> + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.slt.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + readField(mop['Alu']!['a']!) + .lte(readField(mop['Alu']!['b']!)) + .zeroExtend(mxlen.size), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.sltu.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) - + readField(mop['Alu']!['b']!))[mxlen.size - + 1] + .zeroExtend(mxlen.size), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.masked.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) & + ~readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.mul.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) * + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.mulw.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) * + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.mulh.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) * + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.mulhsu.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) * + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.mulhu.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) * + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.div.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) / + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.divu.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) / + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.divuw.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) / + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.divw.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) / + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.rem.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) % + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.remuw.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) % + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem( + Const( + MicroOpAluFunct.remw.value, + width: MicroOpAluFunct.width, + ), + [ + alu < + (readField(mop['Alu']!['a']!) % + readField(mop['Alu']!['b']!)), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ]), + ]), + CaseItem(Const(UpdatePCMicroOp.funct, width: funct.width), [ + nextPc < + (mux( + mop['UpdatePC']!['absolute']!, + Const(0, width: mxlen.size), + currentPc, + ) + + mux( + mop['UpdatePC']!['hasField']!, + readField(mop['UpdatePC']!['offsetField']!), + mux( + mop['UpdatePC']!['hasSource']!, + readSource( + mop['UpdatePC']!['offsetSource']!, + ), + mop['UpdatePC']!['offset']!, + ), + )) & + ~mux( + mop['UpdatePC']!['align']!, + Const(1, width: mxlen.size), + Const(0, width: mxlen.size), + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ]), + CaseItem(Const(MemLoadMicroOp.funct, width: funct.width), [ + Case(mop['MemLoad']!['size']!, [ + for (final size in MicroOpMemSize.values.where( + (s) => s.bytes <= mxlen.width, + )) + CaseItem( + Const(size.value, width: MicroOpMemSize.width), + [ + If( + ((readField(mop['MemLoad']!['base']!) + imm) & + Const(size.bytes - 1, width: mxlen.size)) + .neq(0), + then: doTrap( + Trap.misalignedLoad, + readField(mop['MemLoad']!['base']!) + imm, + ), + orElse: [ + memRead.en < 1, + memRead.addr < + (readField(mop['MemLoad']!['base']!) + imm), + ], + ), + ], + ), + ]), + ]), + CaseItem(Const(MemStoreMicroOp.funct, width: funct.width), [ + Case(mop['MemStore']!['size']!, [ + for (final size in MicroOpMemSize.values.where( + (s) => s.bytes <= mxlen.width, + )) + CaseItem( + Const(size.value, width: MicroOpMemSize.width), + [ + If( + ((readField(mop['MemStore']!['base']!) + imm) & + Const(size.bytes - 1, width: mxlen.size)) + .neq(0), + then: doTrap( + Trap.misalignedStore, + readField(mop['MemStore']!['base']!) + imm, + ), + orElse: [ + memWrite.en < 1, + memWrite.addr < + (readField(mop['MemStore']!['base']!) + + imm), + memWrite.data < + [ + (Const(1, width: 7) << + mop['MemLoad']!['size']!), + readField(mop['MemStore']!['src']!), + ].swizzle(), + ], + ), + ], + ), + ]), + ]), + CaseItem(Const(TrapMicroOp.funct, width: funct.width), [ + Case(currentMode, [ + for (final mode in PrivilegeMode.values) + CaseItem(Const(mode.id, width: 3), [ + Case(mop['Trap']![mode.name]!, [ + for (final trap in Trap.values) + CaseItem( + Const( + trap.index, + width: Trap.values.length.bitLength, + ), + doTrap(trap), + ), + ]), + ]), + ]), + ]), + CaseItem(Const(BranchIfMicroOp.funct, width: funct.width), [ + Case(mop['BranchIf']!['condition']!, [ + CaseItem( + Const( + MicroOpCondition.eq.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).eq(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.ne.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).neq(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.lt.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).lt(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.gt.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).gt(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.ge.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).gte(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.le.value, + width: MicroOpCondition.width, + ), + [ + If( + readSource(mop['BranchIf']!['target']!).lte(0), + then: [ + nextPc < + mux( + mop['BranchIf']!['hasField']!, + readField(mop['BranchIf']!['offsetField']!), + mop['BranchIf']!['offset']!, + ), + done < 1, + valid < 1, + ], + orElse: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + ]), + ]), + CaseItem( + Const(WriteLinkRegisterMicroOp.funct, width: funct.width), + [ + Case(mop['WriteLinkRegister']!['link']!, [ + for (final link in MicroOpLink.values) + CaseItem(Const(link.value, width: MicroOpLink.width), [ + If( + (link.reg != null + ? Const(link.reg!.value, width: 5) + : (link.source != null + ? readSource( + Const( + link.source!.value, + width: MicroOpSource.width, + ), + ).slice(4, 0) + : Const( + Register.x0.value, + width: 5, + ))) + .neq(Register.x0.value), + then: [ + rdWrite.en < 1, + rdWrite.addr < + (link.reg != null + ? Const(link.reg!.value, width: 5) + : (link.source != null + ? readSource( + Const( + link.source!.value, + width: MicroOpSource.width, + ), + ).slice(4, 0) + : Const( + Register.x0.value, + width: 5, + ))), + rdWrite.data < + (nextPc + + mop['WriteLinkRegister']!['pcOffset']!), + ], + ), + ]), + ]), + ], + ), + CaseItem(Const(FenceMicroOp.funct, width: funct.width), [ + rs1Read.en < 0, + rs2Read.en < 0, + if (csrRead != null) csrRead.en < 0, + if (csrWrite != null) csrWrite.en < 0, + memRead.en < 0, + memWrite.en < 0, + rdWrite.en < 0, + fence < 1, + mopStep < mopStep + 1, + microcodeRead.en < 0, + ]), + CaseItem( + Const(ValidateFieldMicroOp.funct, width: funct.width), + [ + Case(mop['ValidateField']!['condition']!, [ + CaseItem( + Const( + MicroOpCondition.eq.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).eq(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.ne.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).neq(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.lt.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).lt(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.gt.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).gt(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.ge.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).gte(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + CaseItem( + Const( + MicroOpCondition.le.value, + width: MicroOpCondition.width, + ), + [ + If( + readField( + mop['ValidateField']!['field']!, + ).lte(mop['ValidateField']!['value']!), + then: [ + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: doTrap(Trap.illegal), + ), + ], + ), + ]), + ], + ), + CaseItem( + Const(ModifyLatchMicroOp.funct, width: funct.width), + [ + If( + mop['ModifyLatch']!['replace']!, + then: [ + writeField( + mop['ModifyLatch']!['field']!, + readSource(mop['ModifyLatch']!['source']!), + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + orElse: [ + clearField(mop['ModifyLatch']!['field']!), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + ], + ), + CaseItem(Const(SetFieldMicroOp.funct, width: funct.width), [ + writeField( + mop['SetField']!['field']!, + mop['SetField']!['value']!, + ), + mopStep < mopStep + 1, + microcodeRead.en < 0, + ]), + CaseItem( + Const(InterruptHoldMicroOp.funct, width: funct.width), + [ + interruptHold < 1, + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + if (csrRead != null) + CaseItem(Const(ReadCsrMicroOp.funct, width: funct.width), [ + If( + currentMode.eq(Const(PrivilegeMode.user.id, width: 3)), + then: doTrap(Trap.illegal), + orElse: [ + csrRead.en < 1, + csrRead.addr < + readField( + mop['ReadCsr']!['source']!, + ).slice(11, 0), + ], + ), + ]), + if (csrWrite != null) + CaseItem(Const(WriteCsrMicroOp.funct, width: funct.width), [ + If( + currentMode.eq(Const(PrivilegeMode.user.id, width: 3)), + then: doTrap(Trap.illegal), + orElse: [ + csrWrite.en < 1, + csrWrite.addr < + readField( + mop['WriteCsr']!['field']!, + ).slice(11, 0), + csrWrite.data < + readSource(mop['WriteCsr']!['source']!), + ], + ), + ]), + CaseItem(Const(TlbFenceMicroOp.funct, width: funct.width), [ + // TODO: once MMU has a TLB + mopStep < mopStep + 1, + microcodeRead.en < 0, + ]), + CaseItem( + Const(TlbInvalidateMicroOp.funct, width: funct.width), + [ + // TODO: once MMU has a TLB + mopStep < mopStep + 1, + microcodeRead.en < 0, + ], + ), + CaseItem(Const(0, width: funct.width), []), + ], + defaultItem: [done < 1, valid < 0], + ), + ], + ), + If( + microcodeRead.en & microcodeRead.done & ~microcodeRead.valid, + then: [done < 1, valid < 0], + ), + If( + ~microcodeRead.en, + then: [ + microcodeRead.en < 1, + microcodeRead.addr < + (instrIndex.zeroExtend(microcodeRead.addr.width) + + mopStep.zeroExtend(microcodeRead.addr.width)), + ], + ), + ]), + Else([done < 1, valid < 1]), + ]), + ]; + } +} + +class StaticExecutionUnit extends ExecutionUnit { + StaticExecutionUnit( + super.clk, + super.reset, + super.enable, + super.currentSp, + super.currentPc, + super.currentMode, + super.instrIndex, + super.instrTypeMap, + super.fields, + super.csrRead, + super.csrWrite, + super.memRead, + super.memWrite, + super.rs1Read, + super.rs2Read, + super.rdWrite, { + super.hasSupervisor = false, + super.hasUser = false, + required super.microcode, + required super.mxlen, + super.mideleg, + super.medeleg, + super.mtvec, + super.stvec, + super.staticInstructions = const [], + super.counterWidth = 32, + super.name = 'river_static_execution_unit', + }); + + @override + List cycle( + Logic instrIndex, + Logic mopStep, { + required Logic alu, + required Logic rs1, + required Logic rs2, + required Logic rd, + required Logic imm, + required Map fields, + required DataPortInterface memRead, + required DataPortInterface memWrite, + required DataPortInterface rs1Read, + required DataPortInterface rs2Read, + required DataPortInterface rdWrite, + }) { + final csrRead = this.csrRead; + final csrWrite = this.csrWrite; + + final maxLen = microcode.microOpSequences.values + .map((s) => s.ops.length * 2) + .fold(0, (a, b) => a > b ? a : b); + + Logic readSource(MicroOpSource source) { + switch (source) { + case MicroOpSource.imm: + return imm; + case MicroOpSource.alu: + return alu; + case MicroOpSource.rs1: + return rs1; + case MicroOpSource.rs2: + return rs2; + case MicroOpSource.rd: + return rd; + case MicroOpSource.pc: + return nextPc; + default: + throw 'Invalid source $source'; + } + } + + Logic readField(MicroOpField field, {bool register = true}) { + switch (field) { + case MicroOpField.rd: + return (register ? rd : fields['rd']!).zeroExtend(mxlen.size); + case MicroOpField.rs1: + return (register ? rs1 : fields['rs1']!).zeroExtend(mxlen.size); + case MicroOpField.rs2: + return (register ? rs2 : fields['rs2']!).zeroExtend(mxlen.size); + case MicroOpField.imm: + return register ? imm : fields['imm']!; + case MicroOpField.pc: + return nextPc; + case MicroOpField.sp: + return nextSp; + default: + throw 'Invalid field $field'; + } + } + + Conditional writeField(MicroOpField field, Logic value) { + switch (field) { + case MicroOpField.rd: + return rd < value.zeroExtend(mxlen.size); + case MicroOpField.rs1: + return rs1 < value.zeroExtend(mxlen.size); + case MicroOpField.rs2: + return rs2 < value.zeroExtend(mxlen.size); + case MicroOpField.imm: + return imm < value.zeroExtend(mxlen.size); + case MicroOpField.sp: + return nextPc < value.zeroExtend(mxlen.size); + case MicroOpField.sp: + return nextSp < value.zeroExtend(mxlen.size); + default: + throw 'Invalid field $field'; + } + } + + Conditional clearField(MicroOpField field) => + writeField(field, fields[field.name]!.zeroExtend(mxlen.size)); + + return [ + Case( + instrIndex, + microcode.execLookup.entries + .where( + (entry) => staticInstructions.isNotEmpty + ? staticInstructions.contains(entry.value.mnemonic) + : true, + ) + .map((entry) { + final op = entry.value; + final steps = []; + + for (final mop in op.indexedMicrocode.values) { + final i = steps.length + 1; + + if (mop is ReadRegisterMicroOp) { + final addr = + (readField(mop.source) + + Const(mop.offset, width: mxlen.size)) + .slice(4, 0); + final port = mop.source == MicroOpSource.rs2 + ? rs2Read + : rs1Read; + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + addr.eq(Const(Register.x2.value, width: 5)), + then: [ + writeField(mop.source, currentSp), + mopStep < mopStep + 2, + ], + orElse: [ + port.addr < addr, + port.en < 1, + mopStep < mopStep + 1, + ], + ), + ]), + ); + + steps.add( + CaseItem(Const(i + 1, width: maxLen.bitLength), [ + writeField( + mop.source, + port.data + Const(mop.valueOffset, width: mxlen.size), + ), + If(port.done & port.valid, then: [mopStep < mopStep + 1]), + ]), + ); + } else if (mop is WriteRegisterMicroOp) { + final addr = + (readField(mop.field) + + Const(mop.offset, width: mxlen.size)) + .slice(4, 0); + + final value = + (readSource(mop.source) + + Const(mop.valueOffset, width: mxlen.size)); + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + addr.eq(Const(Register.x2.value, width: 5)), + then: [nextSp < value], + ), + rdWrite.addr < addr, + rdWrite.data < value, + rdWrite.en < addr.gt(0), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is AluMicroOp) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + alu < + (switch (mop.alu) { + MicroOpAluFunct.add => + readField(mop.a) + readField(mop.b), + MicroOpAluFunct.sub => + readField(mop.a) - readField(mop.b), + MicroOpAluFunct.and => + readField(mop.a) & readField(mop.b), + MicroOpAluFunct.or => + readField(mop.a) | readField(mop.b), + MicroOpAluFunct.xor => + readField(mop.a) ^ readField(mop.b), + MicroOpAluFunct.sll => + readField(mop.a) << readField(mop.b), + MicroOpAluFunct.srl => + readField(mop.a) >> readField(mop.b), + MicroOpAluFunct.sra => + readField(mop.a) >> readField(mop.b), + MicroOpAluFunct.slt => readField( + mop.a, + ).lte(readField(mop.b)).zeroExtend(mxlen.size), + MicroOpAluFunct.sltu => + (readField(mop.a) - readField(mop.b))[mxlen.size - + 1] + .zeroExtend(mxlen.size), + MicroOpAluFunct.masked => + readField(mop.a) & ~readField(mop.b), + MicroOpAluFunct.mul => + readField(mop.a) * readField(mop.b), + MicroOpAluFunct.mulw => + readField(mop.a) * readField(mop.b), + MicroOpAluFunct.mulh => + readField(mop.a) * readField(mop.b), + MicroOpAluFunct.mulhsu => + readField(mop.a) * readField(mop.b), + MicroOpAluFunct.mulhu => + readField(mop.a) * readField(mop.b), + MicroOpAluFunct.div => + readField(mop.a) / readField(mop.b), + MicroOpAluFunct.divu => + readField(mop.a) / readField(mop.b), + MicroOpAluFunct.divuw => + readField(mop.a) / readField(mop.b), + MicroOpAluFunct.divw => + readField(mop.a) / readField(mop.b), + MicroOpAluFunct.rem => + readField(mop.a) % readField(mop.b), + MicroOpAluFunct.remu => + readField(mop.a) % readField(mop.b), + MicroOpAluFunct.remuw => + readField(mop.a) % readField(mop.b), + MicroOpAluFunct.remw => + readField(mop.a) % readField(mop.b), + _ => throw 'Invalid ALU function ${mop.alu}', + }).named( + 'alu_${op.mnemonic}_${mop.alu.name}_${mop.a.name}_${mop.b.name}', + ), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is UpdatePCMicroOp) { + Logic value = Const(mop.offset, width: mxlen.size); + if (mop.offsetField != null) + value = readField(mop.offsetField!); + if (mop.offsetSource != null) + value = readSource(mop.offsetSource!); + if (mop.align) value &= ~Const(1, width: mxlen.size); + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + nextPc < (mop.absolute ? value : (currentPc + value)), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is MemLoadMicroOp) { + final base = readField(mop.base); + final addr = base + imm; + + final unaligned = + (addr & Const(mop.size.bytes - 1, width: mxlen.size)).neq( + 0, + ); + + final raw = memRead.data.slice(mop.size.bits - 1, 0); + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + unaligned, + then: doTrap( + Trap.misalignedLoad, + addr, + '_${op.mnemonic}', + ), + orElse: [ + memRead.en < 1, + memRead.addr < addr, + mopStep < mopStep + 1, + ], + ), + ]), + ); + + steps.add( + CaseItem(Const(i + 1, width: maxLen.bitLength), [ + If( + memRead.en & memRead.done & memRead.valid, + then: [ + writeField( + mop.dest, + mop.unsigned + ? raw.zeroExtend(mxlen.size) + : raw.signExtend(mxlen.size), + ), + mopStep < mopStep + 1, + ], + ), + If( + memRead.en & memRead.done & ~memRead.valid, + then: doTrap(Trap.loadAccess, addr, '_${op.mnemonic}'), + ), + ]), + ); + } else if (mop is MemStoreMicroOp) { + final base = readField(mop.base); + final value = readField(mop.src); + final addr = base + imm; + + final unaligned = + (addr & Const(mop.size.bytes - 1, width: mxlen.size)).neq( + 0, + ); + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + unaligned, + then: doTrap( + Trap.misalignedStore, + addr, + '_${op.mnemonic}', + ), + orElse: [ + memWrite.en < 1, + memWrite.addr < addr, + memWrite.data < + [Const(mop.size.bits, width: 7), value].swizzle(), + If( + memWrite.done & memWrite.valid, + then: [memWrite.en < 0, mopStep < mopStep + 1], + ), + If( + memWrite.done & ~memWrite.valid, + then: [ + memWrite.en < 0, + ...doTrap( + Trap.storeAccess, + addr, + '_${op.mnemonic}', + ), + ], + ), + ], + ), + ]), + ); + } else if (mop is TrapMicroOp) { + final kindMachine = mop.kindMachine; + final kindSupervisor = mop.kindSupervisor ?? kindMachine; + final kindUser = mop.kindUser ?? kindSupervisor; + + Logic computeKind( + PrivilegeMode expectedMode, + Logic a, + Logic b, [ + Logic? fallback, + ]) { + final value = a == b + ? a + : mux( + currentMode.eq(Const(expectedMode.id, width: 3)), + a, + b, + ); + return switch (expectedMode) { + PrivilegeMode.machine => value, + PrivilegeMode.supervisor => + hasSupervisor ? value : (fallback ?? b), + PrivilegeMode.user => hasUser ? value : (fallback ?? b), + }; + } + + steps.add( + CaseItem( + Const(i, width: maxLen.bitLength), + rawTrap( + computeKind( + PrivilegeMode.machine, + Const(kindMachine.interrupt), + computeKind( + PrivilegeMode.supervisor, + Const(kindSupervisor.interrupt), + computeKind( + PrivilegeMode.user, + Const(kindUser.interrupt), + Const(kindMachine.interrupt), + ), + Const(kindMachine.interrupt), + ), + ), + computeKind( + PrivilegeMode.machine, + Const(kindMachine.mcauseCode, width: 6), + computeKind( + PrivilegeMode.supervisor, + Const(kindSupervisor.mcauseCode, width: 6), + computeKind( + PrivilegeMode.user, + Const(kindUser.mcauseCode, width: 6), + Const(kindMachine.mcauseCode, width: 6), + ), + Const(kindMachine.mcauseCode, width: 6), + ), + ), + null, + '_${op.mnemonic}', + ), + ), + ); + } else if (mop is BranchIfMicroOp) { + final target = readSource(mop.target); + + final value = mop.offsetField != null + ? readField(mop.offsetField!) + : Const(mop.offset, width: mxlen.size); + + final condition = switch (mop.condition) { + MicroOpCondition.eq => target.eq(0), + MicroOpCondition.ne => target.neq(0), + MicroOpCondition.lt => target.lt(0), + MicroOpCondition.gt => target.gt(0), + MicroOpCondition.ge => target.gte(0), + MicroOpCondition.le => target.lte(0), + }; + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + condition, + then: [nextPc < value, done < 1, valid < 1], + orElse: [mopStep < mopStep + 1], + ), + ]), + ); + } else if (mop is WriteLinkRegisterMicroOp) { + final value = nextPc + Const(mop.pcOffset, width: mxlen.size); + + Logic reg = Const(Register.x0.value, width: 5); + if (mop.link.reg != null) { + reg = Const(mop.link.reg!.value, width: 5); + } else if (mop.link.source != null) { + reg = readSource(mop.link.source!); + } + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + reg.neq(Register.x0.value), + then: [ + rdWrite.addr < reg.slice(4, 0), + rdWrite.data < value, + rdWrite.en < 1, + ], + ), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is FenceMicroOp) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + rs1Read.en < 0, + rs2Read.en < 0, + if (csrRead != null) csrRead.en < 0, + if (csrWrite != null) csrWrite.en < 0, + memRead.en < 0, + memWrite.en < 0, + rdWrite.en < 0, + fence < 1, + mopStep < mopStep + 1, + ]), + ); + } else if (mop is ValidateFieldMicroOp) { + final field = readField(mop.field); + final value = Const(mop.value, width: mxlen.size); + + final condition = switch (mop.condition) { + MicroOpCondition.eq => field.eq(value), + MicroOpCondition.ne => field.neq(value), + MicroOpCondition.lt => field.lt(value), + MicroOpCondition.gt => field.gt(value), + MicroOpCondition.ge => field.gte(value), + MicroOpCondition.le => field.lte(value), + _ => throw 'Invalid condition: ${mop.condition}', + }; + + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + condition, + then: [mopStep < mopStep + 1], + orElse: doTrap(Trap.illegal, null, '_${op.mnemonic}'), + ), + ]), + ); + } else if (mop is ModifyLatchMicroOp) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + if (mop.replace) + writeField(mop.field, readSource(mop.source)) + else + clearField(mop.field), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is SetFieldMicroOp) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + writeField( + mop.field, + Const(mop.value, width: mxlen.size), + ), + mopStep < mopStep + 1, + ]), + ); + } else if (mop is InterruptHoldMicroOp) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + interruptHold < 1, + mopStep < mopStep + 1, + ]), + ); + } else if (mop is ReadCsrMicroOp && csrRead != null) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + currentMode.eq(Const(PrivilegeMode.user.id, width: 3)), + then: doTrap(Trap.illegal, null, '_${op.mnemonic}'), + orElse: [ + csrRead.en < 1, + csrRead.addr < readField(mop.source).slice(11, 0), + mopStep < mopStep + 1, + ], + ), + ]), + ); + + steps.add( + CaseItem(Const(i + 1, width: maxLen.bitLength), [ + If.block([ + Iff(csrRead.en & csrRead.done & csrRead.valid, [ + writeField(mop.source, csrRead.data), + mopStep < mopStep + 1, + ]), + Iff( + csrRead.en & csrRead.done & ~csrRead.valid, + doTrap(Trap.illegal, null, '_${op.mnemonic}'), + ), + ]), + ]), + ); + } else if (mop is WriteCsrMicroOp && csrWrite != null) { + steps.add( + CaseItem(Const(i, width: maxLen.bitLength), [ + If( + currentMode.eq(Const(PrivilegeMode.user.id, width: 3)), + then: doTrap(Trap.illegal, null, '_${op.mnemonic}'), + orElse: [ + csrWrite.en < 1, + csrWrite.addr < readField(mop.field).slice(11, 0), + csrWrite.data < readSource(mop.source), + mopStep < mopStep + 1, + ], + ), + ]), + ); + + steps.add( + CaseItem(Const(i + 1, width: maxLen.bitLength), [ + If.block([ + Iff(csrWrite.en & csrWrite.done & csrWrite.valid, [ + mopStep < mopStep + 1, + ]), + Iff( + csrWrite.en & csrWrite.done & ~csrWrite.valid, + doTrap(Trap.illegal, null, '_${op.mnemonic}'), + ), + ]), + ]), + ); + } else if (mop is TlbFenceMicroOp) { + // TODO: once MMU has a TLB + } else if (mop is TlbInvalidateMicroOp) { + // TODO: once MMU has a TLB + } else { + print(mop); + } + } + + return CaseItem(Const(entry.key, width: instrIndex.width), [ + Case(mopStep, [ + CaseItem(Const(0, width: maxLen.bitLength), [ + alu < 0, + fence < 0, + rs1 < fields['rs1']!.zeroExtend(mxlen.size), + rs2 < fields['rs2']!.zeroExtend(mxlen.size), + rd < fields['rd']!.zeroExtend(mxlen.size), + imm < fields['imm']!.zeroExtend(mxlen.size), + mopStep < 1, + ]), + ...steps, + CaseItem(Const(steps.length + 1, width: maxLen.bitLength), [ + done < 1, + valid < 1, + ]), + ]), + ]); + }) + .toList(), + defaultItem: [ + alu < 0, + mopStep < 0, + done < 1, + valid < 0, + rs1Read.en < 0, + rs1Read.addr < 0, + rs2Read.en < 0, + rs2Read.addr < 0, + rdWrite.en < 0, + rdWrite.addr < 0, + rdWrite.data < 0, + memRead.en < 0, + memRead.addr < 0, + memWrite.en < 0, + memWrite.addr < 0, + memWrite.data < 0, + ], + ), + ]; } } diff --git a/packages/river_hdl/lib/src/core/pipeline.dart b/packages/river_hdl/lib/src/core/pipeline.dart index a6d7e98..a6c766d 100644 --- a/packages/river_hdl/lib/src/core/pipeline.dart +++ b/packages/river_hdl/lib/src/core/pipeline.dart @@ -20,6 +20,7 @@ class RiverPipeline extends Module { Logic get trapTval => output('trapTval'); Logic get fence => output('fence'); Logic get interruptHold => output('interruptHold'); + Logic get counter => output('counter'); late final FetchUnit fetcher; @@ -38,8 +39,10 @@ class RiverPipeline extends Module { DataPortInterface rs1Read, DataPortInterface rs2Read, DataPortInterface rdWrite, - DataPortInterface? microcodeDecodeRead, { + DataPortInterface? microcodeDecodeRead, + DataPortInterface? microcodeExecRead, { bool useMixedDecoders = false, + bool useMixedExecution = false, bool hasSupervisor = false, bool hasUser = false, bool hasCompressed = false, @@ -49,6 +52,7 @@ class RiverPipeline extends Module { Logic? medeleg, Logic? mtvec, Logic? stvec, + int counterWidth = 32, List staticInstructions = const [], super.name = 'river_pipeline', }) { @@ -139,7 +143,18 @@ class RiverPipeline extends Module { microcodeDecodeRead!, outputTags: {DataPortGroup.control}, inputTags: {DataPortGroup.data, DataPortGroup.integrity}, - uniquify: (og) => 'microcodeRead_$og', + uniquify: (og) => 'microcodeDecodeRead_$og', + ); + } + + if (microcodeExecRead != null) { + microcodeExecRead = microcodeExecRead!.clone() + ..connectIO( + this, + microcodeExecRead!, + outputTags: {DataPortGroup.control}, + inputTags: {DataPortGroup.data, DataPortGroup.integrity}, + uniquify: (og) => 'microcodeExecRead_$og', ); } @@ -160,6 +175,7 @@ class RiverPipeline extends Module { addOutput('trapTval', width: mxlen.size); addOutput('fence'); addOutput('interruptHold'); + addOutput('counter', width: counterWidth); fetcher = FetchUnit( clk, @@ -180,6 +196,7 @@ class RiverPipeline extends Module { microcode: microcode, mxlen: mxlen, staticInstructions: staticInstructions, + counterWidth: counterWidth, ) : StaticInstructionDecoder( clk, @@ -189,6 +206,7 @@ class RiverPipeline extends Module { microcode: microcode, mxlen: mxlen, staticInstructions: staticInstructions, + counterWidth: counterWidth, ); final decoder1 = (useMixedDecoders && microcodeDecodeRead != null) @@ -200,6 +218,7 @@ class RiverPipeline extends Module { microcode: microcode, mxlen: mxlen, staticInstructions: staticInstructions, + counterWidth: counterWidth, ) : null; @@ -214,7 +233,7 @@ class RiverPipeline extends Module { decoder0.done & decoder0.valid, value, decoder1!.instrTypeMap[name]!, - ), + ).named(name), ), ) : decoder0.instrTypeMap; @@ -227,7 +246,7 @@ class RiverPipeline extends Module { decoder0.done & decoder0.valid, value, decoder1!.fields[name]!, - ), + ).named(name), ), ) : decoder0.fields; @@ -244,37 +263,185 @@ class RiverPipeline extends Module { 'readyExecution', ); - final exec = ExecutionUnit( - clk, - reset, - readyExecution, - currentSp, - currentPc, - currentMode, - decodeIndex, - decodeInstrTypeMap, - decodeFields, - csrRead, - csrWrite, - memExecRead, - memWrite, - rs1Read, - rs2Read, - rdWrite, - hasSupervisor: hasSupervisor, - hasUser: hasUser, - microcode: microcode, - mxlen: mxlen, - mideleg: mideleg, - medeleg: medeleg, - mtvec: mtvec, - stvec: stvec, - staticInstructions: staticInstructions, - ); + final memExecRead0 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, mxlen.size) + : memExecRead; + + final memExecRead1 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, mxlen.size) + : null; + + final memWrite0 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(7 + mxlen.size, mxlen.size) + : memWrite; + final memWrite1 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(7 + mxlen.size, mxlen.size) + : null; + + final csrRead0 = + (useMixedExecution && microcodeExecRead != null && csrRead != null) + ? DataPortInterface(mxlen.size, 12) + : csrRead; + final csrWrite0 = + (useMixedExecution && microcodeExecRead != null && csrWrite != null) + ? DataPortInterface(mxlen.size, 12) + : csrWrite; + + final csrRead1 = + (useMixedExecution && microcodeExecRead != null && csrRead != null) + ? DataPortInterface(mxlen.size, 12) + : null; + final csrWrite1 = + (useMixedExecution && microcodeExecRead != null && csrWrite != null) + ? DataPortInterface(mxlen.size, 12) + : null; + + final rs1Read0 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : rs1Read; + final rs1Read1 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : null; + + final rs2Read0 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : rs2Read; + final rs2Read1 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : null; + + final rdWrite0 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : rdWrite; + final rdWrite1 = (useMixedExecution && microcodeExecRead != null) + ? DataPortInterface(mxlen.size, 5) + : null; + + final exec0 = microcodeExecRead != null + ? DynamicExecutionUnit( + clk, + reset, + readyExecution, + currentSp, + currentPc, + currentMode, + decodeIndex, + decodeInstrTypeMap, + decodeFields, + csrRead0, + csrWrite0, + memExecRead0, + memWrite0, + rs1Read0, + rs2Read0, + rdWrite0, + microcodeExecRead, + hasSupervisor: hasSupervisor, + hasUser: hasUser, + microcode: microcode, + mxlen: mxlen, + mideleg: mideleg, + medeleg: medeleg, + mtvec: mtvec, + stvec: stvec, + staticInstructions: staticInstructions, + counterWidth: counterWidth, + ) + : StaticExecutionUnit( + clk, + reset, + readyExecution, + currentSp, + currentPc, + currentMode, + decodeIndex, + decodeInstrTypeMap, + decodeFields, + csrRead0, + csrWrite0, + memExecRead0, + memWrite0, + rs1Read0, + rs2Read0, + rdWrite0, + hasSupervisor: hasSupervisor, + hasUser: hasUser, + microcode: microcode, + mxlen: mxlen, + mideleg: mideleg, + medeleg: medeleg, + mtvec: mtvec, + stvec: stvec, + staticInstructions: staticInstructions, + counterWidth: counterWidth, + ); + + final exec1 = (useMixedExecution && microcodeExecRead != null) + ? StaticExecutionUnit( + clk, + reset, + readyExecution & exec0.done & ~exec0.valid, + currentSp, + currentPc, + currentMode, + decodeIndex, + decodeInstrTypeMap, + decodeFields, + csrRead1, + csrWrite1, + memExecRead1!, + memWrite1!, + rs1Read1!, + rs2Read1!, + rdWrite1!, + hasSupervisor: hasSupervisor, + hasUser: hasUser, + microcode: microcode, + mxlen: mxlen, + mideleg: mideleg, + medeleg: medeleg, + mtvec: mtvec, + stvec: stvec, + staticInstructions: staticInstructions, + counterWidth: counterWidth, + ) + : null; + + final execDone = exec1 != null ? exec0.done | exec1.done : exec0.done; + final execValid = exec1 != null ? exec0.valid | exec1.valid : exec0.valid; + + final execNextSp = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.nextSp, exec1.nextSp) + : exec0.nextSp; + final execNextPc = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.nextPc, exec1.nextPc) + : exec0.nextPc; + final execNextMode = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.nextMode, exec1.nextMode) + : exec0.nextMode; + final execTrap = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.trap, exec1.trap) + : exec0.trap; + final execTrapCause = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.trapCause, exec1.trapCause) + : exec0.trapCause; + final execTrapTval = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.trapTval, exec1.trapTval) + : exec0.trapTval; + final execFence = exec1 != null + ? mux(exec0.done & exec0.valid, exec0.fence, exec1.fence) + : exec0.fence; + final execInterruptHold = exec1 != null + ? mux( + exec0.done & exec0.valid, + exec0.interruptHold, + exec1.interruptHold, + ) + : exec0.interruptHold; Sequential(clk, [ If( - reset | ~exec.done, + reset | ~execDone, then: [ done < 0, valid < 0, @@ -285,18 +452,262 @@ class RiverPipeline extends Module { trapCause < 0, trapTval < 0, fence < 0, + counter < 0, + if (useMixedExecution && + microcodeExecRead != null && + csrRead != null) ...[ + csrRead.en < 0, + csrRead.addr < 0, + csrRead0!.data < 0, + csrRead0!.done < 0, + csrRead0!.valid < 0, + csrRead1!.data < 0, + csrRead1!.done < 0, + csrRead1!.valid < 0, + ], + if (useMixedExecution && + microcodeExecRead != null && + csrWrite != null) ...[ + csrWrite.en < 0, + csrWrite.addr < 0, + csrWrite0!.done < 0, + csrWrite0!.valid < 0, + csrWrite1!.done < 0, + csrWrite1!.valid < 0, + ], + if (useMixedExecution && microcodeExecRead != null) ...[ + memExecRead.en < 0, + memExecRead.addr < 0, + memExecRead0!.data < 0, + memExecRead0!.done < 0, + memExecRead0!.valid < 0, + memExecRead1!.data < 0, + memExecRead1!.done < 0, + memExecRead1!.valid < 0, + memWrite.en < 0, + memWrite.addr < 0, + memWrite0!.done < 0, + memWrite0!.valid < 0, + memWrite1!.done < 0, + memWrite1!.valid < 0, + rs1Read.en < 0, + rs1Read.addr < 0, + rs1Read0!.data < 0, + rs1Read0!.done < 0, + rs1Read0!.valid < 0, + rs1Read1!.data < 0, + rs1Read1!.done < 0, + rs1Read1!.valid < 0, + rs2Read.en < 0, + rs2Read.addr < 0, + rs2Read0!.data < 0, + rs2Read0!.done < 0, + rs2Read0!.valid < 0, + rs2Read1!.data < 0, + rs2Read1!.done < 0, + rs2Read1!.valid < 0, + rdWrite.en < 0, + rdWrite.addr < 0, + rdWrite0!.done < 0, + rdWrite0!.valid < 0, + rdWrite1!.done < 0, + rdWrite1!.valid < 0, + ], ], orElse: [ - done < fetcher.done & decodeDone & exec.done, - valid < fetcher.valid & decodeValid, - nextSp < exec.nextSp, - nextPc < exec.nextPc, - nextMode < exec.nextMode, - trap < exec.trap, - trapCause < exec.trapCause, - trapTval < exec.trapTval, - fence < exec.fence, - interruptHold < exec.interruptHold, + done < fetcher.done & decodeDone & execDone, + valid < fetcher.valid & decodeValid & execValid, + nextSp < execNextSp, + nextPc < execNextPc, + nextMode < execNextMode, + trap < execTrap, + trapCause < execTrapCause, + trapTval < execTrapTval, + fence < execFence, + interruptHold < execInterruptHold, + If(enable, then: [counter < (counter + 1)]), + if (useMixedExecution && microcodeExecRead != null && csrRead != null) + If.block([ + Iff(csrRead0!.en, [ + csrRead.en < 1, + csrRead.addr < csrRead0.addr, + csrRead0!.data < csrRead.data, + csrRead0!.done < csrRead.done, + csrRead0!.valid < csrRead.valid, + ]), + Iff(csrRead1!.en, [ + csrRead.en < 1, + csrRead.addr < csrRead1!.addr, + csrRead1!.data < csrRead.data, + csrRead1!.done < csrRead.done, + csrRead1!.valid < csrRead.valid, + ]), + Else([ + csrRead.en < 0, + csrRead.addr < 0, + csrRead0!.data < 0, + csrRead0!.done < 0, + csrRead0!.valid < 0, + csrRead1!.data < 0, + csrRead1!.done < 0, + csrRead1!.valid < 0, + ]), + ]), + if (useMixedExecution && + microcodeExecRead != null && + csrWrite != null) + If.block([ + Iff(csrWrite0!.en, [ + csrWrite.en < 1, + csrWrite.addr < csrWrite0!.addr, + csrWrite.data < csrWrite0!.data, + csrWrite0!.done < csrWrite.done, + csrWrite0!.valid < csrWrite.valid, + ]), + Iff(csrWrite1!.en, [ + csrWrite.en < 1, + csrWrite.addr < csrWrite1!.addr, + csrWrite.data < csrWrite1!.data, + csrWrite1!.done < csrWrite.done, + csrWrite1!.valid < csrWrite.valid, + ]), + Else([ + csrWrite.en < 0, + csrWrite.addr < 0, + csrWrite0!.done < 0, + csrWrite0!.valid < 0, + csrWrite1!.done < 0, + csrWrite1!.valid < 0, + ]), + ]), + if (useMixedExecution && microcodeExecRead != null) ...[ + If.block([ + Iff(memExecRead0!.en, [ + memExecRead.en < 1, + memExecRead.addr < memExecRead0.addr, + memExecRead0!.data < memExecRead.data, + memExecRead0!.done < memExecRead.done, + memExecRead0!.valid < memExecRead.valid, + ]), + Iff(memExecRead1!.en, [ + memExecRead.en < 1, + memExecRead.addr < memExecRead1!.addr, + memExecRead1!.data < memExecRead.data, + memExecRead1!.done < memExecRead.done, + memExecRead1!.valid < memExecRead.valid, + ]), + Else([ + memExecRead.en < 0, + memExecRead.addr < 0, + memExecRead0!.data < 0, + memExecRead0!.done < 0, + memExecRead0!.valid < 0, + memExecRead1!.data < 0, + memExecRead1!.done < 0, + memExecRead1!.valid < 0, + ]), + ]), + If.block([ + Iff(memWrite0!.en, [ + memWrite.en < 1, + memWrite.addr < memWrite0!.addr, + memWrite.data < memWrite0!.data, + memWrite0!.done < memWrite.done, + memWrite0!.valid < memWrite.valid, + ]), + Iff(memWrite1!.en, [ + memWrite.en < 1, + memWrite.addr < memWrite1!.addr, + memWrite.data < memWrite1!.data, + memWrite1!.done < memWrite.done, + memWrite1!.valid < memWrite.valid, + ]), + Else([ + memWrite.en < 0, + memWrite.addr < 0, + memWrite0!.done < 0, + memWrite0!.valid < 0, + memWrite1!.done < 0, + memWrite1!.valid < 0, + ]), + ]), + If.block([ + Iff(rs1Read0!.en, [ + rs1Read.en < 1, + rs1Read.addr < rs1Read0.addr, + rs1Read0!.data < rs1Read.data, + rs1Read0!.done < rs1Read.done, + rs1Read0!.valid < rs1Read.valid, + ]), + Iff(rs1Read1!.en, [ + rs1Read.en < 1, + rs1Read.addr < rs1Read1!.addr, + rs1Read1!.data < rs1Read.data, + rs1Read1!.done < rs1Read.done, + rs1Read1!.valid < rs1Read.valid, + ]), + Else([ + rs1Read.en < 0, + rs1Read.addr < 0, + rs1Read0!.data < 0, + rs1Read0!.done < 0, + rs1Read0!.valid < 0, + rs1Read1!.data < 0, + rs1Read1!.done < 0, + rs1Read1!.valid < 0, + ]), + ]), + If.block([ + Iff(rs2Read0!.en, [ + rs2Read.en < 1, + rs2Read.addr < rs2Read0.addr, + rs2Read0!.data < rs2Read.data, + rs2Read0!.done < rs2Read.done, + rs2Read0!.valid < rs2Read.valid, + ]), + Iff(rs2Read1!.en, [ + rs2Read.en < 1, + rs2Read.addr < rs2Read1!.addr, + rs2Read1!.data < rs2Read.data, + rs2Read1!.done < rs2Read.done, + rs2Read1!.valid < rs2Read.valid, + ]), + Else([ + rs2Read.en < 0, + rs2Read.addr < 0, + rs2Read0!.data < 0, + rs2Read0!.done < 0, + rs2Read0!.valid < 0, + rs2Read1!.data < 0, + rs2Read1!.done < 0, + rs2Read1!.valid < 0, + ]), + ]), + If.block([ + Iff(rdWrite0!.en, [ + rdWrite.en < 1, + rdWrite.addr < rdWrite0!.addr, + rdWrite.data < rdWrite0!.data, + rdWrite0!.done < rdWrite.done, + rdWrite0!.valid < rdWrite.valid, + ]), + Iff(rdWrite1!.en, [ + rdWrite.en < 1, + rdWrite.addr < rdWrite1!.addr, + rdWrite.data < rdWrite1!.data, + rdWrite1!.done < rdWrite.done, + rdWrite1!.valid < rdWrite.valid, + ]), + Else([ + rdWrite.en < 0, + rdWrite.addr < 0, + rdWrite0!.done < 0, + rdWrite0!.valid < 0, + rdWrite1!.done < 0, + rdWrite1!.valid < 0, + ]), + ]), + ], ], ), ]); diff --git a/packages/river_hdl/lib/src/soc.dart b/packages/river_hdl/lib/src/soc.dart index beb3ddd..b4f044c 100644 --- a/packages/river_hdl/lib/src/soc.dart +++ b/packages/river_hdl/lib/src/soc.dart @@ -13,6 +13,7 @@ class RiverSoCIP extends BridgeModule { this.config, { Map> deviceOptions = const {}, Map deviceFactory = kDeviceModuleFactory, + List staticInstructions = const [], }) : super('RiverSoC') { createPort('reset', PortDirection.input); @@ -71,7 +72,9 @@ class RiverSoCIP extends BridgeModule { for (final coreConfig in config.cores) { final clk = port('clk_${coreConfig.clock.name}'); - final core = addSubModule(RiverCoreIP(coreConfig)); + final core = addSubModule( + RiverCoreIP(coreConfig, staticInstructions: staticInstructions), + ); connectPorts(clk, core.port('clk')); connectPorts(reset, core.port('reset')); diff --git a/packages/river_hdl/pubspec.yaml b/packages/river_hdl/pubspec.yaml index 982ff40..c169819 100644 --- a/packages/river_hdl/pubspec.yaml +++ b/packages/river_hdl/pubspec.yaml @@ -20,6 +20,10 @@ dependencies: git: url: https://github.com/MidstallSoftware/rohd-hcl.git ref: integration + z3: + git: + url: https://github.com/pingbird/dz3 + path: dz3 # path: ^1.9.0 dev_dependencies: diff --git a/packages/river_hdl/test/core/exec_test.dart b/packages/river_hdl/test/core/exec_test.dart index 21f37f5..61e2ab3 100644 --- a/packages/river_hdl/test/core/exec_test.dart +++ b/packages/river_hdl/test/core/exec_test.dart @@ -19,6 +19,7 @@ Future execTest( Map initRegisters = const {}, int nextPc = 4, int memLatency = 0, + bool isDynamic = false, }) async { final clk = SimpleClockGenerator(20).clk; final reset = Logic(); @@ -92,31 +93,71 @@ Future execTest( numEntries: 32, ); - final exec = ExecutionUnit( + final microcodeRead = DataPortInterface( + microcode.mopWidth(mxlen), + microcode.mopIndexWidth(mxlen), + ); + + RegisterFile( clk, reset, - decoder.valid & decoder.done, - Const(0, width: mxlen.size), - Const(0, width: mxlen.size), - mode, - decoder.index, - decoder.instrTypeMap, - decoder.fields, - csrRead, - csrWrite, - memRead, - memWrite, - rs1Read, - rs2Read, - rdWrite, - microcode: microcode, - mxlen: mxlen, - mideleg: csrs.mideleg, - medeleg: csrs.medeleg, - mtvec: csrs.mtvec, - stvec: csrs.stvec, + [], + [microcodeRead], + numEntries: microcode.encodedMops(mxlen).length, + resetValue: microcode.encodedMops(mxlen), ); + final exec = isDynamic + ? DynamicExecutionUnit( + clk, + reset, + decoder.valid & decoder.done, + Const(0, width: mxlen.size), + Const(0, width: mxlen.size), + mode, + decoder.index, + decoder.instrTypeMap, + decoder.fields, + csrRead, + csrWrite, + memRead, + memWrite, + rs1Read, + rs2Read, + rdWrite, + microcodeRead, + microcode: microcode, + mxlen: mxlen, + mideleg: csrs.mideleg, + medeleg: csrs.medeleg, + mtvec: csrs.mtvec, + stvec: csrs.stvec, + ) + : StaticExecutionUnit( + clk, + reset, + decoder.valid & decoder.done, + Const(0, width: mxlen.size), + Const(0, width: mxlen.size), + mode, + decoder.index, + decoder.instrTypeMap, + decoder.fields, + csrRead, + csrWrite, + memRead, + memWrite, + rs1Read, + rs2Read, + rdWrite, + microcode: microcode, + mxlen: mxlen, + mideleg: csrs.mideleg, + medeleg: csrs.medeleg, + mtvec: csrs.mtvec, + stvec: csrs.stvec, + ); + await exec.build(); reset.inject(1); @@ -157,6 +198,13 @@ Future execTest( await clk.nextPosedge; } + for (final csrState in initCsrs.entries) { + csrs + .getBackdoor(LogicValue.ofInt(csrState.key.address, 12)) + .wrEn! + .inject(0); + } + while (!exec.done.value.toBool()) { await clk.nextPosedge; } @@ -165,17 +213,20 @@ Future execTest( await clk.nextPosedge; } + await clk.nextPosedge; + await Simulator.endSimulation(); await Simulator.simulationEnded; expect(exec.done.value.toBool(), isTrue); + expect(exec.valid.value.toBool(), isTrue); expect(exec.nextPc.value.toInt(), nextPc); for (final regState in regStates.entries) { - expect( - regs.getData(LogicValue.ofInt(regState.key.value, 5))!.toInt(), - regState.value, - ); + final value = regs + .getData(LogicValue.ofInt(regState.key.value, 5))! + .toInt(); + expect(value, regState.value, reason: '${regState.key}=$value'); } for (final memState in memStates.entries) { @@ -186,10 +237,10 @@ Future execTest( } for (final csrState in csrStates.entries) { - expect( - csrs.getData(LogicValue.ofInt(csrState.key.address, 12))!.toInt(), - csrState.value, - ); + final value = csrs + .getData(LogicValue.ofInt(csrState.key.address, 12))! + .toInt(); + expect(value, csrState.value, reason: '${csrState.key}=$value'); } } @@ -198,217 +249,245 @@ void main() { await Simulator.reset(); }); - group('RV32I', () { - final microcode = Microcode(Microcode.buildDecodeMap([rv32i])); - - test( - 'addi increments register', - () => execTest(0x00a08293, {Register.x5: 10}, microcode, Mxlen.mxlen_32), - ); + void define(bool isDynamic) { + group('RV32I', () { + final microcode = Microcode(Microcode.buildDecodeMap([rv32i])); + + test( + 'addi increments register', + () => execTest( + 0x00a08293, + {Register.x5: 10}, + microcode, + Mxlen.mxlen_32, + isDynamic: isDynamic, + ), + ); - test( - 'add performs register addition', - () => execTest( - 0x005303B3, - {Register.x7: 16}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 7, Register.x6: 9}, - ), - ); + test( + 'add performs register addition', + () => execTest( + 0x005303B3, + {Register.x7: 16}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 7, Register.x6: 9}, + isDynamic: isDynamic, + ), + ); - test( - 'lw loads from memory', - () => execTest( - 0x0042A303, - {Register.x6: 0xDEADBEEF}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x20}, - initMem: {0x24: 0xDEADBEEF}, - ), - ); + test( + 'lw loads from memory', + () => execTest( + 0x0042A303, + {Register.x6: 0xDEADBEEF}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x20}, + initMem: {0x24: 0xDEADBEEF}, + isDynamic: isDynamic, + ), + ); - test( - 'sw stores to memory', - () => execTest( - 0x0062A223, - {}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x20, Register.x6: 0xDEADBEEF}, - initMem: {}, - ), - ); + test( + 'sw stores to memory', + () => execTest( + 0x0062A223, + {}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x20, Register.x6: 0xDEADBEEF}, + initMem: {}, + isDynamic: isDynamic, + ), + ); - test( - 'beq takes branch when equal', - () => execTest( - 0x00628463, - {}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 5, Register.x6: 5}, - nextPc: 8, - ), - ); + test( + 'beq takes branch when equal', + () => execTest( + 0x00628463, + {}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 5, Register.x6: 5}, + nextPc: 8, + isDynamic: isDynamic, + ), + ); - test( - 'beq does not branch when not equal', - () => execTest( - 0x00628463, - {}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 5, Register.x6: 7}, - nextPc: 4, - ), - ); + test( + 'beq does not branch when not equal', + () => execTest( + 0x00628463, + {}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 5, Register.x6: 7}, + nextPc: 4, + isDynamic: isDynamic, + ), + ); - test( - 'lui loads upper immediate', - () => execTest( - 0x123452B7, - {Register.x5: 0x12345000}, - microcode, - Mxlen.mxlen_32, - ), - ); + test( + 'lui loads upper immediate', + () => execTest( + 0x123452B7, + {Register.x5: 0x12345000}, + microcode, + Mxlen.mxlen_32, + isDynamic: isDynamic, + ), + ); - test( - 'jal writes ra and jumps', - () => execTest( - 0x100002EF, - {Register.x5: 4}, - microcode, - Mxlen.mxlen_32, - nextPc: 0x100, - ), - ); + test( + 'jal writes ra and jumps', + () => execTest( + 0x100002EF, + {Register.x5: 4}, + microcode, + Mxlen.mxlen_32, + nextPc: 0x100, + isDynamic: isDynamic, + ), + ); - test( - 'auipc adds immediate to PC', - () => execTest( - 0x00010297, - {Register.x5: 0x10000}, - microcode, - Mxlen.mxlen_32, - ), - ); + test( + 'auipc adds immediate to PC', + () => execTest( + 0x00010297, + {Register.x5: 0x10000}, + microcode, + Mxlen.mxlen_32, + isDynamic: isDynamic, + ), + ); - test( - 'slti sets when less-than immediate', - () => execTest( - 0x00A22293, - {Register.x5: 1}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x4: 5}, - ), - ); - }); + test( + 'slti sets when less-than immediate', + () => execTest( + 0x00A22293, + {Register.x5: 1}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x4: 5}, + isDynamic: isDynamic, + ), + ); + }); + + group('Zicsr', () { + final microcode = Microcode(Microcode.buildDecodeMap([rv32i, rv32Zicsr])); + + test( + 'csrrw: atomic swap (rd=old, CSR=new)', + () => execTest( + 0x34029373, + {Register.x6: 0xAAAA}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x1234}, + initCsrs: {CsrAddress.mscratch: 0xAAAA}, + csrStates: {CsrAddress.mscratch: 0x1234}, + isDynamic: isDynamic, + ), + ); - group('Zicsr', () { - final microcode = Microcode(Microcode.buildDecodeMap([rv32i, rv32Zicsr])); - - test( - 'csrrw: atomic swap (rd=old, CSR=new)', - () => execTest( - 0x34029373, - {Register.x6: 0xAAAA}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x1234}, - initCsrs: {CsrAddress.mscratch: 0xAAAA}, - csrStates: {CsrAddress.mscratch: 0x1234}, - ), - ); + test( + 'csrrw with rd=x0 still writes CSR but suppresses rd write', + () => execTest( + 0x34029073, + {Register.x0: 0}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x2222}, + initCsrs: {CsrAddress.mscratch: 0x1111}, + csrStates: {CsrAddress.mscratch: 0x2222}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrw with rd=x0 still writes CSR but suppresses rd write', - () => execTest( - 0x34029073, - {Register.x0: 0}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x2222}, - initCsrs: {CsrAddress.mscratch: 0x1111}, - csrStates: {CsrAddress.mscratch: 0x2222}, - ), - ); + test( + 'csrrs: rd=old, CSR |= rs1', + () => execTest( + 0x3402A373, + {Register.x6: 0x100}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x0F}, + initCsrs: {CsrAddress.mscratch: 0x100}, + csrStates: {CsrAddress.mscratch: 0x10F}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrs: rd=old, CSR |= rs1', - () => execTest( - 0x3402A373, - {Register.x6: 0x100}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x0F}, - initCsrs: {CsrAddress.mscratch: 0x100}, - csrStates: {CsrAddress.mscratch: 0x10F}, - ), - ); + test( + 'csrrs with rs1=x0 only reads CSR', + () => execTest( + 0x3400A073, + {}, + microcode, + Mxlen.mxlen_32, + initCsrs: {CsrAddress.mscratch: 0xABCDE}, + csrStates: {CsrAddress.mscratch: 0xABCDE}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrs with rs1=x0 only reads CSR', - () => execTest( - 0x3400A073, - {}, - microcode, - Mxlen.mxlen_32, - initCsrs: {CsrAddress.mscratch: 0xABCDE}, - csrStates: {CsrAddress.mscratch: 0xABCDE}, - ), - ); + test( + 'csrrc: CSR &= ~rs1', + () => execTest( + 0x3402B373, + {Register.x6: 0xFF}, + microcode, + Mxlen.mxlen_32, + initRegisters: {Register.x5: 0x0F}, + initCsrs: {CsrAddress.mscratch: 0xFF}, + csrStates: {CsrAddress.mscratch: 0xF0}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrc: CSR &= ~rs1', - () => execTest( - 0x3402B373, - {Register.x6: 0xFF}, - microcode, - Mxlen.mxlen_32, - initRegisters: {Register.x5: 0x0F}, - initCsrs: {CsrAddress.mscratch: 0xFF}, - csrStates: {CsrAddress.mscratch: 0xF0}, - ), - ); + test( + 'csrrwi: CSR = imm, rd = old CSR', + () => execTest( + 0x3402D373, + {Register.x6: 0x7777}, + microcode, + Mxlen.mxlen_32, + initCsrs: {CsrAddress.mscratch: 0x7777}, + csrStates: {CsrAddress.mscratch: 5}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrwi: CSR = imm, rd = old CSR', - () => execTest( - 0x3402D373, - {Register.x6: 0x7777}, - microcode, - Mxlen.mxlen_32, - initCsrs: {CsrAddress.mscratch: 0x7777}, - csrStates: {CsrAddress.mscratch: 5}, - ), - ); + test( + 'csrrsi: CSR |= imm', + () => execTest( + 0x3401E073, + {}, + microcode, + Mxlen.mxlen_32, + initCsrs: {CsrAddress.mscratch: 0x10}, + csrStates: {CsrAddress.mscratch: 0x13}, + isDynamic: isDynamic, + ), + ); - test( - 'csrrsi: CSR |= imm', - () => execTest( - 0x3401E073, - {}, - microcode, - Mxlen.mxlen_32, - initCsrs: {CsrAddress.mscratch: 0x10}, - csrStates: {CsrAddress.mscratch: 0x13}, - ), - ); + test( + 'csrrsi: CSR &= ~imm', + () => execTest( + 0x3401F073, + {}, + microcode, + Mxlen.mxlen_32, + initCsrs: {CsrAddress.mscratch: 0xF}, + csrStates: {CsrAddress.mscratch: 0xC}, + isDynamic: isDynamic, + ), + ); + }); + } - test( - 'csrrsi: CSR &= ~imm', - () => execTest( - 0x3401F073, - {}, - microcode, - Mxlen.mxlen_32, - initCsrs: {CsrAddress.mscratch: 0xF}, - csrStates: {CsrAddress.mscratch: 0xC}, - ), - ); - }); + group('Static execution', () => define(false)); + group('Dynamic execution', () => define(true)); } diff --git a/packages/river_hdl/test/core/pipeline_test.dart b/packages/river_hdl/test/core/pipeline_test.dart index ba74902..37627f6 100644 --- a/packages/river_hdl/test/core/pipeline_test.dart +++ b/packages/river_hdl/test/core/pipeline_test.dart @@ -83,6 +83,7 @@ Future pipelineTest( rs2Read, rdWrite, null, + null, microcode: microcode, mxlen: mxlen, mideleg: csrs.mideleg, diff --git a/pubspec.lock b/pubspec.lock index 9090582..194c901 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "91.0.0" + version: "93.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 + sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b url: "https://pub.dev" source: hosted - version: "8.4.1" + version: "10.0.1" args: dependency: transitive description: @@ -93,10 +93,18 @@ packages: dependency: transitive description: name: dartdoc - sha256: "73335ae67d2766ff753dc78b0f33ceb399ba5b94a5cfe8ce8d4128fb631717ee" + sha256: f232803d877d6ec4a0bfa72454d3a8d6cbf4519ef09634d9e3faa02d5c3f8de5 url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "9.0.2" + ffi: + dependency: transitive + description: + name: ffi + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c + url: "https://pub.dev" + source: hosted + version: "2.1.5" file: dependency: transitive description: @@ -181,18 +189,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -245,18 +253,18 @@ packages: dependency: transitive description: name: rohd - sha256: "7ce36e8d39bda546009d5b76455c848c6971296d43cdbf2a81654d908c7a2bf2" + sha256: edc62417aaac3aacccd4b9515f11b62b8f823595f4b661333aa554db730a4e11 url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.6.8" rohd_bridge: dependency: transitive description: name: rohd_bridge - sha256: c43a9cd6eb7ea26b71cf96abb8878b6744ea2f8a37a1ef005b8e088c488c5cc5 + sha256: "541577b8af73f1f1a5965f4646b7cff2552727a01d42a87246ec51f94c7351e7" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" rohd_hcl: dependency: transitive description: @@ -366,26 +374,26 @@ packages: dependency: transitive description: name: test - sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae" + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" url: "https://pub.dev" source: hosted - version: "1.28.0" + version: "1.29.0" test_api: dependency: transitive description: name: test_api - sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.9" test_core: dependency: transitive description: name: test_core - sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4 + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" url: "https://pub.dev" source: hosted - version: "0.6.14" + version: "0.6.15" typed_data: dependency: transitive description: @@ -406,10 +414,10 @@ packages: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.1" web: dependency: transitive description: @@ -450,5 +458,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + z3: + dependency: transitive + description: + path: dz3 + ref: HEAD + resolved-ref: "0c12f8fe10d064df5aca0794ed86398bec224471" + url: "https://github.com/pingbird/dz3" + source: git + version: "0.1.0" sdks: dart: ">=3.9.4 <4.0.0" diff --git a/pubspec.lock.json b/pubspec.lock.json index efda6f1..719d389 100644 --- a/pubspec.lock.json +++ b/pubspec.lock.json @@ -4,21 +4,21 @@ "dependency": "transitive", "description": { "name": "_fe_analyzer_shared", - "sha256": "c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d", + "sha256": "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d", "url": "https://pub.dev" }, "source": "hosted", - "version": "91.0.0" + "version": "93.0.0" }, "analyzer": { "dependency": "transitive", "description": { "name": "analyzer", - "sha256": "f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08", + "sha256": "de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b", "url": "https://pub.dev" }, "source": "hosted", - "version": "8.4.1" + "version": "10.0.1" }, "args": { "dependency": "transitive", @@ -114,11 +114,21 @@ "dependency": "transitive", "description": { "name": "dartdoc", - "sha256": "73335ae67d2766ff753dc78b0f33ceb399ba5b94a5cfe8ce8d4128fb631717ee", + "sha256": "f232803d877d6ec4a0bfa72454d3a8d6cbf4519ef09634d9e3faa02d5c3f8de5", "url": "https://pub.dev" }, "source": "hosted", - "version": "9.0.0" + "version": "9.0.2" + }, + "ffi": { + "dependency": "transitive", + "description": { + "name": "ffi", + "sha256": "d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.5" }, "file": { "dependency": "transitive", @@ -224,21 +234,21 @@ "dependency": "transitive", "description": { "name": "matcher", - "sha256": "dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2", + "sha256": "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6", "url": "https://pub.dev" }, "source": "hosted", - "version": "0.12.17" + "version": "0.12.18" }, "meta": { "dependency": "transitive", "description": { "name": "meta", - "sha256": "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394", + "sha256": "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349", "url": "https://pub.dev" }, "source": "hosted", - "version": "1.17.0" + "version": "1.18.0" }, "mime": { "dependency": "transitive", @@ -304,21 +314,21 @@ "dependency": "transitive", "description": { "name": "rohd", - "sha256": "7ce36e8d39bda546009d5b76455c848c6971296d43cdbf2a81654d908c7a2bf2", + "sha256": "edc62417aaac3aacccd4b9515f11b62b8f823595f4b661333aa554db730a4e11", "url": "https://pub.dev" }, "source": "hosted", - "version": "0.6.7" + "version": "0.6.8" }, "rohd_bridge": { "dependency": "transitive", "description": { "name": "rohd_bridge", - "sha256": "c43a9cd6eb7ea26b71cf96abb8878b6744ea2f8a37a1ef005b8e088c488c5cc5", + "sha256": "541577b8af73f1f1a5965f4646b7cff2552727a01d42a87246ec51f94c7351e7", "url": "https://pub.dev" }, "source": "hosted", - "version": "0.2.0" + "version": "0.2.1" }, "rohd_hcl": { "dependency": "transitive", @@ -455,31 +465,31 @@ "dependency": "transitive", "description": { "name": "test", - "sha256": "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae", + "sha256": "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a", "url": "https://pub.dev" }, "source": "hosted", - "version": "1.28.0" + "version": "1.29.0" }, "test_api": { "dependency": "transitive", "description": { "name": "test_api", - "sha256": "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8", + "sha256": "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636", "url": "https://pub.dev" }, "source": "hosted", - "version": "0.7.8" + "version": "0.7.9" }, "test_core": { "dependency": "transitive", "description": { "name": "test_core", - "sha256": "f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4", + "sha256": "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943", "url": "https://pub.dev" }, "source": "hosted", - "version": "0.6.14" + "version": "0.6.15" }, "typed_data": { "dependency": "transitive", @@ -505,11 +515,11 @@ "dependency": "transitive", "description": { "name": "watcher", - "sha256": "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a", + "sha256": "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635", "url": "https://pub.dev" }, "source": "hosted", - "version": "1.1.4" + "version": "1.2.1" }, "web": { "dependency": "transitive", @@ -560,6 +570,17 @@ }, "source": "hosted", "version": "3.1.3" + }, + "z3": { + "dependency": "transitive", + "description": { + "path": "dz3", + "ref": "HEAD", + "resolved-ref": "0c12f8fe10d064df5aca0794ed86398bec224471", + "url": "https://github.com/pingbird/dz3" + }, + "source": "git", + "version": "0.1.0" } }, "sdks": { From 345ad9a5b5cd716112d28b923ada5ff99d32da87 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Sun, 18 Jan 2026 17:49:33 -0800 Subject: [PATCH 2/4] chore(ci): set public --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d26d637..00879d4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,7 @@ jobs: id-token: write contents: read with: + visibility: public runner-map: | { "aarch64-darwin": "macos-latest", From 18acf75b49d95f5c508fdc99b6c733ac1d51c308 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Sun, 18 Jan 2026 17:59:48 -0800 Subject: [PATCH 3/4] chore: remove z3 --- .github/workflows/ci.yaml | 5 ----- flake.nix | 20 -------------------- packages/river_hdl/pubspec.yaml | 5 ----- pubspec.lock | 17 ----------------- pubspec.lock.json | 21 --------------------- 5 files changed, 68 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 00879d4..6fb17de 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,8 +15,3 @@ jobs: contents: read with: visibility: public - runner-map: | - { - "aarch64-darwin": "macos-latest", - "x86_64-linux": "ubuntu-latest" - } diff --git a/flake.nix b/flake.nix index b620281..a17d4db 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,6 @@ gitHashes = { rohd_hcl = "sha256-YobXIH2PTUXxp6MfcAIJG8aXhkc1MZOLthOEaQUJxOM="; - z3 = "sha256-B1gmCsFivLDKCJcBmdf2JOJqDvn6bEawqJ8QLix5Ils="; }; buildDartTest = @@ -121,10 +120,6 @@ src = ./.; - buildInputs = [ - pkgs.z3 - ]; - dartEntryPoints = { "bin/river-emulator" = "packages/river_emulator/bin/river_emulator.dart"; "bin/river-hdlgen" = "packages/river_hdl/bin/river_hdlgen.dart"; @@ -133,10 +128,6 @@ preBuild = '' mkdir -p bin ''; - - postBuild = '' - patchelf bin/river-hdlgen --add-needed libz3.so - ''; }; emulator = buildDartApplication { pname = "river-emulator"; @@ -155,10 +146,6 @@ pname = "river-hdl"; inherit version pubspecLock gitHashes; - buildInputs = [ - pkgs.z3 - ]; - src = ./.; packageRoot = "packages/river_hdl"; @@ -167,16 +154,10 @@ preBuild = '' mkdir -p bin ''; - - postBuild = '' - patchelf bin/river-hdlgen --add-needed libz3.so - ''; }; }; devShells.default = pkgs.mkShell { - LD_LIBRARY_PATH = "${pkgs.z3.lib}/lib"; - packages = with pkgs; ( @@ -186,7 +167,6 @@ yosys icestorm nextpnr - z3 gtkwave surfer pkgsCross.riscv32-embedded.stdenv.cc diff --git a/packages/river_hdl/pubspec.yaml b/packages/river_hdl/pubspec.yaml index c169819..d5c1f8a 100644 --- a/packages/river_hdl/pubspec.yaml +++ b/packages/river_hdl/pubspec.yaml @@ -20,11 +20,6 @@ dependencies: git: url: https://github.com/MidstallSoftware/rohd-hcl.git ref: integration - z3: - git: - url: https://github.com/pingbird/dz3 - path: dz3 - # path: ^1.9.0 dev_dependencies: lints: ^6.0.0 diff --git a/pubspec.lock b/pubspec.lock index 194c901..7906a39 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -97,14 +97,6 @@ packages: url: "https://pub.dev" source: hosted version: "9.0.2" - ffi: - dependency: transitive - description: - name: ffi - sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c - url: "https://pub.dev" - source: hosted - version: "2.1.5" file: dependency: transitive description: @@ -458,14 +450,5 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" - z3: - dependency: transitive - description: - path: dz3 - ref: HEAD - resolved-ref: "0c12f8fe10d064df5aca0794ed86398bec224471" - url: "https://github.com/pingbird/dz3" - source: git - version: "0.1.0" sdks: dart: ">=3.9.4 <4.0.0" diff --git a/pubspec.lock.json b/pubspec.lock.json index 719d389..38010ef 100644 --- a/pubspec.lock.json +++ b/pubspec.lock.json @@ -120,16 +120,6 @@ "source": "hosted", "version": "9.0.2" }, - "ffi": { - "dependency": "transitive", - "description": { - "name": "ffi", - "sha256": "d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c", - "url": "https://pub.dev" - }, - "source": "hosted", - "version": "2.1.5" - }, "file": { "dependency": "transitive", "description": { @@ -570,17 +560,6 @@ }, "source": "hosted", "version": "3.1.3" - }, - "z3": { - "dependency": "transitive", - "description": { - "path": "dz3", - "ref": "HEAD", - "resolved-ref": "0c12f8fe10d064df5aca0794ed86398bec224471", - "url": "https://github.com/pingbird/dz3" - }, - "source": "git", - "version": "0.1.0" } }, "sdks": { From fa243900ed4b380e596ebfcf3357c0c1820ad448 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Sun, 18 Jan 2026 18:37:16 -0800 Subject: [PATCH 4/4] fix(ci): merge queue --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6fb17de..9efd564 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,7 @@ name: CI on: pull_request: workflow_dispatch: + merge_group: push: branches: - master