Saturday, January 11, 2025

Finally have the memory controller IP simulating in Vivado - trying to verify my dram controller again

WANDERED TWISTY PASSAGES FOR DOZENS OF HOURS UNTIL SIMULATION WORKED

I could find nothing in the documentation for the memory generator IP nor online that told me what was needed to get simulation generated into the logic of the IP. It showed me the simulation modules for the DDR3 chips and the overall simulation top level logic but as long as the generated IP was built without simulation support it would not work. 

The secret was finally discovered through experimentation. When you use the IP Catalog to generate the IP you want, for example the memory interface logic for DDR3 memory, it does not present any option for simulation. It generates the various simulation modules I mentioned above but has no way to set the IP itself to make use of simulation.

I discovered a Verilog file, one level down from the top Verilog file generated for the MIG IP, which had the parameters for simulation and for fast calibration support. Since these were all produced when I created the IP, I had assumed they would be rebuilt with the same choices but eventually I decided to manually update that second level file.

Next up was the search for a way to regenerate the IP logic without also recreating the second level file. I eventually discovered that resetting the generated products for the IP and then generating anew would retain my modified second level file. Eureka. The IP logic now simulated, toggling the signal lines to the DDR3 chips and the simulation modules appropriately toggling back responses. 

STARTUP TIME ADDS PAIN TO EACH SIMULATION RUN

The memory controller logic interacts with the DDR3 RAM chips performing a calibration, involving many accesses, until it asserts the Init_Calibration_Done signal and begins outputting the 83 MHz user interface clock that is central to my interface between the memory controller IP and my design logic.

This requires simulation for 123 microseconds even with the "Fast" calibration setting for the IP. In wall clock time, it requires more than two minutes before the simulation reaches that point. The complexity of the memory controller IP and other logic in my design imposes about a two minute startup delay before the first femtosecond of simulation time. In all, it is more than four minutes from a click until I see my part of the design begin executing. 

Iterative debugging, while I vary inputs to the design to test corner cases as well as its core functioning, imposes that four minute burden on each cycle. 

I do still have an error in the simulation - the toggling of some of the DDR3 lines by the memory controller IP is running 1000x faster than it should. Not sure whether this impacts the DDR3 simulation module, but I can look at the code to determine whether I need to find the source of this defect and correct it. 

The unexpected good news is that the clock wizard IP was also working properly, not requiring my simulated version, yet was properly producing the 200, 167 and 100 MHz clocks. I don't know why this would have changed but I welcome it. 

CLEANED UP RESET TIMING OF VARIOUS PARTS OF MY DESIGN

The memory interface logic was dropping the user interface reset (ui_reset) way before the init_calibration_done was raised. I needed to hold all my logic until the memory was ready for use, so I had to combine the two signals so that reset for my design lasted until calibration was done as well as while user interface reset was active. 

ADJUSTED THE PARAMETERS OF THE MEMORY INTERFACE 

I have the memory interface logic operating with a clock of around 325MHz, the user interface to the memory interface operating at 81.2MHz (4:1 ratio of the interface), and my general logic operating at 100MHz. This produced the proper timing of signals to the DDR3 chips as well as proper behavior when I drove the interface. 

OBSERVING MY LOGIC READING RAM AND WRITING RAM 

The testbench I had set up when I simulated my dram controller module using my own simulated version of the memory interface was used to drive this new simulation. It set up addresses for cylinder, head, sector and word before requesting to store away a word we had extracted from the Diablo drive or to fetch a previously written word from RAM when we were uploading the archived cartridge at the end of the run. 

The logic simulated well using my homebrew versions of the memory interface but I needed to see this work properly with the actual IP. First up was watching a request for a memory read, which I scrutinized on the simulation output. Second was a write request, which was subjected to the same level of diligence. 

Initially I saw my design completing only one read from RAM. Digging in I found that the memory interface was dropping the clock enable to the memory chips, disabling them from accepting my following read requests. I never did figure out why the simulation of the memory controller was complaining and shutting down during the first read process. 

Another issue became apparent. My design depends on the far side of a FIFO seeing the empty flag turn off, indicating that data has arrived from the near side. During the simulation, I saw the write enable turn on to push something into the near side but the empty flag never turned off. Upon closer inspection of the FIFO wizard, the empty flag seems to stay on with up to four words pushed in, which makes my design fail. 

To fix this, my logic looks at the count of data in the FIFO instead of the empty flag. When it is non-zero, I kick off the rd_en signal to pull out the word from the FIFO. Once the FIFO interaction with my main logic is working, according to the simulation, I can move forward.


No comments:

Post a Comment