tum-ei-eda / etiss

Extendable Translating Instruction Set Simulator
https://tum-ei-eda.github.io/etiss/
Other
29 stars 36 forks source link

Make ETISS return nonzero exit value through application software #104

Closed fpedd closed 2 years ago

fpedd commented 2 years ago

I am trying to make ETISS return a nonzero exit value through the application software. This way I can use ctest to automagically detect if unit tests failed.

Spike and OVPsim, for example, return a non-zero exit value when the application software runs __asm__ volatile("unimp");. ETISS however simply prints

ETISS: Warning: Illegal instruction at address: 0x43e
Illegal instruction at 0000043E with address 0000043E

and then hangs, but does not return.

I have also tried writing/reading and jumping do random places in the code without any luck, for example:

__asm__ volatile("sw zero, (zero)");

or

__asm__ volatile("li t0, -1\n"
                 "sw zero, (t0)");

I can use ctests TIMEOUT property to make the test fail after waiting x seconds. But this means that I either have to set the timeout very tight or wait quite some time for the tests run through...

rafzi commented 2 years ago

I don't think this is currently possible, because all exceptions are caught by the default exception handler, which just does reporting and then hangs. The hanging is useful for attaching with a debugger, but I'm open to change this behavior, for example to just call exit/abort.

https://github.com/tum-ei-eda/etiss/blob/master/examples/SW/riscv/cmake/pulpino_tumeda/syscalls.c#L231

Calling exit/abort would be the natural way to report the exit value anyway. However this leads to another issue, in that the exit code from exit is not propagated outside of ETISS. The long term solution to this would be a proper semi-hosting interface. Alternatively, the current one could be extended. A first idea would be to add a "returnValue" to the CPU struct:

https://github.com/tum-ei-eda/etiss/blob/master/include_c/etiss/jit/CPU.h#L88

Then we could extend the behavior of the RISC-V "ebreak" instruction (which we use to signal end of simulation) to copy the value of register a0 to cpu->returnValue. The exit syscall implementation would need to copy the exit_status value to a0:

https://github.com/tum-ei-eda/etiss/blob/master/examples/SW/riscv/cmake/pulpino_tumeda/syscalls.c#L118

Finally, the simulation loop would extact the returnValue from the CPU struct after the simulation ended and report it to outside. Here we should probably not forward the value as is, to keep the existing error codes intact. Instead we could add an error code for "non zero application return".

fpedd commented 2 years ago

Thanks for the clarification, @rafzi. Of course, a proper semi-hosting interface would be ideal. This is how Spike with its proxy kernel (pk) is doing it, I think.

However, I think a simpler solution would simply be to catch illegal instruction in ETISS itself and never return to software execution from there. I think this is how OVPsim is doing it.

To that end, I have to admit that I am somewhat confused about how ETISS, or at least the bare_etiss_processor example, works. It prints out that an illegal instruction occurred

ETISS: Warning: Illegal instruction at address: 0x43e

but the cpu->execute(dsys) does not return etiss::RETURNCODE::ILLEGALINSTRUCTION? It appears, that this is because this line always returns etiss::RETURNCODE::NOERROR. What is the point of this switch-case then?

rafzi commented 2 years ago

In my opinion, if the software handles the illegal instruction exception, which ours does, the simulator should not stop execution.

That is how it works in ETISS, at least for RISC-V. I think the code from bare_etiss would still be active for the OR1K arch.

fpedd commented 2 years ago

Okay, so a semi-hosting approach would then be the way to go. Thanks again :)