diff --git a/DecodeM.fs b/DecodeM.fs index cc25d0c..5d0d7a1 100644 --- a/DecodeM.fs +++ b/DecodeM.fs @@ -17,13 +17,6 @@ type InstructionM = | REM of {| rd: Register; rs1: Register; rs2: Register |} | REMU of {| rd: Register; rs1: Register; rs2: Register |} - // Only x64 - | MULW of {| rd: Register; rs1: Register; rs2: Register |} - | DIVW of {| rd: Register; rs1: Register; rs2: Register |} - | DIVUW of {| rd: Register; rs1: Register; rs2: Register |} - | REMW of {| rd: Register; rs1: Register; rs2: Register |} - | REMUW of {| rd: Register; rs1: Register; rs2: Register |} - | None // Instruction not found /// Decode 'M' instructions @@ -56,21 +49,6 @@ let Decode (mstate : MachineState) (instr: InstrField) : InstructionM = | _ -> None | _ -> None - // RV64M Standard Extension (in addition to RV32M) - | 0b0111011 when mstate.Arch.archBits = RV64 -> - match funct7 with - | 0b0000001 -> - match funct3 with - // Multiplication Operations - | 0b000 -> MULW {| rd = rd; rs1 = rs1; rs2 = rs2 |} - // Division Operations - | 0b100 -> DIVW {| rd = rd; rs1 = rs1; rs2 = rs2 |} - | 0b101 -> DIVUW {| rd = rd; rs1 = rs1; rs2 = rs2 |} - | 0b110 -> REMW {| rd = rd; rs1 = rs1; rs2 = rs2 |} - | 0b111 -> REMUW {| rd = rd; rs1 = rs1; rs2 = rs2 |} - | _ -> None - | _ -> None - | _ -> None // Current ISA print log message for current instruction step @@ -79,8 +57,7 @@ let verbosityMessage (instr : InstrField) (decodedInstr : InstructionM) (mstate let instrMsg = match (decodedInstr) with | MUL x | MULH x | MULHSU x | MULHU x | DIV x - | DIVU x | REM x | REMU x | MULW x | DIVW x - | DIVUW x | REMW x | REMUW x -> sprintf "x%d, x%d, x%d" x.rd x.rs1 x.rs2 + | DIVU x | REM x | REMU x -> sprintf "x%d, x%d, x%d" x.rd x.rs1 x.rs2 | _ -> "Undef" let pc = sprintf "%08x:" mstate.PC let instr = sprintf "%08x" instr diff --git a/DecodeM64.fs b/DecodeM64.fs new file mode 100644 index 0000000..532500c --- /dev/null +++ b/DecodeM64.fs @@ -0,0 +1,59 @@ +module ISA.RISCV.Decode.M64 + +open System +open ISA.RISCV.Utils.Bits +open ISA.RISCV.Arch +open ISA.RISCV.MachineState + +//================================================================ -- \begin_latex{Major_Opcodes} +// 'M64' (Integer Multiplication and Division 'M' Standard Extension) +type InstructionM64 = + | MULW of {| rd: Register; rs1: Register; rs2: Register |} + | DIVW of {| rd: Register; rs1: Register; rs2: Register |} + | DIVUW of {| rd: Register; rs1: Register; rs2: Register |} + | REMW of {| rd: Register; rs1: Register; rs2: Register |} + | REMUW of {| rd: Register; rs1: Register; rs2: Register |} + + | None // Instruction not found + +/// Decode 'M64' instructions +let Decode (mstate : MachineState) (instr: InstrField) : InstructionM64 = + let opcode = instr.bitSlice 6 0 + // Register number can be: 0-32 + let rd = int32(instr.bitSlice 11 7) + let rs1 = int32(instr.bitSlice 19 15) + let rs2 = int32(instr.bitSlice 24 20) + + let funct3 = instr.bitSlice 14 12 + let funct7 = instr.bitSlice 31 25 + + match (opcode) with + // RV64M Standard Extension (in addition to RV32M) + | 0b0111011 when mstate.Arch.archBits = RV64 -> + match funct7 with + | 0b0000001 -> + match funct3 with + // Multiplication Operations + | 0b000 -> MULW {| rd = rd; rs1 = rs1; rs2 = rs2 |} + // Division Operations + | 0b100 -> DIVW {| rd = rd; rs1 = rs1; rs2 = rs2 |} + | 0b101 -> DIVUW {| rd = rd; rs1 = rs1; rs2 = rs2 |} + | 0b110 -> REMW {| rd = rd; rs1 = rs1; rs2 = rs2 |} + | 0b111 -> REMUW {| rd = rd; rs1 = rs1; rs2 = rs2 |} + | _ -> None + | _ -> None + + | _ -> None + +// Current ISA print log message for current instruction step +let verbosityMessage (instr : InstrField) (decodedInstr : InstructionM64) (mstate : MachineState) = + let typeName = decodedInstr.GetType().Name + let instrMsg = + match (decodedInstr) with + MULW x | DIVW x | DIVUW x | + REMW x | REMUW x -> sprintf "x%d, x%d, x%d" x.rd x.rs1 x.rs2 + | _ -> "Undef" + let pc = sprintf "%08x:" mstate.PC + let instr = sprintf "%08x" instr + let instrMsg = String.Format("{0,-7}{1}", typeName, instrMsg) + printfn "%s" (String.Format("{0,-12}{1,-12}{2}", pc, instr, instrMsg)) diff --git a/Decoder.fs b/Decoder.fs index cd2ba08..09bfb90 100644 --- a/Decoder.fs +++ b/Decoder.fs @@ -15,6 +15,8 @@ let Decode (mstate : MachineState) (instr: InstrField) : execFunc option = let decI32 = I.Decode mstate instr let decI64 = I64.Decode instr let decM = M.Decode mstate instr + let decM64 = M64.Decode mstate instr + // Check is instruction should be executed let execI32 = match mstate.Arch with @@ -30,8 +32,9 @@ let Decode (mstate : MachineState) (instr: InstrField) : execFunc option = | _ -> false let execM64 = match mstate.Arch with - | RV64im when decM <> M.InstructionM.None -> true + | RV64im when decM64 <> M64.InstructionM64.None -> true | _ -> false + // Decoded instruction and execute ISA function if execI32 then Some(I.Execute decI32) @@ -40,6 +43,6 @@ let Decode (mstate : MachineState) (instr: InstrField) : execFunc option = else if execM32 then Some(M.Execute decM) else if execM64 then - Some(M64.Execute decM) + Some(M64.Execute decM64) else None diff --git a/ExecuteM.fs b/ExecuteM.fs index 88bc8f9..cc722cd 100644 --- a/ExecuteM.fs +++ b/ExecuteM.fs @@ -5,6 +5,36 @@ open ISA.RISCV.Arch open ISA.RISCV.MachineState open ISA.RISCV.Utils.Bits +let mulhu (x : uint64, y : uint64) : uint64 = + let x0 = uint64(uint32 x) + let y0 = uint64(uint32 y) + let x1 = x >>> 32 + let y1 = y >>> 32 + + let t = x1 * y0 + ((x0 * y0) >>> 32) + let y2 = uint64(uint32 t) + let y3 = t >>> 32 + let y4 = x0 * y1 + y2 + let t = x1 * y1 + y3 + (y4 >>> 32) + let y5 = uint64(uint32 t) + let y6 = (t >>> 32) <<< 32 + y6 ||| y5 + +let mulh (x : int64, y : int64) : int64 = + let neg = (x < 0L) <> (y < 0L) + let x1 = uint64(if x < 0L then -x else x) + let y1 = uint64(if y < 0L then -y else y) + let res = mulhu(x1, y1) + let resd = if (x * y) = 0L then 1L else 0L + if neg then int64(~~~res) + resd else int64 res + +let mulhsu (x : int64, y : uint64) : int64 = + let neg = x < 0L + let x1 = uint64(if x < 0L then -x else x) + let res = mulhu(x1, y) + let resd = if (x * int64 y) = 0L then 1L else 0L + if neg then int64(~~~res) + resd else int64 res + //================================================= // MUL - Multiplication operation - sign * sign let execMUL (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = @@ -15,8 +45,15 @@ let execMUL (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineS //================================================= // MULH - Multiplication operation - sign * sign and return high 32 bits let execMULH (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = - let rdVal = (mstate.getRegister rs1) * (mstate.getRegister rs2) - let hRes = rdVal.bitSlice 63 32 + let hRes = + match mstate.Arch.archBits with + | RV32 -> + let rdVal = (mstate.getRegister rs1) * (mstate.getRegister rs2) + rdVal.bitSlice 63 32 + | _ -> + let rs1Val = mstate.getRegister rs1 + let rs2Val = mstate.getRegister rs2 + mulh(rs1Val, rs2Val) let mstate = mstate.setRegister rd hRes mstate.incPC @@ -25,11 +62,13 @@ let execMULH (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : Machine let execMULHSU (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = let rs1Val = mstate.getRegister rs1 let rs2Val = mstate.getRegister rs2 - let rdVal = + let hRes = match mstate.Arch.archBits with - | RV32 -> rs1Val * int64(uint32 rs2Val) - | _ -> rs1Val * int64(uint64 rs2Val) - let hRes = rdVal.bitSlice 63 32 + | RV32 -> + let rdVal = rs1Val * int64(uint32 rs2Val) + rdVal.bitSlice 63 32 + | _ -> + mulhsu(rs1Val, uint64 rs2Val) let mstate = mstate.setRegister rd hRes mstate.incPC @@ -38,11 +77,13 @@ let execMULHSU (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : Machi let execMULHU (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = let rs1Val = mstate.getRegister rs1 let rs2Val = mstate.getRegister rs2 - let rdVal = + let hRes = match mstate.Arch.archBits with - | RV32 -> int64(uint32 rs1Val) * int64(uint32 rs2Val) - | _ -> int64(uint64 rs1Val) * int64(uint64 rs2Val) - let hRes = rdVal.bitSlice 63 32 + | RV32 -> + let rdVal = int64(uint32 rs1Val) * int64(uint32 rs2Val) + rdVal.bitSlice 63 32 + | _ -> + int64(mulhu(uint64 rs1Val, uint64 rs2Val)) let mstate = mstate.setRegister rd hRes mstate.incPC diff --git a/ExecuteM64.fs b/ExecuteM64.fs index a9f33d8..21a3249 100644 --- a/ExecuteM64.fs +++ b/ExecuteM64.fs @@ -1,7 +1,7 @@ module ISA.RISCV.Execute.M64 open ISA.RISCV.Arch -open ISA.RISCV.Decode.M +open ISA.RISCV.Decode.M64 open ISA.RISCV.MachineState //================================================= @@ -15,16 +15,16 @@ let execMULW (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : Machine //================================================= // DIVW - Division Word operation - sign * sign let execDIVW (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = - let rs1Val = mstate.getRegister rs1 - let rs2Val = mstate.getRegister rs2 - let minSigned = 0x8000000000000000L + let rs1Val = int32(mstate.getRegister rs1) + let rs2Val = int32(mstate.getRegister rs2) + let minSigned = 0x80000000 let rdVal = - if rs2Val = 0L then + if rs2Val = 0 then -1 - else if rs1Val = minSigned && rs2Val = -1L then - int32 rs1Val + else if rs1Val = minSigned && rs2Val = -1 then + rs1Val else - int32 rs1Val / int32 rs2Val + rs1Val / rs2Val let mstate = mstate.setRegister rd (int64 rdVal) mstate.incPC @@ -45,16 +45,16 @@ let execDIVUW (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : Machin //================================================= // REMW - Division Unsign Word operation - sign * sign let execREMW (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : MachineState) = - let rs1Val = mstate.getRegister rs1 - let rs2Val = mstate.getRegister rs2 - let minSigned = 0x8000000000000000L + let rs1Val = int32(mstate.getRegister rs1) + let rs2Val = int32(mstate.getRegister rs2) + let minSigned = 0x80000000 let rdVal = - if rs2Val = 0L then + if rs2Val = 0 then int32 rs1Val - else if rs1Val = minSigned && rs2Val = -1L then + else if rs1Val = minSigned && rs2Val = -1 then 0 else - int32 rs1Val % int32 rs2Val + rs1Val % rs2Val let mstate = mstate.setRegister rd (int64 rdVal) mstate.incPC @@ -72,7 +72,7 @@ let execREMUW (rd : Register) (rs1 : Register) (rs2 : Register) (mstate : Machin mstate.incPC // Execute M64-instructions -let Execute (instr : InstructionM) (mstate : MachineState) = +let Execute (instr : InstructionM64) (mstate : MachineState) = match instr with | MULW i -> execMULW i.rd i.rs1 i.rs2 mstate diff --git a/README.md b/README.md index 0e8c963..261c561 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ Technical Group constituted by The RISC-V Foundation - [x] Tests RV32I - [x] Base instruction set: RV64I - [x] Tests RV64I - - [ ] Standard extension M (integer multiply/divide) - - [ ] Tests for Standard extension M RV32/RV64 + - [x] Standard extension M (integer multiply/divide) + - [x] Tests for Standard extension M RV32/RV64 + - [ ] Standard extension A (atomic memory ops) + - [ ] Tests for Standard extension A RV32/RV64 * Features under development - * Standard extension A (atomic memory ops) * Standard extension C (Compressed 16-bit instructions) * Standard extension F (Single-precision floating point) * Standard extension D (Double-precision floating point) diff --git a/Tests/Tests.fsproj b/Tests/Tests.fsproj index 30fa5b6..13b9861 100644 --- a/Tests/Tests.fsproj +++ b/Tests/Tests.fsproj @@ -33,6 +33,9 @@ + + + diff --git a/Tests/asm/Makefile b/Tests/asm/Makefile index 0fbab4c..1e1369e 100644 --- a/Tests/asm/Makefile +++ b/Tests/asm/Makefile @@ -30,7 +30,7 @@ $(BUILDDIR)/%32: $(SRCDIR)/%.S init32.o mmio.ld $(BUILDDIR)/%64: $(SRCDIR)/%.S init64.o mmio.ld mkdir -p $(BUILDDIR) - $(RISCVCC64) -c $(SRCDIR)/$*.S -o intermediate64.o + $(RISCVCC64) -c $(SRCDIR)/$*.S -o intermediate64.o -D x64 $(RISCVCC64) -o $(BUILDDIR)/$*64 -Tmmio.ld intermediate64.o init64.o rm intermediate64.o diff --git a/Tests/asm/src/alu.S b/Tests/asm/src/alu.S index 83a8635..863ad9c 100644 --- a/Tests/asm/src/alu.S +++ b/Tests/asm/src/alu.S @@ -11,10 +11,30 @@ main: sra x3, x2, x1 or x3, x2, x1 and x3, x2, x1 - # x64 +#ifdef x64 addw x3, x2, x1 subw x3, x2, x1 sllw x3, x2, x1 srlw x3, x2, x1 sraw x3, x2, x1 +#endif + + # M-extension + mul x3, x2, x1 + mulh x3, x2, x1 + mulhu x3, x2, x1 + mulhsu x3, x2, x1 + + div x3, x2, x1 + divu x3, x2, x1 + rem x3, x2, x1 + remu x3, x2, x1 + +#ifdef x64 + mulw x3, x2, x1 + divw x3, x2, x1 + divuw x3, x2, x1 + remw x3, x2, x1 + remuw x3, x2, x1 +#endif ret diff --git a/Tests/asm/src/alui.S b/Tests/asm/src/alui.S index 186999f..071e852 100644 --- a/Tests/asm/src/alui.S +++ b/Tests/asm/src/alui.S @@ -11,9 +11,10 @@ main: slli x3, x2, 5 srli x3, x2, 5 srai x3, x2, 5 - # x64 only +#ifdef x64 addiw x3, x2, 5 slliw x3, x2, 5 srliw x3, x2, 5 sraiw x3, x2, 5 +#endif ret diff --git a/Tests/asm/src/mem.S b/Tests/asm/src/mem.S index f094c0a..0ef8c1d 100644 --- a/Tests/asm/src/mem.S +++ b/Tests/asm/src/mem.S @@ -7,15 +7,19 @@ main: lh x3, -10(x2) lw x3, 10(x2) lw x3, -10(x2) +#ifdef x64 ld x3, 10(x2) ld x3, -10(x2) +#endif lbu x3, 10(x2) lbu x3, -10(x2) lhu x3, 10(x2) lhu x3, -10(x2) +#ifdef x64 lwu x3, 10(x2) lwu x3, -10(x2) +#endif sb x3, 10(x2) sb x3, -10(x2) @@ -23,7 +27,8 @@ main: sh x3, -10(x2) sw x3, 10(x2) sw x3, -10(x2) - # x64 +#ifdef x64 sd x3, 10(x2) sd x3, -10(x2) +#endif ret diff --git a/Tests/rv32m/alu.fs b/Tests/rv32m/alu.fs new file mode 100644 index 0000000..778f298 --- /dev/null +++ b/Tests/rv32m/alu.fs @@ -0,0 +1,171 @@ +module Tests.rv32m.alu + +open Xunit + +open ISA.RISCV +open ISA.RISCV.Arch + +//=============================================== +// ALU tests +let ALU instr x1 x2 x3 = + // Init MachineState + let addr = 0x80000000L + let mstate = MachineState.InitMachineState Map.empty RV32im true + let mstate = mstate.setPC addr + let mstate = mstate.setRegister 1 x1 + let mstate = mstate.setRegister 2 x2 + + let executor = Decoder.Decode mstate instr + Assert.NotEqual(executor, None) + let mstate = executor.Value mstate + Assert.Equal(x1, mstate.getRegister 1) + Assert.Equal(x2, mstate.getRegister 2) + Assert.Equal(x3, mstate.getRegister 3) + Assert.Equal(addr + 4L, mstate.PC) + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``MUL: x3 = x2 * x1`` ( x3, x1, x2) = + ALU 0x021101b3 x1 x2 x3 + +//[] +//[] +//[] +//[] +//[] +//[] +//[] +//[] +//[] +//[] +//[] +//[] +let ``MULH: x3 = half (x2 * x1)`` (x3, x1, x2) = + ALU 0x021111b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``MULHSU: x3 = half (sign x2 * unsign x1)`` (x3, x2, x1) = + ALU 0x021121b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``MULHU: x3 = half (unsign x2 * unsign x1)`` (x3, x1, x2) = + ALU 0x021131b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIV: x3 = x2 / x1`` (x3, x2, x1) = + ALU 0x021141b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIVU: x3 = (unsign x2) / (unsign x1)`` (x3, x2, x1) = + ALU 0x021151b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``REM: x3 = x2 % x1`` (x3, x2, x1) = + ALU 0x021161b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``REMU: x3 = (unsign x2) % (unsign x1)`` (x3, x2, x1) = + ALU 0x021171b3 x1 x2 x3 diff --git a/Tests/rv64m/alu.fs b/Tests/rv64m/alu.fs new file mode 100644 index 0000000..2185fae --- /dev/null +++ b/Tests/rv64m/alu.fs @@ -0,0 +1,186 @@ +module Tests.rv64m.alu + +open Xunit + +open ISA.RISCV +open ISA.RISCV.Arch + +//=============================================== +// ALU `x64` tests +let ALU instr x1 x2 x3 = + // Init MachineState + let addr = 0x80000000L + let mstate = MachineState.InitMachineState Map.empty RV64im true + let mstate = mstate.setPC addr + let mstate = mstate.setRegister 1 x1 + let mstate = mstate.setRegister 2 x2 + + let executor = Decoder.Decode mstate instr + Assert.NotEqual(executor, None) + let mstate = executor.Value mstate + Assert.Equal(x1, mstate.getRegister 1) + Assert.Equal(x2, mstate.getRegister 2) + Assert.Equal(x3, mstate.getRegister 3) + Assert.Equal(addr + 4L, mstate.PC) + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``MUL: x3 = x2 * x1`` ( x3, x2, x1) = + ALU 0x021101b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +let ``MULW: x3 = x2 * x1`` (x3, x2, x1) = + ALU 0x021101bb x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +let ``MULH: x3 = half (x2 * x1)`` (x3, x2, x1) = + ALU 0x021111b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``WMULHU: x3 = half (unsign x2 * unsign x1)`` (x3, x2, x1) = + ALU 0x021131b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +let ``WMULHSU: x3 = half (sign x2 * unsign x1)`` (x3, x2, x1) = + ALU 0x021121b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIV: x3 = x2 / x1`` (x3, x2, x1) = + ALU 0x021141b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIVW: x3 = x2 / x1`` (x3, x2, x1) = + ALU 0x021141bb x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIVU: x3 = (unsign x2) / (unsign x1)`` (x3, x2, x1) = + ALU 0x021151b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``DIVUW: x3 = unsign x2 / unsign x1`` (x3, x2, x1) = + ALU 0x021151bb x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``REM: x3 = x2 % x1`` (x3, x2, x1) = + ALU 0x021161b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``REMW: x3 = x2 % x1`` (x3, x2, x1) = + ALU 0x021161bb x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``x64-REMU: x3 = (unsign x2) % (unsign x1)`` (x3, x2, x1) = + ALU 0x021171b3 x1 x2 x3 + +[] +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``REMUW: x3 = unsign x2 % unsign x1`` (x3, x2, x1) = + ALU 0x021171bb x1 x2 x3 diff --git a/risc-v.fsproj b/risc-v.fsproj index fda35de..b7bfb64 100644 --- a/risc-v.fsproj +++ b/risc-v.fsproj @@ -28,6 +28,7 @@ Additionally it's possible to operate with CLI, reading ELF files and set verbos +