Search code examples
carmemulationpipeline

ARM11 Emulator in C


I'm trying to write a C program that simulates execution of an ARM binary file. So what it does right now, we fetch instructions from binary file into an array of uint32_t, which I then decode and execute.

The problem is that I use program counter only to access ints from array and then I increment it. But for the branch instruction, that takes offset, extends it to 32 bits and adds it to PC, PC should be 8 bytes ahead of the instruction that is being executed. So pipeline effect should take place. That is basically:

  1. 32bit (4byte) instruction is fetched from memory
  2. instruction is decoded
  3. decoded instruction is executed

So when the instruction is being executed at the top of the pipeline, the instruction being fetched is two instructions further in memory. Therefore PC is 8 bytes greater that the address of the instruction being executed.

Does anybody have any idea how to implement that pipeline easily? I guess I need to redo my memory storage for the instruction as it is just an array of a fixed size right now. My thought was to align memory, and then after each instruction add 4 to the PC, and access next instruction by using a pointer to the first element in an array and adding PC to that. If that would work, could somebody show me how that would look like?


Solution

  • You dont need to simulate a pipeline. ARM has not used a 2 ahead pipeline in a while in hardware, they simulate the 2 ahead as well.

    What I did in mine was keep the pc one ahead any time it is modified, then on the fetch I fetch at pc-4 then add 4. Any instruction that can modify the pc had to have an if pc then pc+=4. And when I compute a branch dest I had to add another 4. Why I did it this way, I dont know.

    Alternatively you can keep the pc pointing at the current instruction. And every time the pc is read you add 8. Probably easier that way. I abstract my instruction fetch, memory read/write and register read/write using functions and within the read_register() function I would add the if r15 then result+=8; and then return.

    Again, why I didnt do that, I dont remember, I had to hack at it to get it working. Now the reality was my was a thumb(v1) simulator not arm so it was +2 and +4 not +4 and +8. And there was no arm mode at all (supported by my simulator), so only a few instructions in thumb can actually modify the pc so that made it easier to target only those with the if reg==15.

    If you really want to do an arm11 simulator you need to think about thumb mode and then deal with the pc either being 4 ahead or 8 ahead, and remember to strip the lsbit off the pc in thumb mode when it is modified by a handful of instructions that have to have the lsbit set to switch to or stay in thumb mode. Fortunately the arm11 does not support thumb2 then it gets really ugly as it is variable instruction length and you have to be two instructions ahead not just 4 or 8 bytes (can be 4, 6 or 8 bytes ahead you have to decode the next two to find out in thumb mode). If you dont want to support thumb mode you could just easily look for the lsbit being set in a bx or pop and then declare you dont support thumb mode and bail out.

    Your choices are to either simulate a pipeline, or you have to insert one or some if reg==15 then... lines of code, and you have to do it everywhere the pc is used (routing all register reads to one function is a very easy way to do this). I should go fix my simulator to do this. If you end up supporting thumb mode then you could simply say if in thumb mode then add 4 else add 8 in this read register function.