blob: 54ebceedb05bc612ec29b3304390f7d06289497d [file] [log] [blame]
*** Variables ***
${PLATFORM} SEPARATOR=
... """ ${\n}
... cpu: CPU.ARMv7A @ sysbus ${\n}
... ${SPACE*4}cpuType: "cortex-a9" ${\n}
... ${\n}
... pmu: Miscellaneous.ArmPerformanceMonitoringUnit @ { ${\n}
... ${SPACE*8}cpu; ${\n}
... ${SPACE*8}sysbus new Bus.BusRangeRegistration { ${\n}
... ${SPACE*8}${SPACE*4}address: ${MMIO_ADDRESS}; ${\n}
... ${SPACE*8}${SPACE*4}size: 0x1000 ${\n}
... ${SPACE*8}} ${\n}
... ${SPACE*4}} ${\n}
... ${SPACE*4}peripheralId: ${PERIPHERAL_ID} ${\n}
... ${SPACE*4}withProcessorIdMMIORegisters: true ${\n}
... ${\n}
... memory: Memory.MappedMemory @ sysbus 0x0 ${\n}
... ${SPACE*4}size: 0x20000 ${\n}
... """
${BOGUS_EVENT_1} 0x1
${BOGUS_EVENT_2} 0xAB
${SOFTWARE_INCREMENT_EVENT} 0x00
${INSTRUCTIONS_EVENT} 0x8
${CYCLES_EVENT} 0x11
${CPU_MIDR} 0x410fc090
${MMIO_ADDRESS} 0xF0000000
${MMIO_SOFTWARE_LOCK_KEY} 0xC5ACCE55
${PERIPHERAL_ID} 0xFEDCBA9876543210
${REG_ID_PFR1_OFFSET} 0xD24
${REG_MIDR_OFFSET} 0xD00
${REG_MPUIR_OFFSET} 0xD10
${REG_PMCCNTR_OFFSET} 0x07C
${REG_PMCNTENSET_OFFSET} 0xC00
${REG_PMCR_OFFSET} 0xE04
${REG_PMLAR_OFFSET} 0xFB0 # MMIO-only register
${REG_PMPID0_OFFSET} 0xFE0
${REG_PMPID2_OFFSET} 0xFE8
${REG_PMPID4_OFFSET} 0xFD0
${REG_PMSWINC_OFFSET} 0xCA0
${REG_PMXEVCNTR0_OFFSET} 0x000
${REG_PMXEVCNTR30_OFFSET} 0x078
${REG_PMXEVTYPER0_OFFSET} 0x400
${REG_PMXEVTYPER30_OFFSET} 0x478
${REG_TLBTR_OFFSET} 0xD0C
*** Keywords ***
Create Machine
# Some keywords expect numbers to be printed as hex.
Execute Command numbersMode Hexadecimal
Execute Command using sysbus
Execute Command mach create
Execute Command machine LoadPlatformDescriptionFromString ${PLATFORM}
# Create infinite loop
Execute Command sysbus WriteDoubleWord 0x1000 0xE320F000 # nop
Execute Command sysbus WriteDoubleWord 0x1004 0xEAFFFFFD # b to 0x1000
# Set predefined PerformanceInMips, so it's possible to calculate
# the number of expected instructions to execute in the given time frame
Execute Command cpu PerformanceInMips 100
Execute Command cpu PC 0x1000
Set Register Bits
[Arguments] ${regName} ${value}
${enabledCounters}= Execute Command cpu.pmu GetRegister ${regName}
${mask}= Evaluate hex(int($enabledCounters, 16) | int($value))
Execute Command cpu.pmu SetRegister ${regName} ${mask}
Clear Register Bits
[Arguments] ${regName} ${value}
${enabledCounters}= Execute Command cpu.pmu GetRegister ${regName}
${mask}= Evaluate hex(int($enabledCounters, 16) & ~int($value))
Execute Command cpu.pmu SetRegister ${regName} ${mask}
Assert Bit Set
[Arguments] ${value} ${bit}
${isSet}= Evaluate (${value} & (1 << ${bit})) > 0
Should Be True ${isSet}
Assert Bit Unset
[Arguments] ${value} ${bit}
${isNotSet}= Evaluate (${value} & (1 << ${bit})) == 0
Should Be True ${isNotSet}
Enable PMU
Set Register Bits "PMCR" 1
Disable PMU
Clear Register Bits "PMCR" 1
Reset PMU Counters
Set Register Bits "PMCR" 2
Reset PMU Cycle Counter
Set Register Bits "PMCR" 4
Set Cycles Divisor 64
[Arguments] ${divisor}
IF ${divisor}
Set Register Bits "PMCR" 8
ELSE
Clear Register Bits "PMCR" 8
END
Switch Privilege Mode
[Arguments] ${privileged}
# use CPSR to switch between PL0/PL1
IF ${privileged} # PL1 - SVC mode
${cpsr}= Execute Command cpu CPSR
${cpsr}= Evaluate (int(${cpsr}) & ~0x1F ) | 0x13
Execute Command cpu CPSR ${cpsr}
ELSE # PL0
${cpsr}= Execute Command cpu CPSR
${cpsr}= Evaluate (int(${cpsr}) & ~0x1F ) | 0x10
Execute Command cpu CPSR ${cpsr}
END
Enable PMU Counter
[Arguments] ${counter}
${value}= Evaluate (1 << int($counter))
Set Register Bits "PMCNTENSET" ${value}
Disable PMU Counter
[Arguments] ${counter}
${value}= Evaluate (1 << int($counter))
Execute Command cpu.pmu SetRegister "PMCNTENCLR" ${value}
Enable Overflow Interrupt For PMU Counter
[Arguments] ${counter}
${value}= Evaluate (1 << int($counter))
Execute Command cpu.pmu SetRegister "PMINTENSET" ${value}
Disable Overflow Interrupt For PMU Counter
[Arguments] ${counter}
${value}= Evaluate (1 << int($counter))
Execute Command cpu.pmu SetRegister "PMINTENCLR" ${value}
Increment Software PMU Counter
[Arguments] ${counter}
${value}= Evaluate (1 << int($counter))
Execute Command cpu.pmu SetRegister "PMSWINC" ${value}
Assert PMU Counter Is Equal To
[Arguments] ${counter} ${value}
${cnt}= Execute Command cpu.pmu GetCounterValue ${counter}
Should Be Equal As Integers ${cnt} ${value}
Assert Executed Instructions Equal To
[Arguments] ${value}
${executedInstructions}= Execute Command cpu ExecutedInstructions
Should Be Equal As Integers ${executedInstructions} ${value}
Assert PMU Cycle Counter Equal To
[Arguments] ${value}
${cycles}= Execute Command cpu.pmu GetCycleCounterValue
Should Be Equal As Integers ${cycles} ${value}
Assert PMU IRQ Is Set
${irqState}= Execute Command cpu.pmu IRQ
Should Contain ${irqState} GPIO: set
Assert PMU IRQ Is Unset
${irqState}= Execute Command cpu.pmu IRQ
Should Contain ${irqState} GPIO: unset
Assert PMU Counter Overflowed
[Arguments] ${counter}
# n-th bit denotes overflow status for the n-th PMU counter
${overflowStatus}= Execute Command cpu.pmu GetRegister "PMOVSR"
Assert Bit Set ${overflowStatus} ${counter}
Assert PMU Counter Not Overflowed
[Arguments] ${counter}
${overflowStatus}= Execute Command cpu.pmu GetRegister "PMOVSR"
Assert Bit Unset ${overflowStatus} ${counter}
Assert Command Output Equal To
[Arguments] ${expectedOutput} ${command}
${output}= Execute Command ${command}
Should Be Equal ${output} ${expectedOutput} strip_spaces=True
Get ${name} Register Offset
${variableName}= Set Variable ${{ "REG_" + "${name}" + "_OFFSET" }}
[Return] ${ ${variableName} }
MMIO-Accessed ${name} Should Be Equal To Value ${expectedValue}
${offset}= Get ${name} Register Offset
${output}= Execute Command sysbus ReadDoubleWord ${{ ${MMIO_ADDRESS} + ${offset} }}
Should Be Equal As Integers ${output} ${expectedValue}
MMIO-Accessed ${name} Should Be Equal To System Register
# There are no direct PMXEVCNTR and PMXEVTYPER system registers for all counters but only
# two such registers depending on PMSELR so PMU wrapping methods have to be used instead.
IF "${name}".startswith("PMXEVCNTR")
${expectedValue}= Execute Command cpu.pmu GetCounterValue ${{ int("${name}".replace("PMXEVCNTR", "")) }}
ELSE IF "${name}".startswith("PMXEVTYPER")
${expectedValue}= Execute Command cpu.pmu GetCounterEvent ${{ int("${name}".replace("PMXEVTYPER", "")) }}
ELSE
${expectedValue}= Execute Command cpu GetSystemRegisterValue "${name}"
END
MMIO-Accessed ${name} Should Be Equal To Value ${expectedValue}
Write ${value} To ${name} Using MMIO
${offset}= Get ${name} Register Offset
Execute Command sysbus WriteDoubleWord ${{ ${MMIO_ADDRESS} + ${offset} }} ${value}
Unlock MMIO Writes
Write ${MMIO_SOFTWARE_LOCK_KEY} To PMLAR Using MMIO
*** Test Cases ***
Should Count Cycles
Create Machine
Enable PMU
Enable PMU Counter 31 # Cycle counter
Execute Command emulation RunFor "00:00:01.12"
# Given a known PerformanceInMIPS, it can be assumed that 112 000 000 instructions have been executed
Assert PMU Cycle Counter Equal To 112 000 000
Assert Executed Instructions Equal To 112 000 000
Should Count Cycles With Divisor
Create Machine
Enable PMU Counter 31 # Cycle counter
Enable PMU
Set Cycles Divisor 64 ${True}
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 1000000
Assert PMU Cycle Counter Equal To 15625
Set Cycles Divisor 64 ${False}
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 2000000
Assert PMU Cycle Counter Equal To 1015625
Should Program PMU Counter To Count Cycles
Create Machine
Enable PMU
Execute Command cpu.pmu SetCounterEvent 0 ${CYCLES_EVENT}
Enable PMU Counter 0
Execute Command emulation RunFor "00:00:01.12"
# As above, given PerformanceInMIPS, we know how many instructions to expect
# One cycle is equal to one instruction, this is not a mistake
Assert Executed Instructions Equal To 112 000 000
Assert PMU Counter Is Equal To 0 112 000 000
# Now, an instruction should be equal to 1.25 cycles
Execute Command cpu CyclesPerInstruction 1.25
Execute Command emulation RunFor "00:00:00.01"
# Executed 1 000 000 instructions, so 1 250 000 cycles
Assert Executed Instructions Equal To 113 000 000
Assert PMU Counter Is Equal To 0 113 250 000
Should Program PMU Counter To Count Instructions
Create Machine
# Cycles value will be used in dependent tests
Enable PMU Counter 31
Enable PMU
Execute Command cpu.pmu SetCounterEvent 0 ${INSTRUCTIONS_EVENT}
Enable PMU Counter 0
Execute Command emulation RunFor "00:00:01.12"
Assert Executed Instructions Equal To 112 000 000
Assert PMU Counter Is Equal To 0 112 000 000
Provides program-counter
Should Reset PMU counters
Requires program-counter
Assert PMU Counter Is Equal To 0 112 000 000
Reset PMU Counters
Assert PMU Counter Is Equal To 0 0
Assert PMU Cycle Counter Equal To 112 000 000
Reset PMU Cycle Counter
Assert PMU Cycle Counter Equal To 0
Should Kick Software Increment
Create Machine
# Configure PMU counters, only Counter 0 subscribes to Software Increment event, Counter 1 subscribes to non-implemented event
# So only Counter 0 is expected to be incremented
Execute Command cpu.pmu SetCounterEvent 0 ${SOFTWARE_INCREMENT_EVENT}
Execute Command cpu.pmu SetCounterEvent 1 ${BOGUS_EVENT_1}
# Verify the configured events by reading their event ids
${ev1}= Execute Command cpu.pmu GetCounterEvent 1
${ev0}= Execute Command cpu.pmu GetCounterEvent 0
Should Be Equal As Integers ${ev1} 1
Should Be Equal As Integers ${ev0} 0
Increment Software PMU Counter 0
Increment Software PMU Counter 1
# Counters and PMU are disabled, should not count
Assert PMU Counter Is Equal To 0 0
Assert PMU Counter Is Equal To 1 0
Enable PMU Counter 0
Increment Software PMU Counter 1
Increment Software PMU Counter 0
# Still not counting, PMU is not enabled
Assert PMU Counter Is Equal To 0 0
Assert PMU Counter Is Equal To 1 0
Enable PMU
Increment Software PMU Counter 0
Increment Software PMU Counter 1
# PMU Counter 1 is not a Software Increment, so it shouldn't increment at all
# Counter 0 is incremented only once, after "Enable PMU". Previous increments were invalid, since PMU was disabled
Assert PMU Counter Is Equal To 0 1
Assert PMU Counter Is Equal To 1 0
Should Respect PMU Counter Pasue And Resume
Create Machine
Enable PMU
Execute Command cpu.pmu SetCounterEvent 1 ${CYCLES_EVENT}
Enable PMU Counter 1
Execute Command emulation RunFor "00:00:00.01"
Assert PMU Counter Is Equal To 1 1000000
Assert Executed Instructions Equal To 1000000
# Shouldn't count with disabled PMU
Disable PMU
Execute Command emulation RunFor "00:00:00.01"
Assert PMU Counter Is Equal To 1 1000000
Assert Executed Instructions Equal To 2000000
Enable PMU
Execute Command emulation RunFor "00:00:00.01"
Assert PMU Counter Is Equal To 1 2000000
Assert Executed Instructions Equal To 3000000
# Shouldn't count when the counter is disabled
Disable PMU Counter 1
Execute Command emulation RunFor "00:00:00.01"
Assert PMU Counter Is Equal To 1 2000000
Assert Executed Instructions Equal To 4000000
Enable PMU Counter 1
Execute Command emulation RunFor "00:00:00.01"
Assert PMU Counter Is Equal To 1 3000000
Assert Executed Instructions Equal To 5000000
Should Trigger Cycles Overflow
Create Machine
Enable PMU
## Counter 2
# The performance in MIPS is known, so it's possible to calculate the exact moment the counter should overflow
# Configure counter, so after 3 000 000 instructions it should have overflowed and have the value 2 stored
# so it's set to "UINT32_MAX - value + 3"
Execute Command cpu.pmu SetCounterEvent 2 ${CYCLES_EVENT}
Execute Command cpu.pmu SetCounterValue 2 0xFFD23942
Enable Overflow Interrupt For PMU Counter 2
Enable PMU Counter 2
## Counter 1
Execute Command cpu.pmu SetCounterEvent 1 ${CYCLES_EVENT}
# expected to execute 1 000 000 instructions, so load "UINT32_MAX - value" to counter
# it is expected to overflow one instruction after
Execute Command cpu.pmu SetCounterValue 1 0xFFF0BDBF
Enable Overflow Interrupt For PMU Counter 1
Enable PMU Counter 1
# See that it didn't overflow too soon
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 1000000
# The value is counter's base value "0xFFF0BDBF" + 1 000 000 expected instructions to execute
Assert PMU Counter Is Equal To 1 0xFFFFFFFF
Assert PMU Counter Not Overflowed 1
Assert PMU IRQ Is Unset
# It will overflow 1 instruction after, we now execute 100 000, so overflow bit has to be set
Execute Command emulation RunFor "00:00:00.001"
Assert PMU Counter Overflowed 1
Assert PMU IRQ Is Set
Provides cycles-overflow
Should Resume Execution After Cycles Overflow
Requires cycles-overflow
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 2100000
# instructions are counted from 0 after overflow, so instead from 1 000 000 + 100 000 subtract 1
Assert PMU Counter Is Equal To 1 1099999
Provides resumed-after-overflow
Should Overflow Second Time
Requires resumed-after-overflow
# Clear overflow bit for counter 1
Execute Command cpu.pmu SetRegister "PMOVSR" 0x2
Execute Command emulation RunFor "00:00:00.009"
Assert Executed Instructions Equal To 3000000
Assert PMU Counter Is Equal To 2 2
Assert PMU Counter Overflowed 2
Should Increment Bogus Event From Monitor
# The event is unimplemented, and there will be warnings in the logs
# but still can be used it in a test scenario
Create Machine
Enable PMU
Execute Command cpu.pmu SetCounterEvent 1 ${BOGUS_EVENT_2}
Enable PMU Counter 1
Execute Command cpu.pmu BroadcastEvent ${BOGUS_EVENT_2} 5
Assert PMU Counter Is Equal To 1 5
# now, let's overflow
Enable Overflow Interrupt For PMU Counter 1
Execute Command cpu.pmu BroadcastEvent ${BOGUS_EVENT_2} 0xFFFFFFFF
Assert PMU Counter Is Equal To 1 4
Assert PMU Counter Overflowed 1
Assert PMU IRQ Is Set
Should Count Instructions With PL Masking
Create Machine
Enable PMU Counter 0
Execute Command cpu.pmu SetCounterEvent 0 ${INSTRUCTIONS_EVENT} ignoreCountAtPL0=false ignoreCountAtPL1=true
Enable PMU
Enable Overflow Interrupt For PMU Counter 0
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 1000000
# The counter doesn't count at PL1, so should be zero
Assert PMU Counter Is Equal To 0 0
Switch Privilege Mode ${False}
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 2000000
# The PMU counter only counted in PL0, so only 1 000 000 instructions
Assert PMU Counter Is Equal To 0 1000000
Execute Command cpu.pmu SetCounterEvent 0 ${INSTRUCTIONS_EVENT} ignoreCountAtPL0=true ignoreCountAtPL1=true
# Now, counting at PL0 is disabled too, so PMU counter should not progress
Execute Command emulation RunFor "00:00:00.01"
Assert Executed Instructions Equal To 3000000
Assert PMU Counter Is Equal To 0 1000000
# See that the counter doesn't count and doesn't trigger overflow
# Configure counter, so after 3 000 000 instructions it should have overflowed and have the value 2 stored
# so it's set to "UINT32_MAX - value + 3"
# But it shouldn't count anything at PL0
Execute Command cpu.pmu SetCounterValue 0 0xFFD23942
Execute Command emulation RunFor "00:00:00.03"
Assert Executed Instructions Equal To 6000000
# No progress for the couner
Assert PMU Counter Is Equal To 0 0xFFD23942
Assert PMU Counter Not Overflowed 0
Assert PMU IRQ Is Unset
# Now, switch the mode back to PL1 and enable counting events there
Execute Command cpu.pmu SetCounterEvent 0 ${INSTRUCTIONS_EVENT} ignoreCountAtPL0=true ignoreCountAtPL1=false
Switch Privilege Mode ${True}
# The counter hadn't progressed at all, so it's not necessary to set it's value again
Execute Command emulation RunFor "00:00:00.03"
Assert Executed Instructions Equal To 9000000
Assert PMU Counter Is Equal To 0 2
Assert PMU Counter Overflowed 0
Assert PMU IRQ Is Set
Should Allow MMIO Writes Only After Disabling Software Lock
Create Machine
Create Log Tester 0
Execute Command logLevel -1 pmu
Assert Command Output Equal To True pmu SoftwareLockEnabled
# Try writing.
Write 0xAB To PMXEVTYPER0 Using MMIO
Wait For Log Entry write ignored
Wait For Log Entry Software Lock can be cleared by writing ${MMIO_SOFTWARE_LOCK_KEY} to the PMLAR register at ${REG_PMLAR_OFFSET}
MMIO-Accessed PMXEVTYPER0 Should Be Equal To Value 0
# Try unlocking with invalid key.
${invalidUnlockKey}= Set Variable 0xABCD
Write ${invalidUnlockKey} To PMLAR Using MMIO
Wait For Log Entry Tried to disable Software Lock with invalid value ${invalidUnlockKey}, should be ${MMIO_SOFTWARE_LOCK_KEY}
Assert Command Output Equal To True pmu SoftwareLockEnabled
# Unlock.
Unlock MMIO Writes
Wait For Log Entry Software Lock disabled
Assert Command Output Equal To False pmu SoftwareLockEnabled
# Write again.
Write ${BOGUS_EVENT_1} To PMXEVTYPER0 Using MMIO
Should Not Be In Log write ignored
Wait For Log Entry cpu: Invalid/Unimplemented event ${BOGUS_EVENT_1} selected for PMU counter 0
MMIO-Accessed PMXEVTYPER0 Should Be Equal To Value ${BOGUS_EVENT_1}
Should Kick Software Incremented Counters Using MMIO
Create Machine
Unlock MMIO Writes
# Enable PMU.
Write 0x1 To PMCR Using MMIO
# Set counters 0 and 30 to software increment event and enable them.
Write ${SOFTWARE_INCREMENT_EVENT} To PMXEVTYPER0 Using MMIO
Write ${SOFTWARE_INCREMENT_EVENT} To PMXEVTYPER30 Using MMIO
Write ${{ (1 << 30) | 1 }} To PMCNTENSET Using MMIO
# Increment counters using both MMIO and System Registers.
Write 1 To PMSWINC Using MMIO
Write ${{ 1 << 30 }} To PMSWINC Using MMIO
Increment Software PMU Counter 30
# Make sure the MMIO-accessed count is valid and equal to system registers.
MMIO-Accessed PMXEVCNTR0 Should Be Equal To Value 1
MMIO-Accessed PMXEVCNTR0 Should Be Equal To System Register
MMIO-Accessed PMXEVCNTR30 Should Be Equal To Value 2
MMIO-Accessed PMXEVCNTR30 Should Be Equal To System Register
Should Read Peripheral ID Using MMIO
Create Machine
${peripheralId}= Execute Command pmu PeripheralId
${peripheralId}= Strip String ${peripheralId}
# Peripheral ID's bits 20-23 should contain variant from bits 20-23 of MIDR.
Should Be Equal As Integers ${{ ((${peripheralId}^${CPU_MIDR}) >> 20) & 0xF }} 0
# Each of PMPID0-PMPID7 contains 8 bits from PeripheralId.
MMIO-Accessed PMPID0 Should Be Equal To Value ${{ ${peripheralId} & 0xFF }}
MMIO-Accessed PMPID2 Should Be Equal To Value ${{ (${peripheralId} >> 16) & 0xFF }}
MMIO-Accessed PMPID4 Should Be Equal To Value ${{ (${peripheralId} >> 32) & 0xFF }}
Should Read Processor ID System Registers Using MMIO
Create Machine
MMIO-Accessed ID_PFR1 Should Be Equal To System Register
MMIO-Accessed MIDR Should Be Equal To System Register
# MIDR is read for MPUIR because Cortex-A9 doesn't have MPU.
MMIO-Accessed MPUIR Should Be Equal To Value ${CPU_MIDR}
MMIO-Accessed TLBTR Should Be Equal To System Register
Should Read PMU Registers Using MMIO
# Let's use a saved state with enabled cycle counter and counter 0 counting instructions.
Requires program-counter
# Compare PMU registers used in the case providing `program-counter` state.
MMIO-Accessed PMXEVCNTR0 Should Be Equal To System Register
MMIO-Accessed PMXEVTYPER0 Should Be Equal To System Register
MMIO-Accessed PMCCNTR Should Be Equal To System Register
MMIO-Accessed PMCNTENSET Should Be Equal To System Register
MMIO-Accessed PMCR Should Be Equal To System Register