1*** Settings ***
2Test Setup                          Create Machine
3
4*** Variables ***
5${MEMORY_START}                     0x80000000
6${PLATFORM_STRING}                  SEPARATOR=\n
7...                                 dram: Memory.MappedMemory @ sysbus ${MEMORY_START} {
8...                                 ${SPACE*4}size: 0x80000000
9...                                 }
10...                                 mmio: Memory.ArrayMemory @ sysbus 0x100000000 {
11...                                 ${SPACE*4}size: 0x10000
12...                                 }
13...                                 mtvec: Memory.MappedMemory @ sysbus 0x1000 { size: 0x40000 }
14...
15...                                 cpu: CPU.RiscV64 @ sysbus {
16...                                 ${SPACE*4}cpuType: "rv64gc_zicsr_zifencei_zacas";
17...                                 ${SPACE*4}hartId: 1;
18...                                 ${SPACE*4}privilegedArchitecture: PrivilegedArchitecture.Priv1_10;
19...                                 ${SPACE*4}timeProvider: empty;
20...                                 ${SPACE*4}CyclesPerInstruction: 8;
21...                                 ${SPACE*4}allowUnalignedAccesses: true
22...                                 }
23...
24...                                 cpu32: CPU.RiscV32 @ sysbus {
25...                                 ${SPACE*4}cpuType: "rv32gc_zicsr_zifencei_zacas";
26...                                 ${SPACE*4}hartId: 2;
27...                                 ${SPACE*4}privilegedArchitecture: PrivilegedArchitecture.Priv1_10;
28...                                 ${SPACE*4}timeProvider: empty;
29...                                 ${SPACE*4}CyclesPerInstruction: 8;
30...                                 ${SPACE*4}allowUnalignedAccesses: true
31...                                 }
32${PROGRAM_COUNTER}                  0x80000000
33${PROGRAM_COUNTER_32}               0x80000100
34${ORDINARY_ADDRESS}                 0x80001000
35${MAX_PAGE_SIZE}                    0x40000000  # 1 GiB
36${PAGE_SPANNING_ADDRESS}            ${{str(${MEMORY_START} + ${MAX_PAGE_SIZE} - 1)}}  # str necessary since robot's XML-RPC library doesn't support >32-bit integers
37${MMIO_ADDRESS}                     0x0000000100001000
38
39${mtvec}                            0x1010
40${illegal_instruction}              0x2
41
42# Registers used
43${x0}                               0
44${a0}                               10
45${a1}                               11
46${a2}                               12
47${a3}                               13
48${a4}                               14
49${s2}                               18
50
51# 32-, 64- and 128-bit constants
52${wrong_expected_128}               0x12345678910111213141516171819202
53${wrong_expected_64}                0x1234567891011
54${wrong_expected_32}                0x1234
55${expected_128}                     0x2badc00010ffb0ba1afedeadbeefd00d
56${expected_64}                      0x1afedeadbeefd00d
57${expected_32}                      0xbeefd00d
58${new_128}                          0x216b00b5d0d0caca1eeff00dbabedead
59${new_64}                           0xbeeff00dbabedead
60${new_32}                           0xbeefbabe
61
62*** Keywords ***
63Create Machine
64    Execute Command                 mach create
65    Execute Command                 machine LoadPlatformDescriptionFromString """${PLATFORM_STRING}"""
66    Execute Command                 cpu ExecutionMode SingleStep
67    Execute Command                 cpu PC ${PROGRAM_COUNTER}
68    Execute Command                 cpu32 ExecutionMode SingleStep
69    Execute Command                 cpu32 PC ${PROGRAM_COUNTER}
70
71Get Cpu On ${platform:(RV32|RV64)}
72    IF  "${platform}" == "RV32"
73        ${cpu}=                         Set Variable  cpu32
74    ELSE IF  "${platform}" == "RV64"
75        ${cpu}=                         Set Variable  cpu
76    END
77    [return]                        ${cpu}
78
79Amocas.${size:(w|d|q)} ${rd} ${rs2} ${rs1} On ${platform:(RV32|RV64)} Should Throw Illegal Instruction
80    ${cpu}=                         Get Cpu On ${platform}
81
82    # Should have jumped to mtvec
83    PC Should Be Equal              ${mtvec}  cpuName=${cpu}
84
85    # The cause should be illegal instruction.
86    ${mcause}=                      Execute Command  ${cpu} MCAUSE
87    Should Be Equal As Numbers      ${mcause}  ${illegal_instruction}
88
89    # MTVAL should be the opcode that caused the fault.
90    ${mtval}=                       Execute Command  ${cpu} MTVAL
91    ${illegal_amocas_opcode}=       Assemble Amocas.${size} ${rd} ${rs2} ${rs1}
92    Should Be Equal As Numbers      ${mtval}  ${illegal_amocas_opcode}
93
94    # MEPC should point to the illegal instruction.
95    ${mepc}=                        Execute Command  ${cpu} MEPC
96    Should Be Equal As Numbers      ${mepc}  ${PROGRAM_COUNTER}
97
98Amocas.${size:(w|d|q)} Memory Location ${address} Should Now Be Set To ${value}
99    IF  "${size}" == "w"
100        ${current_value}=               Execute Command  sysbus ReadDoubleWord ${address}
101    ELSE IF  "${size}" == "d"
102        ${current_value}=               Execute Command  sysbus ReadQuadWord ${address}
103    ELSE IF  "${size}" == "q"
104        ${current_value_lower}=         Execute Command  sysbus ReadQuadWord ${address}
105        ${current_value_upper}=         Execute Command  sysbus ReadQuadWord ${${address} + 8}
106    END
107
108    IF  "${size}" == "q"
109        ${new_value_lower}=             Set Variable  ${{str(int(${value}) & 0xFFFFFFFFFFFFFFFF)}}
110        ${new_value_upper}=             Set Variable  ${{str((int(${value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}}
111        Should Be Equal As Integers     ${current_value_lower}  ${new_value_lower}  "Memory location lower should now be set to ${new_value_lower}"
112        Should Be Equal As Integers     ${current_value_upper}  ${new_value_upper}  "Memory location upper should now be set to ${new_value_upper}"
113    ELSE
114        Should Be Equal As Integers     ${current_value}  ${value}  "Memory location ${address} should now be set to ${value} but it's ${current_value}"
115    END
116
117Amocas.${size:(w|d|q)} Register ${register} On ${platform:(RV32|RV64)} Should Contain ${expected_value}
118    ${cpu}=                         Get Cpu On ${platform}
119
120    IF  "${platform}" == "RV32" and "${size}" == "d"
121        # str necessary since robot's XML-RPC library doesn't support >32-bit integers
122        ${expected_value_lower}=        Set Variable  ${{str(int(${expected_value}) & 0xFFFFFFFF)}}
123        ${expected_value_upper}=        Set Variable  ${{str((int(${expected_value}) >> 32) & 0xFFFFFFFF)}}
124        Register Should Be Equal        ${register}  ${expected_value_lower}  cpuName=${cpu}
125        Register Should Be Equal        ${${register} + 1}  ${expected_value_upper}  cpuName=${cpu}
126    ELSE IF  "${platform}" == "RV64" and "${size}" == "q"
127        # str necessary since robot's XML-RPC library doesn't support >32-bit integers
128        ${expected_value_lower}=        Set Variable  ${{str(int(${expected_value}) & 0xFFFFFFFFFFFFFFFF)}}
129        ${expected_value_upper}=        Set Variable  ${{str((int(${expected_value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}}
130        Register Should Be Equal        ${register}  ${expected_value_lower}  cpuName=${cpu}
131        Register Should Be Equal        ${${register} + 1}  ${expected_value_upper}  cpuName=${cpu}
132    ELSE
133        Register Should Be Equal        ${register}  ${expected_value}  cpuName=${cpu}
134    END
135
136Amocas.${size:(w|d|q)} Set Register ${register} On ${platform:(RV32|RV64)} To ${value}
137    ${cpu}=                         Get Cpu On ${platform}
138
139    IF  "${register}" == "0"
140        Return From Keyword
141    END
142
143    IF  "${platform}" == "RV32" and "${size}" == "d"
144        ${value_lower}=                 Set Variable  ${{${value} & 0xFFFFFFFF}}
145        ${value_upper}=                 Set Variable  ${{(${value} >> 32) & 0xFFFFFFFF}}
146        Execute Command                 ${cpu} SetRegister ${register} ${value_lower}
147        Execute Command                 ${cpu} SetRegister ${${register} + 1} ${value_upper}
148    ELSE IF  "${platform}" == "RV64" and "${size}" == "q"
149        # str necessary since robot's XML-RPC library doesn't support >32-bit integers
150        ${value_lower}=                 Set Variable  ${{str(int(${value}) & 0xFFFFFFFFFFFFFFFF)}}
151        ${value_upper}=                 Set Variable  ${{str((int(${value}) >> 64) & 0xFFFFFFFFFFFFFFFF)}}
152        Execute Command                 ${cpu} SetRegister ${register} ${value_lower}
153        Execute Command                 ${cpu} SetRegister ${${register} + 1} ${value_upper}
154    ELSE
155        Execute Command                 ${cpu} SetRegister ${register} ${value}
156    END
157
158Assemble Amocas.${size:(w|d|q)} ${rd} ${rs2} ${rs1}
159    # Hand-assembled instructions necessary due to
160    # our version of the LLVM assembler not supporting the Zacas extension. (issue #74345)
161
162    # The machine code for an amocas instruction with its operands and size zeroed out.
163    ${amocas_base}=                 Set Variable  0b00101_0_0_00000_00000_000_00000_0101111
164
165    # Translate size mnemonic to corresponding bit pattern.
166    IF  "${size}" == "w"
167        ${size_bits}=                   Set Variable  0b010
168    ELSE IF  "${size}" == "d"
169        ${size_bits}=                   Set Variable  0b011
170    ELSE IF  "${size}" == "q"
171        ${size_bits}=                   Set Variable  0b100
172    END
173
174    # Insert size into instruction
175    ${amocas_sized}=                Set Variable  ${{${amocas_base} | (${size_bits} << 12)}}
176
177    # Insert rd operand
178    ${amocas_sized_rd}=             Set Variable  ${{${amocas_sized} | (${rd} << 7)}}
179
180    # Insert rs1 operand
181    ${amocas_sized_rd_rs1}=         Set Variable  ${{${amocas_sized_rd} | (${rs1} << 15)}}
182
183    # Insert rs2 operand
184    ${amocas_complete}=             Set Variable  ${{${amocas_sized_rd_rs1} | (${rs2} << 20)}}
185
186    [return]                        ${amocas_complete}
187
188Amocas.${size:(w|d|q)} On ${platform:(RV32|RV64)} ${should:(Should|Shouldn't)} Set Value At ${variable_address} To ${new_value} If Expecting ${expected_value}
189    [Arguments]
190    ...                             ${rd}=${a0}
191    ...                             ${rs1}=${a3}
192    ...                             ${rs2}=${s2}
193    ...                             ${original_value_upper}=0x2badc00010ffb0ba
194    ...                             ${original_value_lower}=${expected_64}
195
196    # Place value in memory.
197    Execute Command                 sysbus WriteQuadWord ${variable_address} ${original_value_lower}
198    Execute Command                 sysbus WriteQuadWord ${${variable_address} + 8} ${original_value_upper}
199
200    ${cpu}=                         Get Cpu On ${platform}
201
202    # Construct amocas instruction.
203    ${MACHINE_CODE_AMOCAS}=         Assemble Amocas.${size} ${rd} ${rs2} ${rs1}
204
205    IF  "${size}" == "w"
206        # str necessary since robot's XML-RPC library doesn't support >32-bit integers
207        ${ORIGINAL_VALUE_MASKED}=       Set Variable  ${{str(${original_value_lower} & 0xFFFFFFFF)}}
208    ELSE IF  "${size}" == "d"
209        ${ORIGINAL_VALUE_MASKED}=       Set Variable  ${original_value_lower}
210    ELSE IF  "${size}" == "q"
211        # str necessary since robot's XML-RPC library doesn't support >32-bit integers
212        ${ORIGINAL_VALUE_MASKED}=       Set Variable  "${{str((${original_value_upper} << 64) | ${original_value_lower})}}"
213    END
214
215    # Place machine code at PC.
216    Execute Command                 sysbus WriteDoubleWord ${PROGRAM_COUNTER} ${MACHINE_CODE_AMOCAS}
217
218    # Set operand values.
219    Amocas.${size} Set Register ${rd} On ${platform} To ${expected_value}
220    Amocas.${size} Set Register ${rs1} On ${platform} To ${variable_address}
221    Amocas.${size} Set Register ${rs2} On ${platform} To ${new_value}
222
223    # Remember previous value.
224    ${result_upper_original_value}=  Execute Command  ${cpu} GetRegister ${${rd} + 1}
225    ${rs2_upper_original_value}=    Execute Command  ${cpu} GetRegister ${${rs2} + 1}
226
227    # Perform amocas.
228    Execute Command                 ${cpu} Step
229
230    IF  "${rd}" != "0"
231        # After amocas, rd register should have the original memory value (before the cas)...
232        Amocas.${size} Register ${rd} On ${platform} Should Contain ${ORIGINAL_VALUE_MASKED}
233    ELSE IF  ("${platform}" == "RV32" and "${size}" == "d") or ("${platform}" == "RV64" and "${size}" == "q")
234        # Unless rd is x0, in which case the original memory value is discarded and neither result register is written.
235        # Ensure rd+1 isn't written to.
236        Register Should Be Equal        ${${rd} + 1}  ${result_upper_original_value}  cpuName=${cpu}
237    END
238    # and the others should remain unchanged
239    IF  "${rs2}" != "0"
240        Amocas.${size} Register ${rs2} On ${platform} Should Contain ${new_value}
241    ELSE IF  ("${platform}" == "RV32" and "${size}" == "d") or ("${platform}" == "RV64" and "${size}" == "q")
242        # Unless rs2 is x0, in which case rs2+1 is interpreted as 0 no matter its contents.
243        # Ensure rs2+1 remains unchanged.
244        Register Should Be Equal        ${${rs2} + 1}  ${rs2_upper_original_value}  cpuName=${cpu}
245    END
246    Register Should Be Equal        ${rs1}  ${variable_address}  cpuName=${cpu}
247
248    IF  "${should}" == "Should"
249        # Now value in memory should have been set.
250        Amocas.${size} Memory Location ${variable_address} Should Now Be Set To ${new_value}
251    ELSE
252        # Value in memory should remain unchanged.
253        Amocas.${size} Memory Location ${variable_address} Should Now Be Set To ${ORIGINAL_VALUE_MASKED}
254    END
255
256*** Test Cases ***
257# w should tests
258Amocas.w On RV64 Should Set Value At Single-Page Memory Location
259    Amocas.w On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${expected_32}
260
261Amocas.w On RV64 Should Set Value At Page-Spanning Memory Location
262    Amocas.w On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_32} If Expecting ${expected_32}
263
264Amocas.w On RV64 Should Set Value At MMIO Memory Location
265    Amocas.w On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_32} If Expecting ${expected_32}
266
267Amocas.w On RV32 Should Set Value At Single-Page Memory Location
268    Amocas.w On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${expected_32}
269
270# w shouldn't tests
271
272Amocas.w On RV64 Shouldn't Set Value At Single-Page Memory Location
273    Amocas.w On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32}
274
275Amocas.w On RV64 Shouldn't Set Value At Page-Spanning Memory Location
276    Amocas.w On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32}
277
278Amocas.w On RV64 Shouldn't Set Value At MMIO Memory Location
279    Amocas.w On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32}
280
281Amocas.w On RV32 Shouldn't Set Value At Single-Page Memory Location
282    Amocas.w On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_32} If Expecting ${wrong_expected_32}
283
284# d should tests
285
286Amocas.d On RV64 Should Set Value At Single-Page Memory Location
287    Amocas.d On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64}
288
289Amocas.d On RV32 Should Set Value At Single-Page Memory Location
290    Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64}
291
292Amocas.d On RV32 Should Handle Zero Source Register
293    # Place value in rs2+1 which should be ignored.
294    Execute Command                 cpu32 SetRegister ${${x0} + 1} ${wrong_expected_32}
295
296    Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To 0 If Expecting ${expected_64}
297    ...                             rs2=${x0}
298
299Amocas.d On RV32 Should Handle Zero Destination Register
300    # Place value in rd+1 which shouldn't be overwritten.
301    Execute Command                 cpu32 SetRegister ${${x0} + 1} ${wrong_expected_32}
302
303    Amocas.d On RV32 Should Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting 0
304    ...                             rd=${x0}
305    ...                             original_value_lower=0
306
307Amocas.d On RV64 Should Set Value At Page-Spanning Memory Location
308    Amocas.d On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_64} If Expecting ${expected_64}
309
310Amocas.d On RV64 Should Set Value At MMIO Memory Location
311    Amocas.d On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_64} If Expecting ${expected_64}
312
313# d shouldn't tests
314
315Amocas.d On RV64 Shouldn't Set Value At Single-Page Memory Location
316    Amocas.d On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64}
317
318Amocas.d On RV32 Shouldn't Set Value At Single-Page Memory Location
319    Amocas.d On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64}
320
321Amocas.d On RV64 Shouldn't Set Value At Single-Page Memory Location
322    Amocas.d On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64}
323
324Amocas.d On RV64 Shouldn't Set Value At Single-Page Memory Location
325    Amocas.d On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_64} If Expecting ${wrong_expected_64}
326
327# q should tests
328
329Amocas.q On RV64 Should Set Value At Single-Page Memory Location
330    Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${expected_128}
331
332Amocas.q On RV64 Should Set Value At Page-Spanning Memory Location
333    Amocas.q On RV64 Should Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_128} If Expecting ${expected_128}
334
335Amocas.q On RV64 Should Set Value At MMIO Memory Location
336    Amocas.q On RV64 Should Set Value At ${MMIO_ADDRESS} To ${new_128} If Expecting ${expected_128}
337
338Amocas.q On RV64 Should Handle Zero Source Register
339    # Place value in rs2+1 which should be ignored.
340    Execute Command                 cpu SetRegister ${${x0} + 1} ${wrong_expected_32}
341
342    Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To 0 If Expecting ${expected_128}
343    ...                             rs2=${x0}
344
345Amocas.q On RV64 Should Handle Zero Destination Register
346    # Place value in rd+1 which shouldn't be overwritten.
347    Execute Command                 cpu SetRegister ${${x0} + 1} ${wrong_expected_32}
348
349    Amocas.q On RV64 Should Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting 0
350    ...                             rd=${x0}
351    ...                             original_value_lower=0
352    ...                             original_value_upper=0
353
354# q shouldn't tests
355
356Amocas.q On RV64 Shouldn't Set Value At Single-Page Memory Location
357    Amocas.q On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128}
358
359Amocas.q On RV64 Shouldn't Set Value At Page-Spanning Memory Location
360    Amocas.q On RV64 Shouldn't Set Value At ${PAGE_SPANNING_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128}
361
362Amocas.q On RV64 Shouldn't Set Value At MMIO Memory Location
363    Amocas.q On RV64 Shouldn't Set Value At ${MMIO_ADDRESS} To ${new_128} If Expecting ${wrong_expected_128}
364
365# illegal instructions
366
367Amocas.d On RV32 Using Odd Registers Should Throw Illegal Instruction
368    ${odd_rd}=                      Set Variable  ${${a0} + 1}
369    ${odd_rs2}=                     Set Variable  ${${s2} + 1}
370
371    Amocas.d On RV32 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_64} If Expecting ${expected_64}
372    ...                             rd=${odd_rd}
373    ...                             rs2=${odd_rs2}
374
375    Amocas.d ${odd_rd} ${odd_rs2} ${a3} On RV32 Should Throw Illegal Instruction
376
377Amocas.q On RV64 Using Odd Registers Should Throw Illegal Instruction
378    ${odd_rd}=                      Set Variable  ${${a0} + 1}
379    ${odd_rs2}=                     Set Variable  ${${s2} + 1}
380
381    Amocas.q On RV64 Shouldn't Set Value At ${ORDINARY_ADDRESS} To ${new_128} If Expecting ${expected_128}
382    ...                             rd=${odd_rd}
383    ...                             rs2=${odd_rs2}
384
385    Amocas.q ${odd_rd} ${odd_rs2} ${a3} On RV64 Should Throw Illegal Instruction
386