0xStubs

System Administration, Programming and Reconfigurable Computing

Zynq DDR self-refresh

The Xilinx Zynq SoC supports disabling the DDR3 memory controller and the corresponding clocks to save energy. The memory can be put into self-refresh mode to retain its data. Inspired by a thread on the Xilinx forums I measured the actual effect of this power saving method on a Digilent ZedBoard.

For this test I created a block design in Vivado with only the ZYNQ7 Processing System IP, in which I disabled all peripherals except UART 1, and exported the design into the SDK. The software running on the Zynq can be found on the bottom of this article. Let’s first discuss the results:

The ZedBoard features two current sense pins. Looking at the schematic we can see that these pins are connected to a high-side 10mΩ current shunt resistor. Here are the measured voltage drops:

  • DDR memory enabled: 2.85mV, corresponding to 285mA or 3.42W
  • DDR in self-refresh mode: 2.69mV, corresponding to 269mA or 3.23W

So we can see a drop of 16mA, corresponding to about 190mW at 12V, when the memory is put into self-refresh mode. It would be interesting to repeat this measurements with more sophisticated methods to see exactly what components consume how much power.

Lastly, here is the code. All register addresses and bit offsets can be found in the Zynq TRM (see inline comments). If you run this code you have to make sure that the program itself lies in ps7_ram instead of ps7_ddr (configurable in lscript.ld).

#include <stdio.h>
#include "sleep.h"
#include "xstatus.h"

/*
 * Procedure as described in section 10.9.6
 * of the Zynq TRM:
 *
 * ddrc.ctrl_reg1[reg_ddrc_selfref_en] = 1
 * ddrc.DRAM_param_reg3 [reg_ddrc_en_dfi_dram_clk_disable] = 1
 * while (slcr.DDR_CMD_STA[CMD_Q_NEMPTY] != 0)
 * while (ddrc.mode_sts_reg[ddrc_reg_operating_mode] != 3)
 * slcr.DDR_CLK_CTRL[DDR_2XCLKACT] = 0
 * slcr.DDR_CLK_CTRL[DDR_3XCLKACT] = 0
 * slcr.DCI_CLK_CTRL[CLKACT] = 0
 *
 * Register addresses and bit offsets are found in
 * Appendix B of the TRM.
 */

#define slcr_lock_value   0x767B
#define slcr_unlock_value 0xDF0D

int main() {

	volatile uint32_t *ddrc_ctrl_reg1       = (uint32_t*) 0xF8006060;
	volatile uint32_t *ddrc_dram_param_reg3 = (uint32_t*) 0xF8006020;
	volatile uint32_t *ddrc_mode_sts_reg    = (uint32_t*) 0xF8006054;
	volatile uint32_t *slcr_dci_clk_ctrl    = (uint32_t*) 0xF8000128;
	volatile uint32_t *slcr_ddr_clk_ctrl    = (uint32_t*) 0xF8000124;
	volatile uint32_t *slcr_ddr_cmd_sta     = (uint32_t*) 0xF8000618;
	volatile uint32_t *slcr_lock            = (uint32_t*) 0xF8000004;
	volatile uint32_t *slcr_unlock          = (uint32_t*) 0xF8000008;

	printf("Program started.\r\n");

	// Unlock the SLCR registers
	*slcr_unlock = slcr_unlock_value;

	while (1) {

		sleep(10);
		printf("Starting DDR3 self refresh.\r\n");

		// ddrc.ctrl_reg1[reg_ddrc_selfref_en] = 1
		*ddrc_ctrl_reg1 |= (1 << 12);

		// ddrc.DRAM_param_reg3 [reg_ddrc_en_dfi_dram_clk_disable] = 1
		*ddrc_dram_param_reg3 |= (1 << 23);

		// while (slcr.DDR_CMD_STA[CMD_Q_NEMPTY] != 0)
		while ((*slcr_ddr_cmd_sta & 1) != 0);

		// while (ddrc.mode_sts_reg[ddrc_reg_operating_mode] != 3)
		while ((*ddrc_mode_sts_reg & 7) != 3);

		// slcr.DDR_CLK_CTRL[DDR_2XCLKACT] = 0
		*slcr_ddr_clk_ctrl &= ~(1 << 1);

		// slcr.DDR_CLK_CTRL[DDR_3XCLKACT] = 0
		*slcr_ddr_clk_ctrl &= ~(1 << 0);

		// slcr.DCI_CLK_CTRL[CLKACT] = 0
		*slcr_dci_clk_ctrl &= ~(1 << 0);

		sleep(10);
		printf("Waking up DDR3 memory.\r\n");

		// Reactivate memory
		*slcr_dci_clk_ctrl |= (1 << 0);
		*slcr_ddr_clk_ctrl |= (1 << 0);
		*slcr_ddr_clk_ctrl |= (1 << 1);
		*ddrc_dram_param_reg3 &= ~(1 << 23);
		*ddrc_ctrl_reg1 &= ~(1 << 12);

	}

	return XST_SUCCESS;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Captcha loading...