Wednesday, March 25, 2015

GHDL Simulation is working again, making it much easier to track down DDR controller bugs

Oh a happy day it will be when the DDR controller is working and I can ignore it for, hopefully, a very long time.  That day feels closer now that I am able to simulate my model in GHDL again.

Early in the year I upgraded GHDL from 0.29 to 0.31 so that I would get meaningful error messages.  However, I discovered a bug in GHDL that prevented me from being able to simulate.

The GHDL maintainers were very good and managed to replicate the problem, fix it in the up-stream version, and also explain a very simple work-around for the meantime.

Somehow I missed the email notification that contained the work-around information, so all this time in between I have been unable to simulate, which has made it rather harder to trackdown the bugs with the DDR controller.

So a bit of hacking yesterday and I now have the model simulating in GHDL again.

This let me work out exactly what was going wrong with the most recent DDR controller bug I had identified.  That bug is that after writing to the DDR memory, the CPU would read the opcode for the next instruction from the DDR memory, instead of from wherever it should have read it.

So, I fired up the simulator and was able to quickly reproduce the bug with a little loop:

loop:  LDA #$99
       STA $4000  ; this address was mapped to the DDR memory
       INC $D020
       JMP loop

When I simulated it, I could see the following.  The bug is that $CF is read from the DDR RAM, instead of $EE (the opcode for INC) being read.  As a result entirely the wrong instruction was executed as can be seen in this sanitised excerpt from the simulator:

$8114 A9 99     lda  #$99          A:99 X:40 Y:00 Z:3F SP:BEFF P:A4 $01=F5
$8116 8D 00 40  sta  $4000         A:99 X:40 Y:00 Z:3F SP:BEFF P:A4 $01=F5 
$8119 CF 20 D0  bbs4 $20,$81EC     A:99 X:40 Y:00 Z:3F SP:BEFF P:A4 $01=F5 

A little poking around revealed that the correct value was being read, but that the CPU state machine was getting a bit confused while waiting for the DDR RAM to acknowledge the write, and when it saw a read from the DDR RAM it kept that instead.  There is a flag that clears the CPU's intent to read once it has the value it is after, but that wasn't being used in one particular place.  So I added the necessary two lines as follows:

      if mem_reading='1' then
        memory_read_value := read_data;
      end if;

... and bingo, it was suddenly working properly:

$8114 A9 99     lda  #$99          A:99 X:40 Y:00 Z:3F SP:BEFF P:A4 $01=F5 
$8116 8D 00 40  sta  $4000         A:99 X:40 Y:00 Z:3F SP:BEFF P:A4 $01=F5 
$8119 EE 20 D0  inc  $D020         A:99 X:40 Y:00 Z:3F SP:BEFF P:24 $01=F5
$811C 4C 14 81  jmp  $8114         A:99 X:40 Y:00 Z:3F SP:BEFF P:24 $01=F5

Oh how nice it is to know that you have fixed a bug before starting a 1-2 hour FPGA synthesis run.

After synthesis, I was able to confirm that the bug was fixed. However, there is at least one more bug to fix, as it seems that when the hypervisor exits to pass control to the ROM stored in DDR RAM, that the reset vector is not being read from the DDR RAM.  But now that I can simulate again, this bug shouldn't be able to hide for long...

No comments:

Post a Comment