Skip to content

Commit

Permalink
ddr5: Add sdram_mr_read command
Browse files Browse the repository at this point in the history
It reads MRs from DDR5 DRAM modules.
First argument selects subchannel:
  0 - A,
  1 - B.
If PHY is connected to single channel any
value is valid, both will map to the same device.
Second argument selects device using PDA address.
Third argument selects exact MR to be read.

Signed-off-by: Maciej Dudek <[email protected]>
  • Loading branch information
mtdudek committed Nov 30, 2023
1 parent 77b227a commit 8fa242f
Show file tree
Hide file tree
Showing 15 changed files with 614 additions and 388 deletions.
45 changes: 45 additions & 0 deletions litex/soc/software/bios/cmds/cmd_litedram.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <generated/soc.h>
#include <generated/csr.h>
#include <generated/mem.h>
#include <generated/sdram_phy.h>
#include <libbase/i2c.h>

#include <liblitedram/sdram.h>
Expand Down Expand Up @@ -387,6 +388,50 @@ static void sdram_mr_write_handler(int nb_params, char **params)
}
define_command(sdram_mr_write, sdram_mr_write_handler, "Write SDRAM Mode Register", LITEDRAM_CMDS);

#ifdef SDRAM_PHY_DDR5
/**
* Command "sdram_mr_read"
*
* Read SDRAM Mode Register (only DDR5)
*
*/
static void sdram_mr_read_handler(int nb_params, char **params)
{
char *c;
uint8_t channel;
uint8_t device;
uint8_t reg;

if (nb_params < 3) {
printf("sdram_mr_read <channel> <device> <reg>");
return;
}

channel = strtoul(params[0], &c, 0);
if (*c != 0 || channel > 1) {
printf("Incorrect channel");
return;
}

device = strtoul(params[1], &c, 0);
if (*c != 0 || device == 15) {
printf("Incorrect device");
return;
}

reg = strtoul(params[2], &c, 0);
if (*c != 0) {
printf("Incorrect reg");
return;
}
sdram_software_control_on();
printf("Reading from channel:%d device:%d MR%d\n", channel, device, reg);
printf("Value:%02x\n", sdram_mode_register_read(channel, device, reg));
sdram_software_control_off();
}
define_command(sdram_mr_read, sdram_mr_read_handler, "Read SDRAM Mode Register", LITEDRAM_CMDS);
#endif // SDRAM_PHY_DDR5

#endif /* CSR_SDRAM_BASE */

/**
Expand Down
34 changes: 34 additions & 0 deletions litex/soc/software/libbase/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,39 @@ void ddr5_i2c_reset(void)
busy_wait(60);
i2c_oe_scl_sda(0, 1, 1);
}
#else

void i2c_reset(void) {};
bool i2c_write(unsigned char slave_addr, unsigned int addr,
const unsigned char *data, unsigned int len, unsigned int addr_size) {
printf("warning:I2C_write: No I2C defined.\n");
printf("warning:I2C_write: slave_addr:%hhx\n addr:%x\n", slave_addr, addr);
return false;
};

bool i2c_read(unsigned char slave_addr, unsigned int addr,
unsigned char *data, unsigned int len, bool send_stop, unsigned int addr_size) {
printf("warning:I2C_read: No I2C defined.\n");
printf("warning:I2C_read: slave_addr:%hhx\n addr:%x\n", slave_addr, addr);
return false;
}
bool i2c_poll(unsigned char slave_addr) {
printf("warning:I2C_poll: No I2C defined.\n");
return false;
};
int i2c_send_init_cmds(void) {
return 0;
};
struct i2c_dev *get_i2c_devs(void) {
return 0;
};
int get_i2c_devs_count(void) {
return 0;
};
void set_i2c_active_dev(int dev) {};
int get_i2c_active_dev(void) {
return -1;
};
void ddr5_i2c_reset(void) {};

#endif /* CONFIG_HAS_I2C */
8 changes: 7 additions & 1 deletion litex/soc/software/liblitedram/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
include ../include/generated/variables.mak
include $(SOC_DIRECTORY)/software/common.mak

OBJECTS = sdram.o bist.o sdram_dbg.o sdram_spd.o utils.o accessors.o sdram_rcd.o ddr5_training.o ddr5_helpers.o
OBJECTS = sdram.o bist.o sdram_dbg.o sdram_spd.o utils.o accessors.o sdram_rcd.o
OBJECTS += ddr5/ddr5_spd_parse.o ddr5/eye_detection_helper.o ddr5_training.o ddr5_helpers.o


all: liblitedram.a

Expand All @@ -11,6 +13,10 @@ liblitedram.a: $(OBJECTS)
# pull in dependency info for *existing* .o files
-include $(OBJECTS:.o=.d)

ddr5/%.o: $(LIBLITEDRAM_DIRECTORY)/ddr5/%.c
mkdir -p ddr5
$(compile)

%.o: $(LIBLITEDRAM_DIRECTORY)/%.c
$(compile)

Expand Down
226 changes: 226 additions & 0 deletions litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include <liblitedram/ddr5/ddr5_spd_parse.h>

#include <stdbool.h>
#include <inttypes.h>
#include <stdio.h>

#include <liblitedram/sdram_spd.h>
#include <liblitedram/ddr5_helpers.h>

#if defined(CSR_SDRAM_BASE) && defined(SDRAM_PHY_DDR5)

enum module_type read_module_type(uint8_t spd) {
#ifdef DDR5_RDIMM_SIM
return RDIMM;
#endif // DDR5_RDIMM_SIM
uint8_t module_type;
if (!sdram_read_spd(spd, 3, &module_type, 1, false)) {
printf("Couldn't read the SPD and check the module type. Defaulting to UDIMM.\n");
return UDIMM;
}

// Module type is in the lower nibble
return module_type & 0x0f;
}

uint8_t read_module_width(uint8_t spd) {
// TODO: change to actual spd data, when PHY will handle variable widths
return SDRAM_PHY_DQ_DQS_RATIO;
uint8_t buf;

// Module width is stored in SPD[6][7:5]
// 000: x4
// 001: x8
// 010: x16
// 011: x32

if (!sdram_read_spd(spd, 6, &buf, 1, false)) {
printf("Couldn't read module width from the SPD, defaulting to x%d.\n", SDRAM_PHY_DQ_DQS_RATIO);
return SDRAM_PHY_DQ_DQS_RATIO;
}

// minimal supported is x4
uint8_t shift = (buf & 0xe0) >> 5;
uint8_t module_width = 4 << shift;
return module_width;
}

uint8_t read_module_ranks(uint8_t spd) {
uint8_t buf;

// Module ranks count is stored in SPD[234][5:3]
// 000: 1
// 001: 2
// 010: 3
// .
// .
// .
// 111: 8

if (!sdram_read_spd(spd, 234, &buf, 1, false)) {
printf("Couldn't read module ranks from the SPD, defaulting to x%d.\n", 1);
return 1;
}

// minimal supported is x4
uint8_t shift = (buf & 0x38) >> 3;
uint8_t module_ranks = shift + 1;

return module_ranks;
}

uint8_t read_module_channels(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[235][6:5]
// 00: 1
// 01: 2

if (!sdram_read_spd(spd, 235, &buf, 1, false)) {
printf("Couldn't read module channels from the SPD, defaulting to x%d.\n", CHANNELS);
return CHANNELS;
}

// minimal supported is x4
uint8_t shift = (buf & 0x60) >> 5;
uint8_t module_channels = shift + 1;

return module_channels;
}


uint16_t read_module_rcd_manufacturer(uint8_t spd) {
uint8_t buf[2];

// Module channels count is stored in SPD[240:241]

if (!sdram_read_spd(spd, 240, &buf[0], 1, false)) {
printf("Couldn't read module RCD manufacturer from the SPD, defaulting to x%d.\n", 0);
return 0;
}
if (!sdram_read_spd(spd, 241, &buf[1], 1, false)) {
printf("Couldn't read module RCD manufacturer from the SPD, defaulting to x%d.\n", 0);
return 0;
}
uint16_t val;
val = *(uint16_t*)buf;
printf("RCD manufacturer: %x\n", val);

return val;
}

uint8_t read_module_rcd_device_type(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[240:241]

if (!sdram_read_spd(spd, 242, &buf, 1, false)) {
printf("Couldn't read module RCD device type from the SPD, defaulting to x%d.\n", 0);
return 0;
}
printf("RCD type: %x\n", buf);

return buf;
}

uint8_t read_module_rcd_device_rev(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[240:241]

if (!sdram_read_spd(spd, 243, &buf, 1, false)) {
printf("Couldn't read module RCD device rev from the SPD, defaulting to x%d.\n", 0);
return 0;
}
printf("RCD rev: %x\n", buf);

return buf;
}

uint8_t read_module_enabled_clock(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[248]
// [0]: QACK: 0 enable/1 disable
// [1]: QBCK: 0 enable/1 disable
// [2]: QCCK: 0 enable/1 disable
// [3]: QDCK: 0 enable/1 disable
// [5]: BCK: 0 enable/1 disable (LRDIMM)

if (!sdram_read_spd(spd, 248, &buf, 1, false)) {
printf("Couldn't read module clock enables from the SPD, defaulting to x%d.\n", 0);
return 0;
}

return buf & 0x2f;
}

uint8_t read_module_enabled_ca(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[249]
// [0]: QACA: 0 enable/1 disable
// [1]: QBCA: 0 enable/1 disable
// [2]: DCS1_n: 0 enable/1 disable
// [3]: BCS_n: 0 enable/1 disable
// [4]: QxCA13: 0 enable/1 disable
// [5]: QACSx_n: 0 enable/1 disable
// [6]: QBCSx_n: 0 enable/1 disable

if (!sdram_read_spd(spd, 249, &buf, 1, false)) {
printf("Couldn't read module CA enables from the SPD, defaulting to x%d.\n", 0);
return 0;
}

return buf & 0x7f;
}

uint8_t read_module_qck_setup(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[250]
// [1:0]: QACK: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES
// [3:2]: QBCK: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES
// [5:4]: QCCK: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES
// [7:6]: QDCK: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES

if (!sdram_read_spd(spd, 250, &buf, 1, false)) {
printf("Couldn't read module QCK setup from the SPD, defaulting to x%d.\n", 0);
return 0;
}

return buf & 0xff;
}

uint8_t read_module_qca_qcs_setup(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[252]
// [1:0]: QxCA: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES
// [5:4]: QxCS: 00 20Ohm/ 01 14Ohm /10 10Ohm /11 RES

if (!sdram_read_spd(spd, 252, &buf, 1, false)) {
printf("Couldn't read module QCA/QCS setup from the SPD, defaulting to x%d.\n", 0);
return 0;
}

return buf & 0x33;
}

uint8_t read_module_slew_rates(uint8_t spd) {
uint8_t buf;

// Module channels count is stored in SPD[252]
// [1:0]: QxCK: 00 12-20 V/ns/ 01 14-27 V/ns /10 RES /11 RES
// [3:2]: QxCA: 00 4-7 V/ns/ 01 6-10 V/ns /10 2.7-4.5 V/ns /11 RES
// [5:4]: QxCS: 00 4-7 V/ns/ 01 6-10 V/ns /10 2.7-4.5 V/ns /11 RES

if (!sdram_read_spd(spd, 254, &buf, 1, false)) {
printf("Couldn't read module slew rates from the SPD, defaulting to x%d.\n", 0);
return 0;
}

return buf & 0x3f;
}

#endif // defined(CSR_SDRAM_BASE) && defined(SDRAM_PHY_DDR5)
Loading

0 comments on commit 8fa242f

Please sign in to comment.