*** Settings *** Test Setup Create Machine Test Tags atomics *** Variables *** ${MEMORY_START} 0x80000000 ${PLATFORM_STRING} SEPARATOR=\n ... dram: Memory.MappedMemory @ sysbus ${MEMORY_START} { ... ${SPACE*4}size: 0x80000000 ... } ... mmio: Memory.ArrayMemory @ sysbus 0x100000000 { ... ${SPACE*4}size: 0x10000 ... } ... mtvec: Memory.MappedMemory @ sysbus 0x1000 { size: 0x40000 } ... ... cpu: CPU.RiscV64 @ sysbus { ... ${SPACE*4}cpuType: "rv64gc_zicsr_zifencei_zacas"; ... ${SPACE*4}hartId: 1; ... ${SPACE*4}privilegedArchitecture: PrivilegedArchitecture.Priv1_10; ... ${SPACE*4}timeProvider: empty; ... ${SPACE*4}CyclesPerInstruction: 8; ... ${SPACE*4}allowUnalignedAccesses: true ... } ... ... cpu32: CPU.RiscV32 @ sysbus { ... ${SPACE*4}cpuType: "rv32gc_zicsr_zifencei_zacas"; ... ${SPACE*4}hartId: 2; ... ${SPACE*4}privilegedArchitecture: PrivilegedArchitecture.Priv1_10; ... ${SPACE*4}timeProvider: empty; ... ${SPACE*4}CyclesPerInstruction: 8; ... ${SPACE*4}allowUnalignedAccesses: true ... } ${PROGRAM_COUNTER} 0x80000000 ${PROGRAM_COUNTER_32} 0x80000100 ${ORDINARY_ADDRESS} 0x80001000 ${MAX_PAGE_SIZE} 0x40000000 # 1 GiB ${PAGE_SPANNING_ADDRESS} ${{str(${MEMORY_START} + ${MAX_PAGE_SIZE} - 1)}} # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${MMIO_ADDRESS} 0x0000000100001000 ${mtvec} 0x1010 ${illegal_instruction} 0x2 # Registers used ${x0} 0 ${a0} 10 ${a1} 11 ${a2} 12 ${a3} 13 ${a4} 14 ${s2} 18 # 32-, 64- and 128-bit constants ${wrong_expected_128} 0x12345678910111213141516171819202 ${wrong_expected_64} 0x1234567891011 ${wrong_expected_32} 0x1234 ${expected_128} 0x2badc00010ffb0ba1afedeadbeefd00d ${expected_64} 0x1afedeadbeefd00d ${expected_32} 0xbeefd00d ${new_128} 0x216b00b5d0d0caca1eeff00dbabedead ${new_64} 0xbeeff00dbabedead ${new_32} 0xbeefbabe *** Keywords *** Create Machine Execute Command mach create Execute Command machine LoadPlatformDescriptionFromString """${PLATFORM_STRING}""" Execute Command cpu ExecutionMode SingleStep Execute Command cpu PC ${PROGRAM_COUNTER} Execute Command cpu32 ExecutionMode SingleStep Execute Command cpu32 PC ${PROGRAM_COUNTER} Get Cpu On ${platform:(RV32|RV64)} IF "${platform}" == "RV32" ${cpu}= Set Variable cpu32 ELSE IF "${platform}" == "RV64" ${cpu}= Set Variable cpu END [return] ${cpu} Amocas.${size:(w|d|q)} ${rd} ${rs2} ${rs1} On ${platform:(RV32|RV64)} Should Throw Illegal Instruction ${cpu}= Get Cpu On ${platform} # Should have jumped to mtvec PC Should Be Equal ${mtvec} cpuName=${cpu} # The cause should be illegal instruction. ${mcause}= Execute Command ${cpu} MCAUSE Should Be Equal As Numbers ${mcause} ${illegal_instruction} # MTVAL should be the opcode that caused the fault. ${mtval}= Execute Command ${cpu} MTVAL ${illegal_amocas_opcode}= Assemble Amocas.${size} ${rd} ${rs2} ${rs1} Should Be Equal As Numbers ${mtval} ${illegal_amocas_opcode} # MEPC should point to the illegal instruction. ${mepc}= Execute Command ${cpu} MEPC Should Be Equal As Numbers ${mepc} ${PROGRAM_COUNTER} Amocas.${size:(w|d|q)} Memory Location ${address} Should Now Be Set To ${value} IF "${size}" == "w" ${current_value}= Execute Command sysbus ReadDoubleWord ${address} ELSE IF "${size}" == "d" ${current_value}= Execute Command sysbus ReadQuadWord ${address} ELSE IF "${size}" == "q" ${current_value_lower}= Execute Command sysbus ReadQuadWord ${address} ${current_value_upper}= Execute Command sysbus ReadQuadWord ${${address} + 8} END IF "${size}" == "q" ${new_value_lower}= Set Variable ${{str(int(${value}) & 0xFFFFFFFFFFFFFFFF)}} ${new_value_upper}= Set Variable ${{str((int(${value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}} Should Be Equal As Integers ${current_value_lower} ${new_value_lower} "Memory location lower should now be set to ${new_value_lower}" Should Be Equal As Integers ${current_value_upper} ${new_value_upper} "Memory location upper should now be set to ${new_value_upper}" ELSE Should Be Equal As Integers ${current_value} ${value} "Memory location ${address} should now be set to ${value} but it's ${current_value}" END Amocas.${size:(w|d|q)} Register ${register} On ${platform:(RV32|RV64)} Should Contain ${expected_value} ${cpu}= Get Cpu On ${platform} IF "${platform}" == "RV32" and "${size}" == "d" # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${expected_value_lower}= Set Variable ${{str(int(${expected_value}) & 0xFFFFFFFF)}} ${expected_value_upper}= Set Variable ${{str((int(${expected_value}) >> 32) & 0xFFFFFFFF)}} Register Should Be Equal ${register} ${expected_value_lower} cpuName=${cpu} Register Should Be Equal ${${register} + 1} ${expected_value_upper} cpuName=${cpu} ELSE IF "${platform}" == "RV64" and "${size}" == "q" # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${expected_value_lower}= Set Variable ${{str(int(${expected_value}) & 0xFFFFFFFFFFFFFFFF)}} ${expected_value_upper}= Set Variable ${{str((int(${expected_value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}} Register Should Be Equal ${register} ${expected_value_lower} cpuName=${cpu} Register Should Be Equal ${${register} + 1} ${expected_value_upper} cpuName=${cpu} ELSE Register Should Be Equal ${register} ${expected_value} cpuName=${cpu} END Amocas.${size:(w|d|q)} Set Register ${register} On ${platform:(RV32|RV64)} To ${value} ${cpu}= Get Cpu On ${platform} IF "${register}" == "0" Return From Keyword END IF "${platform}" == "RV32" and "${size}" == "d" ${value_lower}= Set Variable ${{${value} & 0xFFFFFFFF}} ${value_upper}= Set Variable ${{(${value} >> 32) & 0xFFFFFFFF}} Execute Command ${cpu} SetRegister ${register} ${value_lower} Execute Command ${cpu} SetRegister ${${register} + 1} ${value_upper} ELSE IF "${platform}" == "RV64" and "${size}" == "q" # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${value_lower}= Set Variable ${{str(int(${value}) & 0xFFFFFFFFFFFFFFFF)}} ${value_upper}= Set Variable ${{str((int(${value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}} Execute Command ${cpu} SetRegister ${register} ${value_lower} Execute Command ${cpu} SetRegister ${${register} + 1} ${value_upper} ELSE Execute Command ${cpu} SetRegister ${register} ${value} END Assemble Amocas.${size:(w|d|q)} ${rd} ${rs2} ${rs1} # Hand-assembled instructions necessary due to # our version of the LLVM assembler not supporting the Zacas extension. (issue #74345) # The machine code for an amocas instruction with its operands and size zeroed out. ${amocas_base}= Set Variable 0b00101_0_0_00000_00000_000_00000_0101111 # Translate size mnemonic to corresponding bit pattern. IF "${size}" == "w" ${size_bits}= Set Variable 0b010 ELSE IF "${size}" == "d" ${size_bits}= Set Variable 0b011 ELSE IF "${size}" == "q" ${size_bits}= Set Variable 0b100 END # Insert size into instruction ${amocas_sized}= Set Variable ${{${amocas_base} | (${size_bits} << 12)}} # Insert rd operand ${amocas_sized_rd}= Set Variable ${{${amocas_sized} | (${rd} << 7)}} # Insert rs1 operand ${amocas_sized_rd_rs1}= Set Variable ${{${amocas_sized_rd} | (${rs1} << 15)}} # Insert rs2 operand ${amocas_complete}= Set Variable ${{${amocas_sized_rd_rs1} | (${rs2} << 20)}} [return] ${amocas_complete} Amocas.${size:(w|d|q)} On ${platform:(RV32|RV64)} ${should:(Should|Shouldn't)} Set Value At ${variable_address} To ${new_value} If Expecting ${expected_value} [Arguments] ... ${rd}=${a0} ... ${rs1}=${a3} ... ${rs2}=${s2} ... ${original_value_upper}=0x2badc00010ffb0ba ... ${original_value_lower}=${expected_64} # Place value in memory. Execute Command sysbus WriteQuadWord ${variable_address} ${original_value_lower} Execute Command sysbus WriteQuadWord ${${variable_address} + 8} ${original_value_upper} ${cpu}= Get Cpu On ${platform} # Construct amocas instruction. ${MACHINE_CODE_AMOCAS}= Assemble Amocas.${size} ${rd} ${rs2} ${rs1} IF "${size}" == "w" # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${ORIGINAL_VALUE_MASKED}= Set Variable ${{str(${original_value_lower} & 0xFFFFFFFF)}} ELSE IF "${size}" == "d" ${ORIGINAL_VALUE_MASKED}= Set Variable ${original_value_lower} ELSE IF "${size}" == "q" # str necessary since robot's XML-RPC library doesn't support >32-bit integers ${ORIGINAL_VALUE_MASKED}= Set Variable "${{str((${original_value_upper} << 64) | ${original_value_lower})}}" END # Place machine code at PC. Execute Command sysbus WriteDoubleWord ${PROGRAM_COUNTER} ${MACHINE_CODE_AMOCAS} # Set operand values. Amocas.${size} Set Register ${rd} On ${platform} To ${expected_value} Amocas.${size} Set Register ${rs1} On ${platform} To ${variable_address} Amocas.${size} Set Register ${rs2} On ${platform} To ${new_value} # Remember previous value. ${result_upper_original_value}= Execute Command ${cpu} GetRegister ${${rd} + 1} ${rs2_upper_original_value}= Execute Command ${cpu} GetRegister ${${rs2} + 1} # Perform amocas. Execute Command ${cpu} Step IF "${rd}" != "0" # After amocas, rd register should have the original memory value (before the cas)... Amocas.${size} Register ${rd} On ${platform} Should Contain ${ORIGINAL_VALUE_MASKED} ELSE IF ("${platform}" == "RV32" and "${size}" == "d") or ("${platform}" == "RV64" and "${size}" == "q") # Unless rd is x0, in which case the original memory value is discarded and neither result register is written. # Ensure rd+1 isn't written to. Register Should Be Equal ${${rd} + 1} ${result_upper_original_value} cpuName=${cpu} END # and the others should remain unchanged IF "${rs2}" != "0" Amocas.${size} Register ${rs2} On ${platform} Should Contain ${new_value} ELSE IF ("${platform}" == "RV32" and "${size}" == "d") or ("${platform}" == "RV64" and "${size}" == "q") # Unless rs2 is x0, in which case rs2+1 is interpreted as 0 no matter its contents. # Ensure rs2+1 remains unchanged. Register Should Be Equal ${${rs2} + 1} ${rs2_upper_original_value} cpuName=${cpu} END Register Should Be Equal ${rs1} ${variable_address} cpuName=${cpu} IF "${should}" == "Should" # Now value in memory should have been set. Amocas.${size} Memory Location ${variable_address} Should Now Be Set To ${new_value} ELSE # Value in memory should remain unchanged. Amocas.${size} Memory Location ${variable_address} Should Now Be Set To ${ORIGINAL_VALUE_MASKED} END *** Test Cases *** # w should tests Amocas.w On RV64 Should Set Value At Single-Page Memory Location Amocas.w On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${expected_32} Amocas.w On RV64 Should Set Value At Page-Spanning Memory Location Amocas.w On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_32} If Expecting ${expected_32} Amocas.w On RV64 Should Set Value At MMIO Memory Location Amocas.w On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_32} If Expecting ${expected_32} Amocas.w On RV32 Should Set Value At Single-Page Memory Location Amocas.w On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${expected_32} # w shouldn't tests Amocas.w On RV64 Shouldn't Set Value At Single-Page Memory Location Amocas.w On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32} Amocas.w On RV64 Shouldn't Set Value At Page-Spanning Memory Location Amocas.w On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32} Amocas.w On RV64 Shouldn't Set Value At MMIO Memory Location Amocas.w On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32} Amocas.w On RV32 Shouldn't Set Value At Single-Page Memory Location Amocas.w On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32} # d should tests Amocas.d On RV64 Should Set Value At Single-Page Memory Location Amocas.d On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64} Amocas.d On RV32 Should Set Value At Single-Page Memory Location Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64} Amocas.d On RV32 Should Handle Zero Source Register # Place value in rs2+1 which should be ignored. Execute Command cpu32 SetRegister ${${x0} + 1} ${wrong_expected_32} Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To 0 If Expecting ${expected_64} ... rs2=${x0} Amocas.d On RV32 Should Handle Zero Destination Register # Place value in rd+1 which shouldn't be overwritten. Execute Command cpu32 SetRegister ${${x0} + 1} ${wrong_expected_32} Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting 0 ... rd=${x0} ... original_value_lower=0 Amocas.d On RV64 Should Set Value At Page-Spanning Memory Location Amocas.d On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_64} If Expecting ${expected_64} Amocas.d On RV64 Should Set Value At MMIO Memory Location Amocas.d On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_64} If Expecting ${expected_64} # d shouldn't tests Amocas.d On RV64 Shouldn't Set Value At Single-Page Memory Location Amocas.d On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64} Amocas.d On RV32 Shouldn't Set Value At Single-Page Memory Location Amocas.d On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64} Amocas.d On RV64 Shouldn't Set Value At Page-Spanning Memory Location Amocas.d On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64} Amocas.d On RV64 Shouldn't Set Value At MMIO Memory Location Amocas.d On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64} # q should tests Amocas.q On RV64 Should Set Value At Single-Page Memory Location Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${expected_128} Amocas.q On RV64 Should Set Value At Page-Spanning Memory Location Amocas.q On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_128} If Expecting ${expected_128} Amocas.q On RV64 Should Set Value At MMIO Memory Location Amocas.q On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_128} If Expecting ${expected_128} Amocas.q On RV64 Should Handle Zero Source Register # Place value in rs2+1 which should be ignored. Execute Command cpu SetRegister ${${x0} + 1} ${wrong_expected_32} Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To 0 If Expecting ${expected_128} ... rs2=${x0} Amocas.q On RV64 Should Handle Zero Destination Register # Place value in rd+1 which shouldn't be overwritten. Execute Command cpu SetRegister ${${x0} + 1} ${wrong_expected_32} Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting 0 ... rd=${x0} ... original_value_lower=0 ... original_value_upper=0 # q shouldn't tests Amocas.q On RV64 Shouldn't Set Value At Single-Page Memory Location Amocas.q On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128} Amocas.q On RV64 Shouldn't Set Value At Page-Spanning Memory Location Amocas.q On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128} Amocas.q On RV64 Shouldn't Set Value At MMIO Memory Location Amocas.q On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128} # illegal instructions Amocas.d On RV32 Using Odd Registers Should Throw Illegal Instruction ${odd_rd}= Set Variable ${${a0} + 1} ${odd_rs2}= Set Variable ${${s2} + 1} Amocas.d On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64} ... rd=${odd_rd} ... rs2=${odd_rs2} Amocas.d ${odd_rd} ${odd_rs2} ${a3} On RV32 Should Throw Illegal Instruction Amocas.q On RV64 Using Odd Registers Should Throw Illegal Instruction ${odd_rd}= Set Variable ${${a0} + 1} ${odd_rs2}= Set Variable ${${s2} + 1} Amocas.q On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${expected_128} ... rd=${odd_rd} ... rs2=${odd_rs2} Amocas.q ${odd_rd} ${odd_rs2} ${a3} On RV64 Should Throw Illegal Instruction