Welcome! In this blog post, we’re introducing a new feature for UberDDR3: Self-Refresh mode. This mode allows DDR3 memory to refresh itself, cutting down on power usage—ideal for low-power systems and idle periods.
If you're already using UberDDR3 in your projects and want to reduce unnecessary power consumption, keep reading to learn how this feature works and how you can easily customize UberDDR3 to take advantage of it!
Table of Contents:
I. DDR3 Power Consumption: Impact of Refresh Cycles
DDR3 memory relies on regular refresh cycles to preserve data, but these cycles are a major source of power consumption. As memory sizes increase, the energy required for refresh operations also grows. Typically, DDR3 refreshes its memory every 7.8 microseconds, but higher temperatures force even more frequent refreshes to prevent data loss (source here). Managing this constant power demand is a critical challenge in DDR3 systems.
This is where self-refresh feature comes in, the self-refresh command allows DDR3 SDRAM to retain data even when the system is powered down. In this mode, the memory performs its own refresh cycles using an internal clock and timer, removing the need for external clocking. Self-refresh is ideal for power-down scenarios where no memory access is required, significantly reducing power consumption while ensuring data integrity.
II. UberDDR3 New Feature: Self-refresh
While there’s limited information online about DDR3 self-refresh features, I came across a description from Microchip’s SAMA5D29, a high-performance, low-power Arm Cortex-A5-based embedded microprocessor. It states that self-refresh is triggered by setting the Low-power Command bit (LPCB) to 1 in the MPDDRC Low-Power Register (MPDDRC_LPR). Additionally, the TIMEOUT field in the register allows configuration of when the self-refresh mode should be enabled (documentation here):
0: Activated immediately after DDR-SDRAM is not selected.
1: Activated 64 clock cycles after the last access.
2: Activated 128 clock cycles after the last access.
Inspired by this, I’ve implemented a similar feature in UberDDR3 with four options for the SELF_REFRESH parameter:
0: The user controls self-refresh via the i_user_self_refresh input. Assert to 1 to enter self-refresh and deassert to 0 to exit. Exiting requires a ~1500ns delay to meet the tXSDLL requirement.
1: Self-refresh is triggered after 64 controller clock cycles of no Wishbone requests. If no requests occur for 64 cycles, UberDDR3 enters self-refresh. It exits when a new Wishbone request is received or another 64 cycles pass. The i_user_self_refresh input has no effect here.
2: Same as option 1, but self-refresh is triggered after 128 controller clock cycles.
3: Same as option 1, but self-refresh is triggered after 256 controller clock cycles.
This new feature helps optimize power consumption during idle periods by reducing unnecessary activity when the system is not in use.
III. Design Implementation
Now, let me walk you through how the self-refresh feature was integrated into UberDDR3. Here’s a simple illustration to make it easier to follow:
At the top level, a refresh counter keeps track of idle cycles whenever the Wishbone interface is not processing any requests (indicated by the Wishbone stall signal being low). However, if a new request arrives (signaled by both Wishbone strobe and cyc being high), the refresh counter immediately resets to zero.
The output of this refresh counter determines if 256, 128, or 64 controller cycles have elapsed without any new Wishbone requests. The SELF_REFRESH parameter decides which of these options will trigger self-refresh. It can either select one of the predefined thresholds (256/128/64 cycles) or use the i_user_self_refresh signal provided through a top-level port. This flexibility allows for customizing the self-refresh behavior based on your design requirements.
Inside the DDR3 controller, the reset/refresh sequence controller manages both reset and refresh logic. While it handles various tasks, let’s focus on the part relevant to self-refresh:
As shown above, the refresh sequence is fairly straightforward. Most of the time, the controller operates in the "Normal Operation" state, where it handles read and write requests from the Wishbone interface.
However, DDR3 memory requires a mandatory periodic refresh every 7.8 µs. When this timer expires, the controller transitions to the "Precharge All" state to precharge all banks, followed by the actual "Refresh" operation. Once the refresh is complete, the controller returns to "Normal Operation". This path, referred to as the manual refresh sequence, is illustrated on the left side of the diagram below:
On the right side, we have the self-refresh sequence. When the i_user_self_refresh input is asserted, the controller immediately exits "Normal Operation" and transitions to "Precharge All", bypassing the 7.8 µs timer. It then enters self-refresh mode ("Enter Self-refresh") by holding the DDR3 CKE (Clock Enable) pin low. In this state, the DDR3 memory minimizes power consumption while preserving data.
The controller remains in self-refresh mode until the i_user_self_refresh signal is deasserted. Exiting self-refresh ("Exit Self-refresh") is as simple as driving the CKE pin high again.
Although not mandatory, a manual refresh is performed immediately after exiting self-refresh. As outlined in the JEDEC DDR3 specification:
The use of Self-Refresh mode introduces the possibility that an internally timed refresh event can be missed when CKE is raised for exit from Self-Refresh mode. Upon exit from Self-Refresh, the DDR3 SDRAM requires a minimum of one extra refresh command before it is put back into Self-Refresh Mode.
And that’s it! From a high-level user perspective, self-refresh is a simple yet effective mechanism to save power while maintaining data integrity.
III.I Timing Penalty After Self-Refresh Exit
Now, let’s talk about the timing penalty involved when exiting self-refresh mode. As shown earlier, each state transition in DDR3 requires specific timing parameters to be met. The key parameter to focus on here is tXSDLL, which has a value of 1500 ns.
This 1500 ns penalty is the sum of two components:
Self-refresh exit delay (~1200 ns): The time required for the DDR3 memory to stabilize after leaving self-refresh mode.
tRFC (Refresh Cycle Time, 300 ns): The time taken for the manual refresh following the self-refresh exit.
This timing penalty is crucial to keep in mind when implementing self-refresh. Simply deasserting the i_user_self_refresh input does not mean you can immediately resume normal DDR3 operations in the next clock cycle. You will need to wait for the 1500 ns timing penalty to be satisfied before issuing any new requests!
IV. Testbench Simulation
Unlike other DDR3 operations, such as read and write, self-refresh does not have an immediately visible functionality that can be directly observed. It operates in the background, making it less obvious to the user when self-refresh is actually active.
To verify that the self-refresh mechanism is working as intended, we can use a testbench simulation with the Micron DDR3 model file. This model allows us to confirm that the DDR3 memory enters and exits self-refresh mode correctly.
To run the testbench, refer to the blog post Getting Started with UberDDR3 (Part 1). From there, you can configure the localparam for SELF_REFRESH to values 0, 1, 2, or 3 in ddr3_dimm_micron_sim.sv to test the feature.
As shown above, on the left side of the simulation waveform:
When the i_user_self_refresh signal is asserted, the DDR3 clock-enable signal (o_ddr3_cke) is driven low, indicating the start of self-refresh.
The Micron DDR3 model detects this and reports "Self Refresh Enter" at time 197803750 ps.
For this simulation, I used SELF_REFRESH = 3, which corresponds to a self-refresh duration of 256 controller clock cycles. At the end of this period, the Micron DDR3 model detects the exit from self-refresh and reports "Self Refresh Exit" at time 200313750 ps.
Just to demonstrate that self-refresh lasts approximately 256 controller clock cycles, let’s do the math:
200313.750 ns - 197803.750 ns = 2510 ns
Since the controller clock runs at 100 MHz (10 ns period), divide 2510 ns by 10 ns: 2510 ns ÷ 10 ns = 251 cycles
This result is very close to the expected value of 256 cycles! The small discrepancy of 5 cycles is because we are monitoring the o_ddr3_cke signal directly instead of the i_user_self_refresh input.
Next, let’s focus on the right side of the waveform above. Notice that after the controller enters self-refresh, the Wishbone strobe signal is asserted. This prompts the controller to immediately begin the self-refresh exit sequence (you can see the o_ddr3_cke signal go high right after the Wishbone strobe is asserted).
However, even after the self-refresh exit is complete, the Wishbone stall signal remains high for a brief period. This delay corresponds to the 1500 ns timing penalty required after exiting self-refresh. On this scale, the impact of the timing penalty is clear: the controller must wait after self-refresh exit before resuming normal operations.
V. Project Demonstration
To wrap up this blog post, let’s showcase the new self-refresh feature with an FPGA demo. Building on what we learned from the previous blog update, we’ll use MicroBlaze's Dhrystone Test running on the Arty-S7 board for this demonstration.
To set up the demo, follow the steps outlined in UberDDR3 + MicroBlaze (Part 1) and UberDDR3 + MicroBlaze (Part 2). The only difference is the customization for the UberDDR3 controller. As shown below, you can select your desired self-refresh mode from the drop-down menu. For this demo, I’ve chosen mode 3 (enable self-refresh after 256 clock cycles of inactivity).
I also added some debug logic to monitor when self-refresh is active. If you’ve followed Part 1 and Part 2 of the mentioned blog post, you should have a working Dhrystone Test application. Below is the output from the serial terminal, showing that the Dhrystone Test ran successfully:
Now, let’s examine the signals captured by the Internal Logic Analyzer (ILA). While the test is running, the i_user_self_refresh signal of the controller remains low, indicating that self-refresh is not active
The instruction_address signal provides insight into the current state of the controller. Most of the time, its value is 22, which corresponds to the Normal Operation state. Occasionally, it switches to perform a manual Refresh. These transitions occur every 7.8us.
After the Dhrystone Test completes and there are no more requests to the DDR3 controller, the i_user_self_refresh signal starts toggling:
When i_user_self_refresh is high, the instruction_address signal shows a value of 26, which corresponds to the Self-Refresh state. Since we used SELF_REFRESH = 3, the self-refresh lasts for 256 clock cycles, as expected.
And that’s it! We successfully integrated and verified the new self-refresh feature in UberDDR3.
VI. Conclusion
In this blog post, we looked at how DDR3 refresh cycles impact power consumption and how the new self-refresh feature in UberDDR3 helps reduce this. By simply configuring the SELF_REFRESH parameter to your preferred mode, UberDDR3 can significantly reduce power usage while preserving data integrity during idle periods. We walked through the integration process, testing, and showed how it works in an FPGA demo. With this update, UberDDR3 becomes more power-efficient, making it a better choice for low-power applications.
That wraps up this post. Catch you in the next blog post!
Comentarios