From 8fa242f3b67aa29f317a0c4b9622204b550cbf4f Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 30 Nov 2023 16:33:22 +0100 Subject: [PATCH] ddr5: Add `sdram_mr_read` command 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 --- litex/soc/software/bios/cmds/cmd_litedram.c | 45 ++ litex/soc/software/libbase/i2c.c | 34 ++ litex/soc/software/liblitedram/Makefile | 8 +- .../liblitedram/ddr5/ddr5_spd_parse.c | 226 ++++++++++ .../liblitedram/ddr5/ddr5_spd_parse.h | 94 ++++ .../liblitedram/ddr5/eye_detection_helper.c | 47 ++ .../liblitedram/ddr5/eye_detection_helper.h | 44 ++ litex/soc/software/liblitedram/ddr5_helpers.c | 24 +- litex/soc/software/liblitedram/ddr5_helpers.h | 15 +- .../soc/software/liblitedram/ddr5_training.c | 422 +++--------------- .../soc/software/liblitedram/ddr5_training.h | 8 +- litex/soc/software/liblitedram/sdram.c | 7 + litex/soc/software/liblitedram/sdram.h | 4 + litex/soc/software/liblitedram/sdram_rcd.c | 11 + litex/soc/software/liblitedram/sdram_rcd.h | 13 +- 15 files changed, 614 insertions(+), 388 deletions(-) create mode 100644 litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.c create mode 100644 litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.h create mode 100644 litex/soc/software/liblitedram/ddr5/eye_detection_helper.c create mode 100644 litex/soc/software/liblitedram/ddr5/eye_detection_helper.h diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index b4e75be03f..fe0b318e24 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -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 "); + 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 */ /** diff --git a/litex/soc/software/libbase/i2c.c b/litex/soc/software/libbase/i2c.c index f7a966a65b..d8f0c1d274 100644 --- a/litex/soc/software/libbase/i2c.c +++ b/litex/soc/software/libbase/i2c.c @@ -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 */ diff --git a/litex/soc/software/liblitedram/Makefile b/litex/soc/software/liblitedram/Makefile index e7293be547..0c24914f2c 100644 --- a/litex/soc/software/liblitedram/Makefile +++ b/litex/soc/software/liblitedram/Makefile @@ -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 @@ -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) diff --git a/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.c b/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.c new file mode 100644 index 0000000000..46a1c97d48 --- /dev/null +++ b/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.c @@ -0,0 +1,226 @@ +#include + +#include +#include +#include + +#include +#include + +#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) diff --git a/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.h b/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.h new file mode 100644 index 0000000000..30328aea91 --- /dev/null +++ b/litex/soc/software/liblitedram/ddr5/ddr5_spd_parse.h @@ -0,0 +1,94 @@ +#ifndef LIBLITEDRAM_DDR5_DDR5_SPD_PARSE_H +#define LIBLITEDRAM_DDR5_DDR5_SPD_PARSE_H +#include +#ifdef CSR_SDRAM_BASE +#include + +enum module_type { + RDIMM = 0b0001, + UDIMM = 0b0010, + SODIMM = 0b0011, + LRDIMM = 0b0100, + DDIM = 0b1010, + SOLDER_DOWN = 0b1011, +}; + +/** + * read_module_type + * + * Reads the 3rd byte of the SPD and extracts the module type. + * If the SPD cannot be read, it defaults to the UDIMM. + */ +enum module_type read_module_type(uint8_t spd); + +/** + * read_module_width + * Reads the 6th byte of the SPD and extracts the primary SDRAM + * module width. If SPD cannot be read, it defaults to the + * SDRAM_PHY_DQ_DQS_RATIO. + */ +uint8_t read_module_width(uint8_t spd); + +/** + * read_module_ranks + * Reads 234h byte of the SPD and extracts number of ranks. + */ +uint8_t read_module_ranks(uint8_t spd); + +/** + * read_module_channels + * Reads 235th byte of th SPD and extracts DIMM channel count. + */ +uint8_t read_module_channels(uint8_t spd); + +/** + * read_module_rcd_manufacturer + * Reads 240th and 241th bytes of the SPD and extracts + * RCD manufacturer. + */ +uint16_t read_module_rcd_manufacturer(uint8_t spd); + +/** + * read_module_rcd_device_type + * Reads 242th byte of the SPD and returns device type. + */ +uint8_t read_module_rcd_device_type(uint8_t spd); + +/** + * read_module_rcd_device_rev + * Reads 243th byte of the SPD and returns device revision. + */ +uint8_t read_module_rcd_device_rev(uint8_t spd); + +/** + * read_module_enabled_clock + * Reads 248th byte of the SPD and parses QCK enabled drivers + */ +uint8_t read_module_enabled_clock(uint8_t spd); + +/** + * read_module_enabled_ca + * Reads 249th byte of the SPD and parses Qx enabled drivers. + */ +uint8_t read_module_enabled_ca(uint8_t spd); + +/** + * read_module_qck_setup + * Reads 250th byte of the SPD and pareses QCK driver strengths. + */ +uint8_t read_module_qck_setup(uint8_t spd); + +/** + * read_module_qca_qcs_setup + * Reads 252th byte of the SPD and pareses QCA and QCS driver strengths. + */ +uint8_t read_module_qca_qcs_setup(uint8_t spd); + +/** + * read_module_slew_rates + * Reads 254th byte of the SPD and parses QCK, QCA and QCS slew rates. + */ +uint8_t read_module_slew_rates(uint8_t spd); + +#endif // CSR_SDRAM_BASE +#endif // LIBLITEDRAM_DDR5_DDR5_SPD_PARSE_H diff --git a/litex/soc/software/liblitedram/ddr5/eye_detection_helper.c b/litex/soc/software/liblitedram/ddr5/eye_detection_helper.c new file mode 100644 index 0000000000..9c355851e7 --- /dev/null +++ b/litex/soc/software/liblitedram/ddr5/eye_detection_helper.c @@ -0,0 +1,47 @@ +#include + +#if defined(CSR_SDRAM_BASE) && defined(SDRAM_PHY_DDR5) + +#include +#include +#include + +#include + +#define MAX(a, b) (a > b ? a : b) +#define MIN(a, b) (a < b ? a : b) + +static int32_t helper_arr[2*MAX(64, SDRAM_PHY_DELAYS)]; +static int32_t helper_arr_it; + +void clear_helper_arr(void) { + helper_arr_it = 0; + for (int i = 0; i < sizeof(helper_arr)/sizeof(int); ++i) + helper_arr[i] = 0; +} + +void set_helper_arr_value_and_advance(uint32_t value) { + helper_arr[helper_arr_it++] = value; +} + +int one_in_helper_arr(int max) { + if (helper_arr[0]) return -1; + for (int it = 1; it < max; ++it) { + if (helper_arr[it]) return 1; + } + return 0; +} + +void find_eye_in_helper_arr(int *left, int *right, int max) { + for (int it = 0; it < 2* max; ++it) { + if (helper_arr[it] && *right == UNSET_DELAY) + *right = it; + if (!helper_arr[it] && *right != UNSET_DELAY) { + *left = it; + return; + } + } + if (helper_arr[2*max - 1]) + *left = 2 * max; +} +#endif // defined(CSR_SDRAM_BASE) && defined(SDRAM_PHY_DDR5) diff --git a/litex/soc/software/liblitedram/ddr5/eye_detection_helper.h b/litex/soc/software/liblitedram/ddr5/eye_detection_helper.h new file mode 100644 index 0000000000..923c8fe056 --- /dev/null +++ b/litex/soc/software/liblitedram/ddr5/eye_detection_helper.h @@ -0,0 +1,44 @@ +#ifndef LIBLITEDRAM_DDR5_EYE_DETECTION_HELPER_H +#define LIBLITEDRAM_DDR5_EYE_DETECTION_HELPER_H +#include +#ifdef CSR_SDRAM_BASE +#include + +#include + +// Use max int16_t, all Fs could be interpreted as -1 +#define UNSET_DELAY 0xefff + +/** + * clear_helper_arr + * This function clears helper array and resets access iterator + * to 0. + */ +void clear_helper_arr(void); + +/** + * set_helper_arr_value_and_advance + * Stores `value` in helper array. It also advances access iterator by 1. + */ +void set_helper_arr_value_and_advance(uint32_t value); + +/** + * one_in_helper_arr + * Searches helper array from 0 up to `max`, `max` not included. + * Returns: + * -1 if index 0 has non zero value; + * 1 if any other index has non-zero value, + * 0 in all other cases. + */ +int one_in_helper_arr(int max); + +/** + * find_eye_in_helper_arr + * Searches helper array from 0 to 2*`max`, `max` not included. + * It returns first index with non-zero value in the `right` variable, + * and in the `left` variable it returns first index with value of zero that + * is after `right`. + */ +void find_eye_in_helper_arr(int *left, int *right, int max); +#endif // CSR_SDRAM_BASE +#endif // LIBLITEDRAM_DDR5_EYE_DETECTION_HELPER_H diff --git a/litex/soc/software/liblitedram/ddr5_helpers.c b/litex/soc/software/liblitedram/ddr5_helpers.c index 2d56cfd1bf..64fd00a926 100644 --- a/litex/soc/software/liblitedram/ddr5_helpers.c +++ b/litex/soc/software/liblitedram/ddr5_helpers.c @@ -452,6 +452,20 @@ void read_registers(int channel, int rank, int module, int width) { } } +/** + * ca_check_if_has_line13 + * + * Detect if CA13 is present. + * Requires to already be in the CATM. + */ +int check_ca_13th_line(int32_t channel, int32_t rank) { + cmd_injector(channel, 0xf, 0, 1<<13, 0, 0, 1, 0); + cmd_injector(channel, 0x1, 1< #if defined(CSR_SDRAM_BASE) && defined(SDRAM_PHY_DDR5) +#include +#include #include #include @@ -52,25 +54,9 @@ // \______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // --------------<============>------------- -static int32_t helper_arr[2*MAX(64, SDRAM_PHY_DELAYS)]; -static int32_t helper_arr_it; static int32_t helper_modules_without_shift; static int32_t helper_modules_seen; -static void full_clear_helper_arr(void) { - helper_arr_it = 0; - helper_modules_without_shift = 0; - helper_modules_seen = 0; - for (int i = 0; i < sizeof(helper_arr)/sizeof(int); ++i) - helper_arr[i] = 0; -} - -static void clear_helper_arr(void) { - helper_arr_it = 0; - for (int i = 0; i < sizeof(helper_arr)/sizeof(int); ++i) - helper_arr[i] = 0; -} - static int reduce_cs(uint32_t cs, int modules) { uint32_t ok, module; ok = 1; @@ -79,29 +65,6 @@ static int reduce_cs(uint32_t cs, int modules) { return !!ok; } - -// -1 starts with `1`, 0 doesn't have `1`, 1 in all other cases -static int one_in_helper_arr(int max) { - if (helper_arr[0]) return -1; - for (int it = 1; it < max; ++it) { - if (helper_arr[it]) return 1; - } - return 0; -} - -static void find_eye_in_helper_arr(int *left, int *right, int max) { - for (int it = 0; it < 2* max; ++it) { - if (helper_arr[it] && *right == UNSET_DELAY) - *right = it; - if (!helper_arr[it] && *right != UNSET_DELAY) { - *left = it; - return; - } - } - if (helper_arr[2*max - 1]) - *left = 2 * max; -} - static void CS_scan_single(const training_ctx_t *const ctx, int32_t channel, int32_t rank, int shift_0101) { int csdly; @@ -129,7 +92,7 @@ static void CS_scan_single(const training_ctx_t *const ctx, int32_t channel, int } works = works & works_ ? 0 : works | works_; printf("%d", reduce_cs(works, ctx->modules)); - helper_arr[helper_arr_it++] = reduce_cs(works, ctx->modules); + set_helper_arr_value_and_advance(reduce_cs(works, ctx->modules)); ctx->cs.inc_dly(channel, rank, 0); } ctx->cs.rst_dly(channel, rank, 0); @@ -137,7 +100,9 @@ static void CS_scan_single(const training_ctx_t *const ctx, int32_t channel, int static void CS_scan(const training_ctx_t *const ctx, int32_t channel, int32_t rank) { int shift = 1; - full_clear_helper_arr(); + clear_helper_arr(); + helper_modules_without_shift = 0; + helper_modules_seen = 0; ctx->cs.rst_dly(channel, rank, 0); // Enter CS training @@ -157,7 +122,9 @@ static void CS_scan(const training_ctx_t *const ctx, int32_t channel, int32_t ra printf("|\n"); break; case 0: - full_clear_helper_arr(); + clear_helper_arr(); + helper_modules_without_shift = 0; + helper_modules_seen = 0; printf("\nChange polarization|"); CS_scan_single(ctx, channel, rank, 1); printf("|"); @@ -228,10 +195,10 @@ static void CA_setup_array(training_ctx_t *const ctx) { * Depending on the die density and usage of die stacking, * CA13 may be used or not. */ -static void CA_check_lines(training_ctx_t *const ctx, int32_t channel) { +static void CA_check_lines(training_ctx_t *const ctx, int32_t channel, int rank) { if (ctx->training_type == HOST_DRAM) { ctx->ca.enter_training_mode(channel, 0); - if (ctx->ca.has_line13(channel)) + if (ctx->ca.has_line13(channel, rank)) ctx->ca.line_count = 14; else ctx->ca.line_count = 13; @@ -247,7 +214,7 @@ static void CA_scan_single(training_ctx_t *const ctx, int32_t channel, int32_t r for (cadly = 0; cadly < ctx->max_delay_taps; cadly++) { works = ctx->ca.check(channel, rank, address, shift_back); printf("%d", !!works); - helper_arr[helper_arr_it++] = works; + set_helper_arr_value_and_advance(works); ctx->ca.inc_dly(channel, rank, address); } ctx->ca.rst_dly(channel, rank, address); @@ -514,60 +481,71 @@ static void CK_CS_CA_finalize_timings(training_ctx_t *const ctx , int channel) { CS_CA_rescan(ctx, new_ckdly, channel); } +void sdram_ddr5_ca_cs_prep(training_ctx_t *const ctx) { + if (ctx->rate == DDR && ctx->training_type != RCD_DRAM) + disable_dfi_2n_mode(); + CA_setup_array(ctx); +} + #ifdef SKIP_NO_DELAYS -void sdram_ddr5_cs_ca_training(training_ctx_t *const ctx , int channel) { +static bool sdram_ddr5_cs_ca_channel_training(training_ctx_t *const ctx , int channel) { printf("CS/CA training impossible\n" "Keeping DRAM in 2N mode\n"); + return false; } #else -void sdram_ddr5_cs_ca_training(training_ctx_t *const ctx , int channel) { +static bool sdram_ddr5_cs_ca_channel_training(training_ctx_t *const ctx , int channel) { #ifndef SDRAM_PHY_ADDRESS_DELAY_CAPABLE printf("WARNING:\n" "PHY does not have IO delays on address lines!!!\n" "BIOS will try to check if 1N mode is possible, but it may be unstable.\n" "Build BIOS with -DSKIP_NO_DELAYS, to skip CS/CA training and force 2N mode.\n"); #endif // SDRAM_PHY_ADDRESS_DELAY_CAPABLE - int32_t _channel, _max_channel; uint8_t CS_success, CA_success; - if (ctx->rate == DDR && ctx->training_type != RCD_DRAM) - disable_dfi_2n_mode(); - - _channel = channel; - _max_channel = channel + 1; - if (channel == -1) { - _channel = 0; - _max_channel = ctx->channels; - } - CS_success = 1; CA_success = 1; - CA_setup_array(ctx); - for (; _channel < _max_channel; ++_channel) { - ctx->ck.rst_dly(_channel, 0, 0); - printf("Subchannel:%c CS training\n", (char)('A'+_channel)); - CS_training(ctx, _channel, &CS_success); - ctx->CS_CA_successful &= CS_success; + ctx->ck.rst_dly(channel, 0, 0); + printf("Subchannel:%c CS training\n", (char)('A'+channel)); + CS_training(ctx, channel, &CS_success); #ifndef KEEP_GOING_ON_DRAM_ERROR - if (!ctx->CS_CA_successful) - return; + if (!CS_success) + return false; #endif // KEEP_GOING_ON_DRAM_ERROR - printf("CA training\n"); - CA_check_lines(ctx, _channel); - CA_training(ctx, _channel, &CA_success); - ctx->CS_CA_successful &= CA_success; + printf("CA training\n"); + CA_check_lines(ctx, channel, 0); //FIXME: Add support for multiple ranks + CA_training(ctx, channel, &CA_success); #ifndef KEEP_GOING_ON_DRAM_ERROR + if (!CA_success) { + return false; +#endif // KEEP_GOING_ON_DRAM_ERROR + } + +#ifndef KEEP_GOING_ON_DRAM_ERROR + return (CS_success & CA_success); +#endif // KEEP_GOING_ON_DRAM_ERROR + return true; +} +#endif // SKIP_NO_DELAYS + +static void sdram_ddr5_cs_ca_training(training_ctx_t *const ctx) { + int32_t _channel; + uint8_t CA_BUS_success; + + CA_BUS_success = 1; + sdram_ddr5_ca_cs_prep(ctx); + + for (_channel = 0; _channel < ctx->channels; ++_channel) { + CA_BUS_success &= sdram_ddr5_cs_ca_channel_training(ctx, _channel); +#ifndef KEEP_GOING_ON_DRAM_ERROR + ctx->CS_CA_successful &= CA_BUS_success; if (!ctx->CS_CA_successful) return; #endif // KEEP_GOING_ON_DRAM_ERROR } - ctx->CS_CA_successful &= (CS_success & CA_success); - if (ctx->CS_CA_successful) { - CK_CS_CA_finalize_timings(ctx, channel); - } + CK_CS_CA_finalize_timings(ctx, -1); } -#endif // SKIP_NO_DELAYS // MR2:OP[7] value to use, whenever MR2 is being modified static int use_internal_write_timing = 0; @@ -626,7 +604,10 @@ static bool sdram_ddr5_check_enumerate(int rank, int width, int channels, int mo send_mrw(channel, rank, MODULE_BROADCAST, 2, 0|use_internal_write_timing|single_cycle_MPC); busy_wait_us(1); } +#ifndef KEEP_GOING_ON_DRAM_ERROR return ok; +#endif // KEEP_GOING_ON_DRAM_ERROR + return true; } static bool dram_enumerate(training_ctx_t *const ctx , int rank) { @@ -1842,261 +1823,16 @@ bool sdram_ddr5_write_training(training_ctx_t *const ctx ) { return true; } -/** - * ca_check_if_has_line13 - * - * Detect if CA13 is present. - * Requires to already be in the CATM. - */ -static int ca_check_if_has_line13(int32_t channel) { - cmd_injector(channel, 0xf, 0, 1<<13, 0, 0, 1, 0); - cmd_injector(channel, 0x1, 1, 1<<13, 0, 0, 1, 0); - store_continuous(channel); - - return and_sample(channel); -} - -#if defined(CONFIG_HAS_I2C) -enum module_type { - RDIMM = 0b0001, - UDIMM = 0b0010, - SODIMM = 0b0011, - LRDIMM = 0b0100, - DDIM = 0b1010, - SOLDER_DOWN = 0b1011, -}; - -/** - * read_module_type - * - * Reads the 3rd byte of the SPD and extracts the module type. - * If the SPD cannot be read, it defaults to the UDIMM. - */ -static enum module_type read_module_type(uint8_t spd) { - 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; -} - -//static uint8_t read_module_width(uint8_t spd) { -// 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; -//} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} -#endif // defined(CONFIG_HAS_I2C) - training_ctx_t host_dram_ctx; -#if defined(CONFIG_HAS_I2C) training_ctx_t host_rcd_ctx; training_ctx_t rcd_dram_ctx; -#endif // defined(CONFIG_HAS_I2C) static void init_structs(void) { host_dram_ctx = (training_ctx_t) DEFAULT_HOST_DRAM; -#if defined(CONFIG_HAS_I2C) host_rcd_ctx = (training_ctx_t) DEFAULT_HOST_RCD; rcd_dram_ctx = (training_ctx_t) DEFAULT_RCD_DRAM; -#endif // defined(CONFIG_HAS_I2C) } -#if defined(CONFIG_HAS_I2C) static void rcd_init(training_ctx_t *const ctx ) { // Issue a VR_ENABLE command to the PMIC uint8_t cmd = 0xa0; @@ -2125,11 +1861,9 @@ static void rcd_init(training_ctx_t *const ctx ) { ctx->ca.check = dca_check_if_works_ddr_MONTAGE_QUIRK; } - sdram_ddr5_cs_ca_training(ctx, -1); -#ifndef KEEP_GOING_ON_DRAM_ERROR + sdram_ddr5_cs_ca_training(ctx); if(!ctx->CS_CA_successful) return; -#endif // KEEP_GOING_ON_DRAM_ERROR busy_wait(6); // FIXME: this function should initialize all RCDs @@ -2169,7 +1903,6 @@ static void rcd_init(training_ctx_t *const ctx ) { busy_wait(2); } -#endif // defined(CONFIG_HAS_I2C) /** * sdram_ddr5_flow @@ -2191,13 +1924,8 @@ void sdram_ddr5_flow(void) { training_ctx_t *base_ctx = &host_dram_ctx; bool is_rdimm = false; -#if defined(CONFIG_HAS_I2C) ddr5_i2c_reset(); -#ifdef DDR5_RDIMM_SIM - is_rdimm = true; -#else is_rdimm = read_module_type(0) == RDIMM; -#endif // DDR5_RDIMM_SIM // FIXME: handle multiple sticks and SPDs int die_width = SDRAM_PHY_DQ_DQS_RATIO; //FIXME: change to SPD value when PHY works `read_module_width(0);` if (is_rdimm) { @@ -2209,12 +1937,10 @@ void sdram_ddr5_flow(void) { rcd_dram_ctx.die_width = die_width; rcd_dram_ctx.ranks = read_module_ranks(0); // FIXME: handle multiple sticks and SPDs rcd_dram_ctx.channels = read_module_channels(0); // FIXME: handle multiple sticks and SPDs -#endif // defined(CONFIG_HAS_I2C) reset_all_phy_regs(host_dram_ctx.channels, host_dram_ctx.ranks, host_dram_ctx.all_ca_count, host_dram_ctx.modules, host_dram_ctx.die_width); -#if defined(CONFIG_HAS_I2C) if (is_rdimm) { printf("Detected RDIMM. Initializing RCD and running Host->RCD training\n"); ddrphy_CSRModule_rdimm_mode_write(1); @@ -2223,10 +1949,8 @@ void sdram_ddr5_flow(void) { base_ctx->rate = host_rcd_ctx.rate; base_ctx->CS_CA_successful &= host_rcd_ctx.CS_CA_successful; base_ctx->manufacturer = host_rcd_ctx.manufacturer; -#ifndef KEEP_GOING_ON_DRAM_ERROR if(!base_ctx->CS_CA_successful) return; -#endif // KEEP_GOING_ON_DRAM_ERROR if (base_ctx->manufacturer == 0x9D86) { base_ctx->cs.enter_training_mode = enter_qcstm_RAMBUS_QUIRK; base_ctx->cs.check = qcs_check_if_works_RAMBUS_QUIRK; @@ -2236,11 +1960,6 @@ void sdram_ddr5_flow(void) { reset_sequence(base_ctx->ranks); #endif // SKIP_RESET_SEQUENCE } -#else -#ifndef SKIP_RESET_SEQUENCE - reset_sequence(base_ctx->ranks); -#endif // SKIP_RESET_SEQUENCE -#endif // defined(CONFIG_HAS_I2C) dram_start_sequence(base_ctx->ranks); @@ -2257,7 +1976,9 @@ void sdram_ddr5_flow(void) { #endif // SKIP_MRS_SEQUENCE #ifndef SKIP_CSCA_TRAINING for (int channel = 0; channel < base_ctx->channels; ++channel) { - sdram_ddr5_cs_ca_training(base_ctx, channel); + sdram_ddr5_ca_cs_prep(base_ctx); + sdram_ddr5_cs_ca_channel_training(base_ctx, channel); + CK_CS_CA_finalize_timings(base_ctx, channel); } #endif // SKIP_CSCA_TRAINING } else { @@ -2266,19 +1987,17 @@ void sdram_ddr5_flow(void) { setup_dram_mrs_sequence(rank); #endif // SKIP_MRS_SEQUENCE #ifndef SKIP_CSCA_TRAINING - sdram_ddr5_cs_ca_training(base_ctx, -1); + sdram_ddr5_cs_ca_training(base_ctx); #endif // SKIP_CSCA_TRAINING } + if(!base_ctx->CS_CA_successful) + return; + #ifdef SKIP_CSCA_TRAINING disable_dfi_2n_mode(); #endif // SKIP_CSCA_TRAINING -#ifndef KEEP_GOING_ON_DRAM_ERROR - if(!base_ctx->CS_CA_successful) - return; -#endif // KEEP_GOING_ON_DRAM_ERROR - use_1n_mode = 1<<2; if(is_rdimm) { enter_ca_pass(0); @@ -2322,11 +2041,8 @@ void sdram_ddr5_flow(void) { send_mpc(channel, rank, 0x58, 0); for (int rank = 0; rank < base_ctx->ranks; ++rank) { - if(dram_enumerate(base_ctx, rank)) - continue; -#ifndef KEEP_GOING_ON_DRAM_ERROR - return; -#endif // KEEP_GOING_ON_DRAM_ERROR + if(!dram_enumerate(base_ctx, rank)) + return; } // Enable DQ RTT after enumerate @@ -2343,25 +2059,19 @@ void sdram_ddr5_flow(void) { } } -#if defined(CONFIG_HAS_I2C) if (is_rdimm) { base_ctx = &host_dram_ctx; host_dram_ctx.ranks = rcd_dram_ctx.ranks; host_dram_ctx.RDIMM = rcd_dram_ctx.RDIMM; } -#endif // defined(CONFIG_HAS_I2C) base_ctx->ranks = 1; //FIXME: when PHY works with multiple ranks if (!sdram_ddr5_read_training(base_ctx)) { -#ifndef KEEP_GOING_ON_DRAM_ERROR return; -#endif // KEEP_GOING_ON_DRAM_ERROR } if (!sdram_ddr5_write_training(base_ctx)) { -#ifndef KEEP_GOING_ON_DRAM_ERROR return; -#endif // KEEP_GOING_ON_DRAM_ERROR } } diff --git a/litex/soc/software/liblitedram/ddr5_training.h b/litex/soc/software/liblitedram/ddr5_training.h index 633297170f..62ec507a4d 100644 --- a/litex/soc/software/liblitedram/ddr5_training.h +++ b/litex/soc/software/liblitedram/ddr5_training.h @@ -11,9 +11,6 @@ // DRAM Mode Registers Definitions #define DRAM_SCRATCH_PAD 63 -// Use max int16_t, all Fs could be interpreted as -1 -#define UNSET_DELAY 0xefff - // max CL is 66 (JESD79-5A 3.5.2) // if in 2N Mode, 1 more cycle is used for the command #define MAX_READ_CYCLE_DELAY (66 + 1) @@ -61,7 +58,7 @@ typedef struct { delay_checker_ca_t check; - int (*has_line13)(int32_t channel); + int (*has_line13)(int32_t channel, int32_t rank); } ca; struct { int delays[CHANNELS][2]; @@ -117,7 +114,7 @@ typedef struct { .inc_dly = ca_inc, \ .rst_dly = ca_rst, \ .check = ca_check_if_works, \ - .has_line13 = ca_check_if_has_line13, \ + .has_line13 = check_ca_13th_line, \ }, \ .training_type = HOST_DRAM, \ \ @@ -211,7 +208,6 @@ typedef struct { } -void sdram_ddr5_cs_ca_training(training_ctx_t *ctx, int channel); bool sdram_ddr5_read_training(training_ctx_t *ctx); bool sdram_ddr5_write_training(training_ctx_t *ctx); diff --git a/litex/soc/software/liblitedram/sdram.c b/litex/soc/software/liblitedram/sdram.c index c86a77a4e5..8d58c8f184 100644 --- a/litex/soc/software/liblitedram/sdram.c +++ b/litex/soc/software/liblitedram/sdram.c @@ -294,6 +294,13 @@ void sdram_mode_register_write(char reg, int value) { } #endif +#ifdef SDRAM_PHY_DDR5 +uint8_t sdram_mode_register_read(int channel, int pda, int reg) { + send_mrr(channel, 0, reg); + return recover_mrr_value(channel, pda, SDRAM_PHY_DQ_DQS_RATIO); //TODO: use correct width when PHY supports multiple widths +} +#endif + #if !defined(SDRAM_PHY_DDR5) && defined(CSR_DDRPHY_BASE) /*-----------------------------------------------------------------------*/ diff --git a/litex/soc/software/liblitedram/sdram.h b/litex/soc/software/liblitedram/sdram.h index 8b7eae151c..c107e35364 100644 --- a/litex/soc/software/liblitedram/sdram.h +++ b/litex/soc/software/liblitedram/sdram.h @@ -44,6 +44,10 @@ void sdram_software_control_off(void); /* Mode Register */ /*-----------------------------------------------------------------------*/ void sdram_mode_register_write(char reg, int value); +#ifdef SDRAM_PHY_DDR5 +uint8_t sdram_mode_register_read(int channel, int pda, int reg); +#endif + /*-----------------------------------------------------------------------*/ /* Write Leveling */ diff --git a/litex/soc/software/liblitedram/sdram_rcd.c b/litex/soc/software/liblitedram/sdram_rcd.c index 8e586bfd07..67cf9c49af 100644 --- a/litex/soc/software/liblitedram/sdram_rcd.c +++ b/litex/soc/software/liblitedram/sdram_rcd.c @@ -203,4 +203,15 @@ bool sdram_rcd_write(uint8_t rcd, uint8_t dev, uint8_t function, uint8_t page_nu #endif /* defined(SDRAM_PHY_DDR5) || defined(SDRAM_PHY_DDR4_RDIMM) */ +#elif defined(CSR_SDRAM_BASE) + +bool sdram_rcd_read(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, uint8_t *data, bool byte_read) { + return false; +} +bool sdram_rcd_write(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, const uint8_t *data, uint8_t size, bool byte_write) { + return false; +} + #endif /* CSR_SDRAM_BASE && CONFIG_HAS_I2C */ diff --git a/litex/soc/software/liblitedram/sdram_rcd.h b/litex/soc/software/liblitedram/sdram_rcd.h index a3eab65159..6830b50ca2 100644 --- a/litex/soc/software/liblitedram/sdram_rcd.h +++ b/litex/soc/software/liblitedram/sdram_rcd.h @@ -25,11 +25,20 @@ extern "C" { #define RCD_READ_CMD 0b00 #define RCD_WRITE_CMD(size) (((size) >> 1) + 1) -bool sdram_rcd_read(uint8_t rcd, uint8_t dev, uint8_t function, uint8_t page_num, uint8_t reg_num, uint8_t *data, bool byte_read); -bool sdram_rcd_write(uint8_t rcd, uint8_t dev, uint8_t function, uint8_t page_num, uint8_t reg_num, const uint8_t *data, uint8_t size, bool byte_write); +bool sdram_rcd_read(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, uint8_t *data, bool byte_read); +bool sdram_rcd_write(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, const uint8_t *data, uint8_t size, bool byte_write); #endif /* defined(SDRAM_PHY_DDR5) || defined(SDRAM_PHY_DDR4_RDIMM) */ +#elif defined(CSR_SDRAM_BASE) + +bool sdram_rcd_read(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, uint8_t *data, bool byte_read); +bool sdram_rcd_write(uint8_t rcd, uint8_t dev, uint8_t function, + uint8_t page_num, uint8_t reg_num, const uint8_t *data, uint8_t size, bool byte_write); + #endif /* CSR_SDRAM_BASE && CONFIG_HAS_I2C */ #ifdef __cplusplus