From 8d1e7528b62e7afb2bf799258505733b400d84ea Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Thu, 15 Aug 2024 14:41:24 -0700 Subject: [PATCH] Add Zaamo instructions. Also adds params to properly handle the configurable priority of Misaligned/Access/Page faults --- arch/ext/I.yaml | 39 ++++++- arch/inst/A/amoadd.d.yaml | 40 +++++++ arch/inst/A/amoadd.w.yaml | 39 +++++++ arch/inst/A/amoand.d.yaml | 40 +++++++ arch/inst/A/amoand.w.yaml | 39 +++++++ arch/inst/A/amomax.d.yaml | 40 +++++++ arch/inst/A/amomax.w.yaml | 39 +++++++ arch/inst/A/amomaxu.d.yaml | 40 +++++++ arch/inst/A/amomaxu.w.yaml | 39 +++++++ arch/inst/A/amomin.d.yaml | 40 +++++++ arch/inst/A/amomin.w.yaml | 39 +++++++ arch/inst/A/amominu.d.yaml | 40 +++++++ arch/inst/A/amominu.w.yaml | 39 +++++++ arch/inst/A/amoor.d.yaml | 40 +++++++ arch/inst/A/amoor.w.yaml | 39 +++++++ arch/inst/A/amoswap.d.yaml | 39 +++++++ arch/inst/A/amoswap.w.yaml | 38 +++++++ arch/inst/A/amoxor.d.yaml | 40 +++++++ arch/inst/A/amoxor.w.yaml | 39 +++++++ arch/isa/builtin_functions.idl | 34 +++++- arch/isa/globals.isa | 201 +++++++++++++++++++++++++++++++-- arch/isa/util.idl | 16 +++ cfgs/generic_rv64/params.yaml | 4 + lib/arch_def.rb | 3 +- lib/idl/ast.rb | 4 + 25 files changed, 999 insertions(+), 11 deletions(-) create mode 100644 arch/inst/A/amoadd.d.yaml create mode 100644 arch/inst/A/amoadd.w.yaml create mode 100644 arch/inst/A/amoand.d.yaml create mode 100644 arch/inst/A/amoand.w.yaml create mode 100644 arch/inst/A/amomax.d.yaml create mode 100644 arch/inst/A/amomax.w.yaml create mode 100644 arch/inst/A/amomaxu.d.yaml create mode 100644 arch/inst/A/amomaxu.w.yaml create mode 100644 arch/inst/A/amomin.d.yaml create mode 100644 arch/inst/A/amomin.w.yaml create mode 100644 arch/inst/A/amominu.d.yaml create mode 100644 arch/inst/A/amominu.w.yaml create mode 100644 arch/inst/A/amoor.d.yaml create mode 100644 arch/inst/A/amoor.w.yaml create mode 100644 arch/inst/A/amoswap.d.yaml create mode 100644 arch/inst/A/amoswap.w.yaml create mode 100644 arch/inst/A/amoxor.d.yaml create mode 100644 arch/inst/A/amoxor.w.yaml diff --git a/arch/ext/I.yaml b/arch/ext/I.yaml index 1ca3d962c..ea266d886 100644 --- a/arch/ext/I.yaml +++ b/arch/ext/I.yaml @@ -109,10 +109,47 @@ I: maximum: 127 MISALIGNED_LDST: description: | - Whether or not the implementation supports misaligned loads and stores in + Whether or not the implementation supports non-atomic misaligned loads and stores in main memory (does *not* affect misaligned support to device memory) schema: type: boolean + MISALIGNED_LDST_EXCEPTION_PRIORITY: + description: | + The relative priority of a load/store/AMO exception vs. load/store/AMO page-fault + or access-fault exceptions. + + May be one of: + + [separator="!"] + !=== + ! high ! Misaligned load/store/AMO exceptions are always higher priority than load/store/AMO page-fault and access-fault exceptions. + ! low ! Misaligned load/store/AMO exceptions are always lower priority than load/store/AMO page-fault and access-fault exceptions. + !=== + + MISALIGNED_LDST_EXCEPTION_PRIORITY cannot be "high" when MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE + is non-zero, since the atomicity of an access cannot be determined in that case until after + address translation. + schema: + type: string + enum: ["high", "low"] + extra_validation: | + assert (MISALIGNED_LDST_EXCEPTION_PRIORITY == "low") if MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE.positive? + MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE: + description: | + The maximum granule size, in bytes, that the hart can atomically perform a + misligned load/store/AMO without raising a Misaligned exception. When MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE is 0, the hart + cannot atomically perform a misaligned load/store/AMO. When a power of two, the hart can + atomically load/store/AMO a misaligned access that is fully contained in a + MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE-aligned region. + + [NOTE] + Even if the hart is capable of performing a misligned load/store/AMO atomically, + a misaligned exception may still occur if the access does not have the appropriate + Misaligned Atomicity Granule PMA set. + schema: + type: integer + # can't be larger than a page, since there is no way to reconcile that with virtual memory + enum: [0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096] MISALIGNED_SPLIT_STRATEGY: description: | when misaligned accesses are supported, this determines the *order* in the implementation appears diff --git a/arch/inst/A/amoadd.d.yaml b/arch/inst/A/amoadd.d.yaml new file mode 100644 index 000000000..91db7a70a --- /dev/null +++ b/arch/inst/A/amoadd.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoadd.d: + long_name: Atomic fetch-and-add doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * Add the value of register _rs2_ to the loaded value + * Write the sum to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 00000------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Add, aq, rl); diff --git a/arch/inst/A/amoadd.w.yaml b/arch/inst/A/amoadd.w.yaml new file mode 100644 index 000000000..1ca53a0b2 --- /dev/null +++ b/arch/inst/A/amoadd.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoadd.w: + long_name: Atomic fetch-and-add word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Add the least-significant word of register _rs2_ to the loaded value + * Write the sum to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 00000------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Add, aq, rl); diff --git a/arch/inst/A/amoand.d.yaml b/arch/inst/A/amoand.d.yaml new file mode 100644 index 000000000..376feece6 --- /dev/null +++ b/arch/inst/A/amoand.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoand.d: + long_name: Atomic fetch-and-and doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * AND the value of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 01100------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::And, aq, rl); diff --git a/arch/inst/A/amoand.w.yaml b/arch/inst/A/amoand.w.yaml new file mode 100644 index 000000000..f0c7f9f04 --- /dev/null +++ b/arch/inst/A/amoand.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoand.w: + long_name: Atomic fetch-and-and word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * AND the least-significant word of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 01100------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::And, aq, rl); diff --git a/arch/inst/A/amomax.d.yaml b/arch/inst/A/amomax.d.yaml new file mode 100644 index 000000000..b90e630cc --- /dev/null +++ b/arch/inst/A/amomax.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomax.d: + long_name: Atomic MAX doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * Signed compare the value of register _rs2_ to the loaded value, and select the maximum value + * Write the maximum to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 10100------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Max, aq, rl); diff --git a/arch/inst/A/amomax.w.yaml b/arch/inst/A/amomax.w.yaml new file mode 100644 index 000000000..4d4543799 --- /dev/null +++ b/arch/inst/A/amomax.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomax.w: + long_name: Atomic MAX word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Signed compare the least-significant word of register _rs2_ to the loaded value, and select the maximum value + * Write the maximum to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 10100------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Max, aq, rl); diff --git a/arch/inst/A/amomaxu.d.yaml b/arch/inst/A/amomaxu.d.yaml new file mode 100644 index 000000000..01d805774 --- /dev/null +++ b/arch/inst/A/amomaxu.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomaxu.d: + long_name: Atomic MAX unsigned doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * Unsigned compare the value of register _rs2_ to the loaded value, and select the maximum value + * Write the maximum to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 11100------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Maxu, aq, rl); diff --git a/arch/inst/A/amomaxu.w.yaml b/arch/inst/A/amomaxu.w.yaml new file mode 100644 index 000000000..b43f8917e --- /dev/null +++ b/arch/inst/A/amomaxu.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomaxu.w: + long_name: Atomic MAX unsigned word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Unsigned compare the least-significant word of register _rs2_ to the loaded value, and select the maximum value + * Write the maximum to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 11100------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Maxu, aq, rl); diff --git a/arch/inst/A/amomin.d.yaml b/arch/inst/A/amomin.d.yaml new file mode 100644 index 000000000..4e34dc3a0 --- /dev/null +++ b/arch/inst/A/amomin.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomin.d: + long_name: Atomic MIN doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * Signed compare the value of register _rs2_ to the loaded value, and select the mimimum value + * Write the minimum to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 10000------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Min, aq, rl); diff --git a/arch/inst/A/amomin.w.yaml b/arch/inst/A/amomin.w.yaml new file mode 100644 index 000000000..ef145b7dc --- /dev/null +++ b/arch/inst/A/amomin.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amomin.w: + long_name: Atomic MIN word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Signed compare the least-significant word of register _rs2_ to the loaded value, and select the mimimum value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 10000------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Min, aq, rl); diff --git a/arch/inst/A/amominu.d.yaml b/arch/inst/A/amominu.d.yaml new file mode 100644 index 000000000..dcb17992b --- /dev/null +++ b/arch/inst/A/amominu.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amominu.d: + long_name: Atomic MIN unsigned doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * Unsigned compare the value of register _rs2_ to the loaded value, and select the mimimum value + * Write the minimum to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 11000------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Minu, aq, rl); diff --git a/arch/inst/A/amominu.w.yaml b/arch/inst/A/amominu.w.yaml new file mode 100644 index 000000000..6ec6dc4a4 --- /dev/null +++ b/arch/inst/A/amominu.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amominu.w: + long_name: Atomic MIN unsigned word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Unsigned compare the least-significant word of register _rs2_ to the loaded word, and select the mimimum value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 11000------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Minu, aq, rl); diff --git a/arch/inst/A/amoor.d.yaml b/arch/inst/A/amoor.d.yaml new file mode 100644 index 000000000..271ec25f4 --- /dev/null +++ b/arch/inst/A/amoor.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoor.d: + long_name: Atomic fetch-and-or doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * OR the value of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 01000------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Or, aq, rl); diff --git a/arch/inst/A/amoor.w.yaml b/arch/inst/A/amoor.w.yaml new file mode 100644 index 000000000..332c4570a --- /dev/null +++ b/arch/inst/A/amoor.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoor.w: + long_name: Atomic fetch-and-or word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * OR the least-significant word of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 01000------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Or, aq, rl); diff --git a/arch/inst/A/amoswap.d.yaml b/arch/inst/A/amoswap.d.yaml new file mode 100644 index 000000000..056e6607e --- /dev/null +++ b/arch/inst/A/amoswap.d.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoswap.d: + long_name: Atomic SWAP doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the value into _rd_ + * Store the value of register _rs2_ to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 00001------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Swap, aq, rl); diff --git a/arch/inst/A/amoswap.w.yaml b/arch/inst/A/amoswap.w.yaml new file mode 100644 index 000000000..e7804a73c --- /dev/null +++ b/arch/inst/A/amoswap.w.yaml @@ -0,0 +1,38 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoswap.w: + long_name: Atomic SWAP word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * Store the least-significant word of register _rs2_ to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 00001------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Swap, aq, rl); diff --git a/arch/inst/A/amoxor.d.yaml b/arch/inst/A/amoxor.d.yaml new file mode 100644 index 000000000..19cfdfe80 --- /dev/null +++ b/arch/inst/A/amoxor.d.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoxor.d: + long_name: Atomic fetch-and-xor doubleword + description: | + Atomically: + + * Load the doubleword at address _rs1_ + * Write the loaded value into _rd_ + * XOR the value of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + base: 64 + assembly: xd, xs2, (xrs1) + encoding: + match: 00100------------011-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<64>(virtual_address, X[rs2], AmoOperation::Xor, aq, rl); diff --git a/arch/inst/A/amoxor.w.yaml b/arch/inst/A/amoxor.w.yaml new file mode 100644 index 000000000..5a2d8c975 --- /dev/null +++ b/arch/inst/A/amoxor.w.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +amoxor.w: + long_name: Atomic fetch-and-xor word + description: | + Atomically: + + * Load the word at address _rs1_ + * Write the sign-extended value into _rd_ + * XOR the least-significant word of register _rs2_ to the loaded value + * Write the result to the address in _rs1_ + definedBy: [A, Zaamo] + assembly: xd, xs2, (xrs1) + encoding: + match: 00100------------010-----0101111 + variables: + - name: aq + location: 26 + - name: rl + location: 27 + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + if (implemented?(ExtensionName::A) && (CSR[misa].A == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, $encoding); + } + + XReg virtual_address = X[rs1]; + + X[rd] = amo<32>(virtual_address, X[rs2][31:0], AmoOperation::Xor, aq, rl); diff --git a/arch/isa/builtin_functions.idl b/arch/isa/builtin_functions.idl index 47226e533..87dce26bf 100644 --- a/arch/isa/builtin_functions.idl +++ b/arch/isa/builtin_functions.idl @@ -335,4 +335,36 @@ builtin function atomically_set_pte_a_d { * Sets the 'A' and 'D' bits and writes the result to _pte_addr_ * return true } -} \ No newline at end of file +} + +builtin function atomic_read_modify_write_32 { + returns Bits<32> + arguments + Bits phys_addr, + Bits<32> value, + AmoOperation op + description { + Atomically read-modify-write 32-bits starting at phys_address using +value+ and +op+. + + Return the original (unmodified) read value. + + All access checks/alignment checks/etc. should be done before calling this function; + it's assumed the RMW is OK to proceed. + } +} + +builtin function atomic_read_modify_write_64 { + returns Bits<64> + arguments + Bits phys_addr, + Bits<64> value, + AmoOperation op + description { + Atomically read-modify-write 64-bits starting at phys_address using +value+ and +op+. + + Return the original (unmodified) read value. + + All access checks/alignment checks/etc. should be done before calling this function; + it's assumed the RMW is OK to proceed. + } +} diff --git a/arch/isa/globals.isa b/arch/isa/globals.isa index 65360f7fa..a70a24d89 100644 --- a/arch/isa/globals.isa +++ b/arch/isa/globals.isa @@ -43,14 +43,37 @@ enum MemoryOperation { Fetch } +# Types of Atmoic Read-modify-write operations +enum AmoOperation { + Swap + Add + And + Or + Xor + Max + Maxu + Min + Minu +} + enum PmaAttribute { RsrvNone # LR/SC not allowed RsrvNonEventual # LR/SC allowed, but no gaurantee it will ever succeed RsrvEventual # LR/SC with forward-progress gaurantees + MAG16 # Misaligned Atomicity Granule = 16-byte + MAG8 # Misaligned Atomicity Granule = 8-byte + MAG4 # Misaligned Atomicity Granule = 4-byte + MAG2 # Misaligned Atomicity Granule = 2-byte + + AmoNone # No AMOs allowed + AmoSwap # amoswap is allowed + AmoLogical # amoswap, amoand, amoor, and amoxor are allowed + AmoArithmetic # All amos are allowed + # page walk permissions - HardwarePageTableRead - HardwarePageTableWrite + HardwarePageTableRead # permission to read during a page walk + HardwarePageTableWrite # permission to write during a page walk } # do not change these values!! the compiler assumes them @@ -1341,6 +1364,40 @@ function canonical_vaddr? { } } +function misaligned_is_atomic? { + template U32 N + returns Boolean + arguments Bits physical_address + description { + Returns true if an access starting at +physical_address+ that is +N+ bits long is atomic. + + This function takes into account any Atomicity Granule PMAs, so *it should not be used + for load-reserved/store-conditional*, since those PMAs do not apply to those accesses. + } + body { + # if the hart doesn't support Misligned Atomicity Granules, + # then this misligned access is not atomic + return false if MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE == 0; + + if (pma_applies?(PmaAttribute::MAG16, physical_address, N) && + in_naturally_aligned_region?(physical_address_address, N)) { + return true; + } else if (pma_applies?(PmaAttribute::MAG8, physical_address, N) && + in_naturally_aligned_region?(physical_address, N)) { + return true; + } else if (pma_applies?(PmaAttribute::MAG4, physical_address, N) && + in_naturally_aligned_region?(physical_address, N)) { + return true; + } else if (pma_applies?(PmaAttribute::MAG2, physical_address, N) && + in_naturally_aligned_region?(physical_address, N)) { + return true; + } else { + # not saved by a Misaligned Atomicity Granule + return false; + } + } +} + function read_memory_aligned { template U32 len returns Bits @@ -1367,16 +1424,49 @@ function read_memory { returns Bits arguments XReg virtual_address description { - Read from virtual memory + Read from virtual memory. + } body { Boolean aligned = is_naturally_aligned(virtual_address); + XReg physical_address; if (aligned) { return read_memory_aligned(virtual_address); - } else if (!aligned && !MISALIGNED_LDST) { + } + + # access isn't naturally aligned, but it still might be atomic if this hart supports + # Misliagned Atomicity Granules. We won't know that, though, until after translation since PMAs + # apply to physical addresses + if (MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE > 0) { + # sanity check that the implementation isn't expecting a Misaligned exception + # before an access/page fault exception (that would be an invalid config) + assert(MISALIGNED_LDST_EXCEPTION_PRIORITY == "low"); + + physical_address = (CSR[misa].S == 1) + ? translate(virtual_address, MemoryOperation::Read) + : virtual_address; + + if (misaligned_is_atomic?(physical_address)) { + access_check(physical_address, virtual_address, MemoryOperation::Read, ExceptionCode::LoadAccessFault); + return read_physical_memory(physical_address); + } + } + + # at this point, we have a misligned access + if (!MISALIGNED_LDST) { + # misaligned is not supported, we'll raise either a Misaligned exception or + # a page/access fault exception, depending on the configuration + if (MISALIGNED_LDST_EXCEPTION_PRIORITY == "low") { + # do translation to trigger any access/page faults before raising misaligned + physical_address = (CSR[misa].S == 1) + ? translate(virtual_address, MemoryOperation::Read) + : virtual_address; + access_check(physical_address, virtual_address, MemoryOperation::Read, ExceptionCode::LoadAccessFault); + } raise (ExceptionCode::LoadAddressMisaligned, virtual_address); } else { + # misaligned, must break into multiple reads if (MISALIGNED_SPLIT_STRATEGY == "by_byte") { Bits result = 0; @@ -1563,6 +1653,66 @@ function store_conditional { } } +function amo { + template U32 N # number of bits being loaded/stored + returns Bits + arguments + Bits virtual_address, # the virtual address to load from/store to + Bits value, # the value for the second hald of the atomic operation + AmoOperation op, # atomic operation to apply + Bits<1> aq, # acquire semantics? 0=no, 1=yes + Bits<1> rl # release semantics? 0=no, 1=yes + description { + Atomically read-modify-write the location at virtual_address. + + The value written to virtual_address will depend on +op+. + + If +aq+ is 1, then the amo also acts as a memory model acquire. + If +rl+ is 1, then the amo also acts as a memory model release. + } + body { + Boolean aligned = is_naturally_aligned(virtual_address); + + if (!aligned && MISALIGNED_LDST_EXCEPTION_PRIORITY == "high") { + raise(ExceptionCode::StoreAmoAddressMisaligned, virtual_address); + } + + Bits physical_address = + (CSR[misa].S == 1) + ? translate(virtual_address, MemoryOperation::ReadModifyWrite) + : virtual_address; + + # PMA Atomicity checks + if (pma_applies?(PmaAttribute::AmoNone, physical_address, N)) { + raise(ExceptionCode::StoreAmoAccessFault, virtual_address); + } else if (((op == AmoOperation::Add || op == AmoOperation::Max || op == AmoOperation::Maxu || op == AmoOperation::Min || op == AmoOperation::Minu)) && + !pma_applies?(PmaAttribute::AmoArithmetic, physical_address, N)) { + raise(ExceptionCode::StoreAmoAccessFault, virtual_address); + } else if (((op == AmoOperation::And || op == AmoOperation::Or || op == AmoOperation::Xor)) && + !pma_applies?(PmaAttribute::AmoLogical, physical_address, N)) { + raise(ExceptionCode::StoreAmoAccessFault, virtual_address); + } else { + assert( + pma_applies?(PmaAttribute::AmoSwap, physical_address, N) && + op == AmoOperation::Swap + ); + } + + # pma alignment checks + if (!aligned && + !misaligned_is_atomic?(physical_address)) { + raise (ExceptionCode::StoreAmoAddressMisaligned, virtual_address); + } + + if (N == 32) { + return atomic_read_modify_write_32(physical_address, value, op); + } else { + return atomic_read_modify_write_64(physical_address, value, op); + } + + } +} + function write_memory_aligned { @@ -1593,15 +1743,50 @@ function write_memory { } body { Boolean aligned = is_naturally_aligned(virtual_address); + XReg physical_address; if (aligned) { write_memory_aligned(virtual_address, value); - } else if (!aligned && !MISALIGNED_LDST) { + } + + # access isn't naturally aligned, but it still might be atomic if this hart supports + # Misliagned Atomicity Granules. We won't know that, though, until after translation since PMAs + # apply to physical addresses + if (MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE > 0) { + # sanity check that the implementation isn't expecting a Misaligned exception + # before an access/page fault exception (that would be an invalid config) + assert(MISALIGNED_LDST_EXCEPTION_PRIORITY == "low"); + + physical_address = (CSR[misa].S == 1) + ? translate(virtual_address, MemoryOperation::Write) + : virtual_address; + + if (misaligned_is_atomic?(physical_address)) { + access_check(physical_address, virtual_address, MemoryOperation::Write, ExceptionCode::StoreAmoAccessFault); + write_physical_memory(physical_address, value); + } + } + + # at this point, we have a misligned access that must be split + if (!MISALIGNED_LDST) { + # misaligned is not supported, we'll raise either a Misaligned exception or + # a page/access fault exception, depending on the configuration + if (MISALIGNED_LDST_EXCEPTION_PRIORITY == "low") { + # do translation to trigger any access/page faults before raising misaligned + physical_address = (CSR[misa].S == 1) + ? translate(virtual_address, MemoryOperation::Write) + : virtual_address; + access_check(physical_address, virtual_address, MemoryOperation::Write, ExceptionCode::StoreAmoAccessFault); + } raise (ExceptionCode::StoreAmoAddressMisaligned, virtual_address); } else { - # misaligned, must break into multiple writes - for (U32 i = 0; i <= len; i++) { - write_memory_aligned<8>(virtual_address + i, (value >> (8*i))[7:0]); + # misaligned, must break into multiple reads + if (MISALIGNED_SPLIT_STRATEGY == "by_byte") { + for (U32 i = 0; i <= len; i++) { + write_memory_aligned<8>(virtual_address + i, (value >> (8*i))[7:0]); + } + } else if (MISALIGNED_SPLIT_STRATEGY == "custom") { + unpredictable("An implementation is free to break a misaligned access any way, leading to unpredictable behavior when any part of the misaligned access causes an exception"); } } } diff --git a/arch/isa/util.idl b/arch/isa/util.idl index e5dde3874..308a23305 100644 --- a/arch/isa/util.idl +++ b/arch/isa/util.idl @@ -131,6 +131,22 @@ function is_naturally_aligned { } } +function in_naturally_aligned_region? { + template U32 N + returns Boolean + arguments + XReg address, # starting address + U32 length # length of the access + description { + Checks if a length-bit access starting at +address+ lies entirely within an N-bit naturally-aligned region. + } + body { + XReg Mask = (N/8) - 1; + + return (address & ~mask) == ((address + length - 1) & ~mask); + } +} + function contains? { returns Boolean arguments diff --git a/cfgs/generic_rv64/params.yaml b/cfgs/generic_rv64/params.yaml index c7fbb6ec1..423eaadab 100644 --- a/cfgs/generic_rv64/params.yaml +++ b/cfgs/generic_rv64/params.yaml @@ -22,6 +22,10 @@ params: # must be true when Zicclsm is supported MISALIGNED_LDST: true + MISALIGNED_LDST_EXCEPTION_PRIORITY: high + + MAX_MISALIGNED_ATOMICITY_GRANULE_SIZE: 0 + MISALIGNED_SPLIT_STRATEGY: by_byte # whether or not the implementation supports misaligned atomics diff --git a/lib/arch_def.rb b/lib/arch_def.rb index 72fd18bcd..ad6400cb7 100644 --- a/lib/arch_def.rb +++ b/lib/arch_def.rb @@ -1448,7 +1448,8 @@ def pruned_operation_ast(global_symtab, effective_xlen) return nil unless @data.key?("operation()") - pruned_ast = type_checked_operation_ast(arch_def, effective_xlen).prune(fill_symtab(global_symtab, effective_xlen)) + type_checked_ast = type_checked_operation_ast(arch_def, effective_xlen) + pruned_ast = type_checked_ast.prune(fill_symtab(global_symtab, effective_xlen)) arch_def.idl_compiler.type_check( pruned_ast, fill_symtab(global_symtab, effective_xlen), diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index 8888328b8..b9b1efd5f 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -41,6 +41,9 @@ class AstNode # @return [String] Source input file attr_reader :input_file + # @return [Integer] Starting line in the source input file (i.e., position 0 of {#input} in the file) + attr_reader :starting_line + # @return [String] Source string attr_reader :input @@ -131,6 +134,7 @@ def message def initialize(input, interval, children) @input = input @input_file = nil + @starting_line = 0 @interval = interval children.each { |child| raise ArgumentError, "Children of #{self.class.name} must be AstNodes (found a #{child.class.name})" unless child.is_a?(AstNode)} @children = children