1*** Variables ***
2${a0}                               0xA
3${r0}                               0x0
4${r1}                               0x1
5${ASSEMBLY_ADDRESS}                 0x0
6${X_CHAR}                           120
7${ARM_UART_DATA_ADDRESS}            0x2000
8${WATCHPOINT_ADDRESS}               0x100
9${ARM_PLATFORM}                     SEPARATOR=\n
10...                                 """
11...                                 cpu: CPU.ARMv7A @ sysbus
12...                                 ${SPACE*4}cpuType: "cortex-a9"
13...
14...                                 mem: Memory.MappedMemory @ sysbus 0x0
15...                                 ${SPACE*4}size: 0x1000
16...
17...                                 uart: UART.TrivialUart @ sysbus 0x2000
18...                                 """
19${ARM64_PLATFORM}                   SEPARATOR=\n
20...                                 """
21...                                 cpu: CPU.ARMv8A @ sysbus
22...                                 ${SPACE*4}cpuType: "cortex-a53"
23...                                 ${SPACE*4}genericInterruptController: gic
24...
25...                                 mem: Memory.MappedMemory @ sysbus 0x0
26...                                 ${SPACE*4}size: 0x1000
27...
28...                                 gic: IRQControllers.ARM_GenericInterruptController @ {
29...                                 ${SPACE*8}sysbus new Bus.BusMultiRegistration { address: 0x8000000; size: 0x010000; region: "distributor" };
30...                                 ${SPACE*8}sysbus new Bus.BusMultiRegistration { address: 0x8010000; size: 0x010000; region: "cpuInterface" }
31...                                 ${SPACE*4}}
32...                                 ${SPACE*4}\[0-1\] -> cpu@\[0-1\]
33...                                 ${SPACE*4}architectureVersion: IRQControllers.ARM_GenericInterruptControllerVersion.GICv2
34...                                 ${SPACE*4}supportsTwoSecurityStates: true
35...                                 """
36${RISCV_PLATFORM}                   SEPARATOR=\n
37...                                 """
38...                                 cpu: CPU.RiscV32 @ sysbus
39...                                 ${SPACE*4}timeProvider: clint
40...                                 ${SPACE*4}cpuType: "rv32gc"
41...
42...                                 mem: Memory.MappedMemory @ sysbus 0x0
43...                                 ${SPACE*4}size: 0x100000
44...
45...                                 clint: IRQControllers.CoreLevelInterruptor @ sysbus 0x44000000
46...                                 ${SPACE*4}frequency: 66000000
47...                                 """
48
49*** Keywords ***
50Create Platform
51    [Arguments]                     ${platform}  ${assembly}
52    Execute Command                 using sysbus
53    Execute Command                 mach create
54    Execute Command                 machine LoadPlatformDescriptionFromString ${platform}
55    Execute Command                 cpu AssembleBlock ${ASSEMBLY_ADDRESS} "${assembly}"
56    Execute Command                 cpu PC ${ASSEMBLY_ADDRESS}
57
58Expect Instructions Count
59    [Arguments]                     ${expected_count}
60    ${insn_count}=                  Execute Command  cpu ExecutedInstructions
61    Should Be Equal As Numbers      ${insn_count}  ${expected_count}
62
63Expect PC
64    [Arguments]                     ${expected_pc}
65    ${pc}=                          Execute Command  cpu PC
66    Should Be Equal As Numbers      ${pc}  ${expected_pc}
67
68Repeat String
69    [Arguments]                     ${str}  ${count}
70    ${rep_str}=                     Set Variable  ${EMPTY}
71    FOR  ${i}  IN RANGE  0  ${count}
72        ${rep_str}=                     Catenate  SEPARATOR=${EMPTY}  ${rep_str}  ${str}
73    END
74    [return]                        ${rep_str}
75
76Surround Assembly Block With Nops
77    [Arguments]                     ${inner_assembly}  ${preceeding_nops}  ${following_nops}
78    ${start_nops}=                  Repeat String  nop;  ${preceeding_nops}
79    ${end_nops}=                    Repeat String  nop;  ${following_nops}
80    ${assembly}=                    Catenate  SEPARATOR=${EMPTY}  ${start_nops}  ${inner_assembly}  ${end_nops}
81    [return]                        ${assembly}
82
83Execute Instructions
84    [Arguments]                     ${instructions_count}
85    ${time_interval}=               evaluate  ${instructions_count} / 1000000
86    ${time_interval}=               Format String  {0:.6f}  ${time_interval}
87    Execute Command                 sysbus.cpu PerformanceInMips 1
88    Execute Command                 emulation RunFor "${time_interval}"
89
90*** Test Cases ***
91Should Have Correct Instructions Count On Translation Block End
92    [Tags]                          instructions_counting
93    ${assembly}=                    Surround Assembly Block With Nops  ${EMPTY}  10  0
94    Create Platform                 ${ARM64_PLATFORM}  ${assembly}
95    Execute Command                 cpu MaximumBlockSize 7
96    Execute Command                 sysbus.cpu SetHookAtBlockEnd "cpu.Log(LogLevel.Info, 'BlockEnd Hook: Executed {0} Instructions', cpu.ExecutedInstructions)"
97    Create Log Tester               1
98    Wait For Log Entry              BlockEnd Hook: Executed 7 Instructions  pauseEmulation=true
99
100Should Have Correct Instructions Count After Multiple Translation Blocks
101    [Tags]                          instructions_counting
102    ${assembly}=                    Surround Assembly Block With Nops  bl -0x24;  9  0
103    Create Platform                 ${ARM64_PLATFORM}  ${assembly}
104    Execute Command                 sysbus.cpu SetHookAtBlockEnd "cpu.Log(LogLevel.Info, 'BlockEnd hook at PC: {} with {} executed instructions'.format(cpu.PC, cpu.ExecutedInstructions))"
105    Create Log Tester               1
106    Execute Instructions            100
107    FOR  ${i}  IN RANGE  1  10
108        Wait For Log Entry              BlockEnd hook at PC: 0x0 with ${i * 10} executed instructions  timeout=0
109    END
110
111Should Have Correct Instructions Count On Execute Instructions
112    [Tags]                          instructions_counting
113    ${assembly}=                    Surround Assembly Block With Nops  ${EMPTY}  10  0
114    Create Platform                 ${ARM64_PLATFORM}  ${assembly}
115    Execute Instructions            8
116    Expect Instructions Count       8
117
118Should Have Correct Instructions Count On WFI
119    [Tags]                          instructions_counting
120    ${assembly}=                    Surround Assembly Block With Nops  wfi;  3  0
121    Create Platform                 ${ARM64_PLATFORM}  ${assembly}
122    Execute Command                 cpu AddHookAtWfiStateChange 'self.Log(LogLevel.Info, "ENTER WFI - instructions count = {}".format(self.ExecutedInstructions))'
123    Create Log Tester               1
124    Start Emulation
125
126    Wait For Log Entry              ENTER WFI - instructions count = 4  pauseEmulation=true
127
128Should Have Correct Instructions Count On MMU External Fault
129    [Tags]                          instructions_counting
130    ${assembly}=                    Surround Assembly Block With Nops  lw a1, 0(a0);  4  0
131    Create Platform                 ${RISCV_PLATFORM}  ${assembly}
132    Execute Command                 cpu EnableExternalWindowMmu true
133    Execute Command                 cpu SetRegister ${a0} 0x100000
134    Create Log Tester               1
135    Wait For Log Entry              MMU fault - the address 0x100000 is not specified in any of the existing ranges
136    Expect Instructions Count       5
137
138Should Have Correct Instructions Count On Read Watchpoint
139    [Tags]                          instructions_counting
140    ${assembly}=                    Surround Assembly Block With Nops  ldrb r0, [r1];  7  4
141    Create Platform                 ${ARM_PLATFORM}  ${assembly}
142    Execute Command                 sysbus.cpu SetRegister ${r1} ${WATCHPOINT_ADDRESS}
143
144    Execute Command                 sysbus AddWatchpointHook ${WATCHPOINT_ADDRESS} 1 Read "cpu.Log(LogLevel.Info, 'Watchpoint hook at PC: {}'.format(cpu.PC))"
145    Execute Command                 sysbus.cpu SetHookAtBlockBegin "cpu.Log(LogLevel.Info, 'BlockBegin hook at PC: {} with {} executed instructions'.format(cpu.PC, cpu.ExecutedInstructions))"
146
147    Create Log Tester               0
148    Expect Instructions Count       0
149
150    Execute Instructions            12
151
152    Wait For Log Entry              Watchpoint hook at PC: 0x1c  timeout=0
153    Wait For Log Entry              BlockBegin hook at PC: 0x1c with 7 executed instructions  timeout=0
154
155    Should Not Be In Log            Watchpoint hook
156    Should Not Be In Log            BlockBegin hook
157
158    Expect PC                       0x30
159    Expect Instructions Count       12
160
161Should Have Correct Instructions Count On Uart Access
162    [Tags]                          instructions_counting
163    ${assembly}=                    Surround Assembly Block With Nops  strb r0, [r1];  7  4
164    Create Platform                 ${ARM_PLATFORM}  ${assembly}
165    Create Terminal Tester          sysbus.uart
166
167    Execute Command                 sysbus.cpu SetRegister ${r0} ${X_CHAR}
168    Execute Command                 sysbus.cpu SetRegister ${r1} ${ARM_UART_DATA_ADDRESS}
169
170    Wait For Prompt On Uart         x  pauseEmulation=true
171
172    Expect PC                       0x20
173    Expect Instructions Count       8
174