Let’s be honest: for those who believe it is necessary to use a full-featured soft-core memory controller connected to an industry-standard DDR4 memory model—no, it doesn’t have to get that complicated. In fact, I would highly suggest finding a more straightforward solution to your simulation problems. Most of the time, a simplified behavioral model of the memory is more than sufficient. The reason? Simulating such an “extreme monster” (PHY, Controller, Calibration, Timing, etc.) with literally tons of logic is simply time-consuming and practically provides no additional guarantees that your designed component (DUT) will work. For instance, an AXI4-accessible DDR4 interface provides almost zero added value for your test cases. If you follow the AXI4 interface rules, there is literally nothing that can go wrong with your DUT—unless you are testing some sort of ECC or timing-related problems. The same applies to AMD’s native UI interface, which obeys similar rules.
When you should aim for the full-featured DDR simulation?
The only time you should really endure this Micron-AMD pain is when you intend to test either the interface bandwidth (based on custom access patterns) or the real timing of the interface in case your design is heavily dependent upon it somehow. Eventually, you are also working with ECC or simply have higher requirements that somehow enforce the usage of such solution (IE. For the purposes of certification) .
Library Compilation
One can easily customize and generate the softcore DDR4 memory controller (PG150) in Vivado. This is where I usually suggest looking up your required configuration and customizing the controller with your own timing parameters. There is an excellent answer record 63462 to help you refine your parameters. However, in order to start the simulation of the generated IP, you have to precompile the simulation libraries. I was specifically using Aldec’s Riviera-PRO 2025.04 and AMD’s Vivado 2025.2. Sometimes it’s better to check that the given simulator version is supported in IDE the documentation, but if there is a couple of years of difference, I usually ignore it—just pay higher attention to compilation warnings. The real theoretical problem you might encounter is that some 3rd party generated files require an assertion-based license. If you do not have it, the simulation will fail later. The fix is to use the “-na all” vlog switch to suppress these assertions per the vendor’s documentation HERE.
- config_compile_simlib -cfgopt {riviera.verilog.xpm:-na all}
- compile_simlib -simulator riviera -language all -family kintexuplus -library all -directory #DESTINATION#
The “Free Model” Conspiracy
Here is a key thing to mention: All of the large verification companies and EDA vendors basically offer to buy a specialized VIP for (not only) DDR4 testing. They actually don’t want you to use some “free model”—they want you to pay for the Verification IP. While I generally support such intentions, if your budget simply can’t afford for it, then you are left with “What’s out there“.
As a result, the available models out there might be outdated, lack documentation, work partially, be encrypted or simply have all of these nice features together. If you use Questa, Modelsim, VCS, or ncVerilog, you can go directly to Micron’s website and download their model. It’s funny, though, that there is no “Aldec” option—at least in the model I downloaded—yet the readme within it still mentions downloading from Aldec directly. You won’t find it via the usual search (I tried). Fortunately, the legacy link below still works. The reason why you need a specific vendor-based model is that the DDR4 micron model is encrypted and can be decrypted only in the corresponding tool (IE – Questa – encrypted model will not work for Riviera and so on.)
Simulation File Hunt
One of the trickiest parts is grabbing the correct files generated out-of-context by the IP. If you try to manually hunt for files after clicking “Disable IP Container,” you are going to miss things. The best strategy is to “Open IP Example Design” of the DDR4 controller, select Riviera as your preferred simulator, and select “Compile Simulation Scripts only”.
NOTE: Don’t forget to specify your pre-compiled library location for the step above. If you don’t do that, the reference TCL script will default to including the raw “rfs” encrypted source files (e.g., iomodule_v3_1_vh_rfs.vhd) in the compilation list. These are the source files for the libraries themselves and are not required if you already compiled the libraries earlier. The goal is to not compile these again, but to use the libraries you already pre-compiled by pointing the script to your library path. It should simply link the required library (e.g., using -L iomodule_v3_1_13 instead of compiling the source).
Once you have this file, don’t deviate much from it. Specifically:
- glbl.v: Most AMD/Xilinx sources require
glbl.vto properly reset internal primitives. You must usexil_defaultlib. - MicroBlaze Initialization: The IP example design includes an
importsfolder where you can find the BRAM initialization for the internal MicroBlaze controller. You will see files likebd_629f_lmb_bram_I_0.mem. Without these, the controller brain is empty. The used hash number might be different, adjust to your needs.
Relic of the ancients
Eventually, you will be able to run the simulation. Personally, I expect that you will immediately hit a lot of complaints from the DDR4 Micron model. This is because the model is likely old, outdated, and doesn’t contain the required timing settings it should (i.e., it uses different timing than the Controller itself). Don’t panic.
- Lookup the
task LoadTiming();intiming_tasks.svwithin the model. - Adjust the timing of the DDR4 model to your (or the controller’s) expected values.
I had to personally even adjust itCK_min and itCK_max to force the model to stay in TS_1250 speed bin (by using nonsensical min-max periods for other speed bins). Newer models might contain the ALLOW_JITTER parameter that should force the model to stay in the selected speed bin despite some clock glitch, but sometimes you just have to force the model by your hand. Eventually, you should be able to see the following log entires besides your successful reads and writes.
# KERNEL: Setting Timeset:TS_1250 with tCk:1250 @1777.8 ns
# KERNEL: PG150_TB.MicronWrap.Micron_Model.always_diff_ck.if_diff_ck:Initialization complete @3938.4 ns
# KERNEL: stopped at time: 8 us
The example compilation script and the model wrapper for your reference can be found below: