From ce8bd132995a9c968649c3b57a0a3d3fa2c66b02 Mon Sep 17 00:00:00 2001 From: Tommy Cox Date: Wed, 27 Jul 2022 16:23:37 -0600 Subject: [PATCH 1/7] Commit ECC patch to a new branch. Initial ECC patch for testing purposes. --- litex/soc/integration/soc.py | 69 +++++--- litex/soc/software/bios/cmds/cmd_litedram.c | 18 +- litex/soc/software/liblitedram/bist.c | 177 +++++++++++++------- litex/soc/software/liblitedram/bist.h | 4 +- 4 files changed, 172 insertions(+), 96 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index a9c7f5eee2..e4fb4560f2 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -440,21 +440,6 @@ def add_slave(self, name=None, slave=None, region=None): colorer(name, color="underline"), colorer("added", color="green"))) - def get_address_width(self, standard): - standard_from = self.standard - standard_to = standard - - # AXI or AXI-Lite SoC Bus and Wishbone requested: - if standard_from in ["axi", "axi-lite"] and standard_to in ["wishbone"]: - address_shift = log2_int(self.data_width//8) - return self.address_width - address_shift - # Wishbone SoC Bus and AXI, AXI-Lite requested: - if standard_from in ["wishbone"] and standard_to in ["axi", "axi-lite"]: - address_shift = log2_int(self.data_width//8) - return self.address_width + address_shift - # Else just return address_width: - return self.address_width - # Str ------------------------------------------------------------------------------------------ def __str__(self): r = "{}-bit {} Bus, {}GiB Address Space.\n".format( @@ -1053,7 +1038,7 @@ def add_cpu(self, name="vexriscv", variant="standard", reset_address=None, cfu=N name = "SoCDMABusHandler", standard = "wishbone", data_width = self.bus.data_width, - address_width = self.bus.get_address_width(standard="wishbone"), + address_width = self.bus.address_width, bursting = self.bus.bursting ) dma_bus = wishbone.Interface(data_width=self.bus.data_width) @@ -1413,7 +1398,8 @@ def add_jtagbone(self, name="jtagbone", chain=1): # Add SDRAM ------------------------------------------------------------------------------------ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, - with_bist = False, + with_bist = True, + with_ecc = True, with_soc_interconnect = True, l2_cache_size = 8192, l2_cache_min_data_width = 128, @@ -1427,6 +1413,7 @@ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, from litedram.frontend.wishbone import LiteDRAMWishbone2Native from litedram.frontend.axi import LiteDRAMAXI2Native from litedram.frontend.bist import LiteDRAMBISTGenerator, LiteDRAMBISTChecker + from litedram.frontend.ecc import LiteDRAMNativePortECC # LiteDRAM core. self.check_if_exists(name) @@ -1460,8 +1447,38 @@ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, # LiteDRAM BIST. if with_bist: - sdram_generator = LiteDRAMBISTGenerator(sdram.crossbar.get_port()) - sdram_checker = LiteDRAMBISTChecker( sdram.crossbar.get_port()) + if with_ecc: + ecc_port_w = sdram.crossbar.get_port() + user_port_w = LiteDRAMNativePort( + mode = ecc_port_w.mode, + address_width = ecc_port_w.address_width, + data_width = ecc_port_w.data_width//2 + ) + ecc_w = LiteDRAMNativePortECC( + user_port_w, ecc_port_w, + burst_cycles=user_port_w.data_width//8, + with_error_injection=True + ) + setattr(self.submodules, f"{name}_eccw", ecc_w) + + ecc_port_r = sdram.crossbar.get_port() + user_port_r = LiteDRAMNativePort( + mode = ecc_port_r.mode, + address_width = ecc_port_r.address_width, + data_width = ecc_port_r.data_width//2 + ) + ecc_r = LiteDRAMNativePortECC( + user_port_r, ecc_port_r, + burst_cycles=user_port_r.data_width//8, + with_error_injection=True + ) + setattr(self.submodules, f"{name}_eccr", ecc_r) + else: + user_port_w = sdram.crossbar.get_port() + user_port_r = sdram.crossbar.get_port() + + sdram_generator = LiteDRAMBISTGenerator(user_port_w) + sdram_checker = LiteDRAMBISTChecker(user_port_r) setattr(self.submodules, f"{name}_generator", sdram_generator) setattr(self.submodules, f"{name}_checker", sdram_checker) @@ -1804,7 +1821,7 @@ def add_sdcard(self, name="sdcard", mode="read+write", use_emulator=False, softw # Block2Mem DMA. if "read" in mode: - bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.get_address_width(standard="wishbone")) + bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width) self.submodules.sdblock2mem = SDBlock2MemDMA(bus=bus, endianness=self.cpu.endianness) self.comb += self.sdcore.source.connect(self.sdblock2mem.sink) dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus @@ -1812,20 +1829,20 @@ def add_sdcard(self, name="sdcard", mode="read+write", use_emulator=False, softw # Mem2Block DMA. if "write" in mode: - bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.get_address_width(standard="wishbone")) + bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width) self.submodules.sdmem2block = SDMem2BlockDMA(bus=bus, endianness=self.cpu.endianness) self.comb += self.sdmem2block.source.connect(self.sdcore.sink) dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus dma_bus.add_master("sdmem2block", master=bus) # Interrupts. - self.submodules.sdirq = EventManager() - self.sdirq.card_detect = EventSourcePulse(description="SDCard has been ejected/inserted.") + self.submodules.sdirq = EventManager() + self.sdirq.card_detect = EventSourcePulse(description="SDCard has been ejected/inserted.") if "read" in mode: self.sdirq.block2mem_dma = EventSourcePulse(description="Block2Mem DMA terminated.") if "write" in mode: self.sdirq.mem2block_dma = EventSourcePulse(description="Mem2Block DMA terminated.") - self.sdirq.cmd_done = EventSourceLevel(description="Command completed.") + self.sdirq.cmd_done = EventSourceLevel(description="Command completed.") self.sdirq.finalize() if "read" in mode: self.comb += self.sdirq.block2mem_dma.trigger.eq(self.sdblock2mem.irq) @@ -1876,7 +1893,7 @@ def add_sata(self, name="sata", phy=None, mode="read+write", with_identify=True) # Sector2Mem DMA. if "read" in mode: - bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.get_address_width(standard="wishbone")) + bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width) self.submodules.sata_sector2mem = LiteSATASector2MemDMA( port = self.sata_crossbar.get_port(), bus = bus, @@ -1886,7 +1903,7 @@ def add_sata(self, name="sata", phy=None, mode="read+write", with_identify=True) # Mem2Sector DMA. if "write" in mode: - bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.get_address_width(standard="wishbone")) + bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width) self.submodules.sata_mem2sector = LiteSATAMem2SectorDMA( bus = bus, port = self.sata_crossbar.get_port(), diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 132015b17e..c0ef049cb2 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -68,9 +68,9 @@ static void sdram_bist_handler(int nb_params, char **params) { char *c; int burst_length; - int random; + int amode; if (nb_params < 2) { - printf("sdram_bist "); + printf("sdram_bist "); return; } burst_length = strtoul(params[0], &c, 0); @@ -78,12 +78,20 @@ static void sdram_bist_handler(int nb_params, char **params) printf("Incorrect burst_length"); return; } - random = strtoul(params[1], &c, 0); + amode = strtoul(params[1], &c, 0); if (*c != 0) { - printf("Incorrect random"); + printf("Incorrect addr_mode"); return; } - sdram_bist(burst_length, random); + if (nb_params == 3) { + extern uint32_t rand_data; + rand_data = strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect rand_data"); + return; + } + } + sdram_bist(burst_length, amode); } define_command(sdram_bist, sdram_bist_handler, "Run SDRAM Build-In Self-Test", LITEDRAM_CMDS); #endif diff --git a/litex/soc/software/liblitedram/bist.c b/litex/soc/software/liblitedram/bist.c index 0445637924..782070bf23 100644 --- a/litex/soc/software/liblitedram/bist.c +++ b/litex/soc/software/liblitedram/bist.c @@ -2,6 +2,7 @@ // License: BSD #include +#include #if defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) #include #include @@ -12,9 +13,22 @@ #include +#if !defined(MAIN_RAM_SIZE) +#define MAIN_RAM_SIZE 0x08000000 +#endif + #define SDRAM_TEST_BASE 0x00000000 #define SDRAM_TEST_DATA_BYTES (CSR_SDRAM_DFII_PI0_RDDATA_SIZE*4) +#if defined(CSR_SDRAM_ECCR_BASE) +#define SDRAM_TEST_SIZE MAIN_RAM_SIZE/2 +#else +#define SDRAM_TEST_SIZE MAIN_RAM_SIZE +#endif + +/* If 0, generate sequential data; if 1, generate random data */ +uint32_t rand_data = 1; + uint32_t wr_ticks; uint32_t wr_length; uint32_t rd_ticks; @@ -31,7 +45,8 @@ __attribute__((unused)) static void cdelay(int i) #endif } -static uint32_t pseudo_random_bases[128] = { +#define PRB_SIZE (sizeof(pseudo_random_bases)/sizeof(pseudo_random_bases[0])) +static uint32_t pseudo_random_bases[] = { 0x000e4018,0x0003338d,0x00233429,0x001f589d, 0x001c922b,0x0011dc60,0x000d1e8f,0x000b20cf, 0x00360188,0x00041174,0x0003d065,0x000bfe34, @@ -66,61 +81,74 @@ static uint32_t pseudo_random_bases[128] = { 0x00027e36,0x000e51ae,0x002e7627,0x00275c9f, }; -void sdram_bist_loop(uint32_t loop, uint32_t burst_length, uint32_t random) { +static inline void sdram_generator_init(uint32_t base, uint32_t length) { + sdram_generator_reset_write(1); + sdram_generator_reset_write(0); + sdram_generator_random_write(rand_data); + sdram_generator_base_write(base); + sdram_generator_end_write(base + length); + sdram_generator_length_write(length); + cdelay(100); +} + +static inline void sdram_checker_init(uint32_t base, uint32_t length) { + sdram_checker_reset_write(1); + sdram_checker_reset_write(0); + sdram_checker_random_write(rand_data); + sdram_checker_base_write(base); + sdram_checker_end_write(base + length); + sdram_checker_length_write(length); + cdelay(100); +} + +static void sdram_clear_counts(void) { + wr_length = 0; + wr_ticks = 0; + rd_length = 0; + rd_ticks = 0; + rd_errors = 0; +} + +#define TARGET_BYTES 0x20000 +/* burst_length of 1, DMA transaction is about 468 cycles */ +/* burst_length > 0x400, DMA transaction is about 1 cycle per word */ + +void sdram_bist_loop(uint32_t burst_length, uint32_t amode) { int i; - uint32_t base; - uint32_t length; - length = burst_length*SDRAM_TEST_DATA_BYTES; + uint32_t base = SDRAM_TEST_BASE; + uint32_t length = burst_length*SDRAM_TEST_DATA_BYTES; + uint32_t loop_cnt = (TARGET_BYTES > length) ? TARGET_BYTES / length : 1; rd_errors = 0; - for(i=0; i<128; i++) { - if (random) - base = SDRAM_TEST_BASE + pseudo_random_bases[(i+loop)%128]*SDRAM_TEST_DATA_BYTES; - else - base = SDRAM_TEST_BASE + ((i+loop)%128)*SDRAM_TEST_DATA_BYTES; - if (i == 0) { - /* Prepare first write */ - sdram_generator_reset_write(1); - sdram_generator_reset_write(0); - sdram_generator_random_write(1); /* Random data */ - sdram_generator_base_write(base); - sdram_generator_end_write(base + length); - sdram_generator_length_write(length); - cdelay(100); - } + /* Prepare first write */ + sdram_generator_init(base, length); + for (i = 0; i < loop_cnt; i++) { /* Start write */ sdram_generator_start_write(1); /* Prepare next read */ - sdram_checker_reset_write(1); - sdram_checker_reset_write(0); - sdram_checker_random_write(1); /* Random data */ - sdram_checker_base_write(base); - sdram_checker_end_write(base + length); - sdram_checker_length_write(length); - cdelay(100); + sdram_checker_init(base, length); /* Wait write */ - while(sdram_generator_done_read() == 0); + while (sdram_generator_done_read() == 0); /* Get write results */ - wr_length += length; wr_ticks += sdram_generator_ticks_read(); + wr_length += length; /* Start read */ sdram_checker_start_write(1); - if (i != 127) { - if (random) - base = SDRAM_TEST_BASE + pseudo_random_bases[(i+1+loop)%128]*SDRAM_TEST_DATA_BYTES; - else - base = SDRAM_TEST_BASE + ((i+1+loop)%128)*SDRAM_TEST_DATA_BYTES; + if (i != loop_cnt-1) { + switch (amode) { + case 1: + base = base + length; + if (base >= (SDRAM_TEST_BASE+SDRAM_TEST_SIZE)) base = SDRAM_TEST_BASE; + break; + case 2: + base = SDRAM_TEST_BASE + pseudo_random_bases[i%PRB_SIZE]*SDRAM_TEST_DATA_BYTES; + break; + } /* Prepare next write */ - sdram_generator_reset_write(1); - sdram_generator_reset_write(0); - sdram_generator_random_write(1); /* Random data */ - sdram_generator_base_write(base); - sdram_generator_end_write(base + length); - sdram_generator_length_write(length); - cdelay(100); + sdram_generator_init(base, length); } /* Wait read */ - while(sdram_checker_done_read() == 0); + while (sdram_checker_done_read() == 0); /* Get read results */ rd_ticks += sdram_checker_ticks_read(); rd_errors += sdram_checker_errors_read(); @@ -135,46 +163,69 @@ static uint32_t compute_speed_mibs(uint32_t length, uint32_t ticks) { return speed; } -void sdram_bist(uint32_t burst_length, uint32_t random) +void sdram_bist(uint32_t burst_length, uint32_t amode) { uint32_t i; + uint32_t uc; /* update count */ + uint32_t uf; /* update frequency */ uint32_t total_length; uint32_t total_errors; - printf("Starting SDRAM BIST with burst_length=%d and random=%d\n", burst_length, random); - + printf("Starting SDRAM BIST with burst_length=%lu and addr_mode=%lu\n", burst_length, amode); +#if defined(CSR_SDRAM_ECCR_BASE) + sdram_eccr_clear_write(1); + sdram_eccr_clear_write(0); +#endif + sdram_clear_counts(); i = 0; + uc = 0; + uf = burst_length * 2 + 2; total_length = 0; total_errors = 0; - for(;;) { + for (;;) { /* Exit on key pressed */ if (readchar_nonblock()) break; + /* Header */ + if (uc%8 == 0 && wr_length == 0) { +#if defined(CSR_SDRAM_ECCR_BASE) + printf("WR-BW(MiB/s) RD-BW(MiB/s) TESTED(MiB) ERRORS SEC DED\n"); +#else + printf("WR-BW(MiB/s) RD-BW(MiB/s) TESTED(MiB) ERRORS\n"); +#endif + } + /* Bist loop */ - sdram_bist_loop(i, burst_length, random); + sdram_bist_loop(burst_length, amode); - /* Results */ - if (i%1000 == 0) { - printf("WR-SPEED(MiB/s) RD-SPEED(MiB/s) TESTED(MiB) ERRORS\n"); - } - if (i%100 == 100-1) { - printf("%15u %15u %12u %12u\n", - compute_speed_mibs(wr_length, wr_ticks), - compute_speed_mibs(rd_length, rd_ticks), - total_length/(1024*1024), - total_errors); + i++; + /* Results, update about once per second */ + if ((burst_length < 0x100 && i%uf == 0) || + (wr_ticks >= CONFIG_CLOCK_FREQUENCY/2)) { + uc++; total_length += wr_length; total_errors += rd_errors; +#if defined(CSR_SDRAM_ECCR_BASE) + printf("%12lu %12lu %12lu %10lu %10lu %10lu\n", + compute_speed_mibs(wr_length, wr_ticks), + compute_speed_mibs(rd_length, rd_ticks), + total_length/(1024*1024), + total_errors, + sdram_eccr_sec_errors_read(), + sdram_eccr_ded_errors_read()); +#else + printf("%12lu %12lu %12lu %10lu\n", + compute_speed_mibs(wr_length, wr_ticks), + compute_speed_mibs(rd_length, rd_ticks), + total_length/(1024*1024), + total_errors); +#endif + /* Clear length/ticks/errors */ - wr_length = 0; - wr_ticks = 0; - rd_length = 0; - rd_ticks = 0; - rd_errors = 0; + sdram_clear_counts(); } - i++; } } diff --git a/litex/soc/software/liblitedram/bist.h b/litex/soc/software/liblitedram/bist.h index 2f0ea1a37e..b8ac653215 100644 --- a/litex/soc/software/liblitedram/bist.h +++ b/litex/soc/software/liblitedram/bist.h @@ -4,7 +4,7 @@ #ifndef __SDRAM_BIST_H #define __SDRAM_BIST_H -void sdram_bist_loop(uint32_t loop, uint32_t burst_length, uint32_t random); -void sdram_bist(uint32_t burst_length, uint32_t random); +void sdram_bist_loop(uint32_t burst_length, uint32_t amode); +void sdram_bist(uint32_t burst_length, uint32_t amode); #endif /* __SDRAM_BIST_H */ From 3280f6b3231502b4d39d5c429ff3fa2f7a601bff Mon Sep 17 00:00:00 2001 From: Tommy Cox Date: Wed, 27 Jul 2022 16:26:54 -0600 Subject: [PATCH 2/7] Change target git repos/branches to modified copies. --- litex_setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litex_setup.py b/litex_setup.py index 7972f8b38d..ff37fe89c7 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -76,12 +76,12 @@ def __init__(self, url, clone="regular", develop=True, sha1=None, branch="master # ------------------ "pythondata-software-picolibc": GitRepo(url="https://github.com/litex-hub/", clone="recursive"), "pythondata-software-compiler_rt": GitRepo(url="https://github.com/litex-hub/"), - "litex": GitRepo(url="https://github.com/enjoy-digital/", tag=True), + "litex": GitRepo(url="https://github.com/TommyCox/", branch="bist_ecc", tag=True), # LiteX Cores Ecosystem. # ---------------------- "liteeth": GitRepo(url="https://github.com/enjoy-digital/", tag=True), - "litedram": GitRepo(url="https://github.com/enjoy-digital/", tag=True), + "litedram": GitRepo(url="https://github.com/TommyCox", tag=True), "litepcie": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesata": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesdcard": GitRepo(url="https://github.com/enjoy-digital/", tag=True), From b32e0a50503a4087519087ce117f92b6cb233103 Mon Sep 17 00:00:00 2001 From: gslloyd Date: Mon, 1 Aug 2022 17:16:10 -0600 Subject: [PATCH 3/7] Fix link to litedram --- litex_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex_setup.py b/litex_setup.py index ff37fe89c7..55b6e5b820 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -81,7 +81,7 @@ def __init__(self, url, clone="regular", develop=True, sha1=None, branch="master # LiteX Cores Ecosystem. # ---------------------- "liteeth": GitRepo(url="https://github.com/enjoy-digital/", tag=True), - "litedram": GitRepo(url="https://github.com/TommyCox", tag=True), + "litedram": GitRepo(url="https://github.com/TommyCox/", tag=True), "litepcie": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesata": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesdcard": GitRepo(url="https://github.com/enjoy-digital/", tag=True), From 3ee2a86ec278f309833e8580bf7b4d4335d39642 Mon Sep 17 00:00:00 2001 From: Scott Lloyd Date: Sat, 6 Aug 2022 23:29:41 -0600 Subject: [PATCH 4/7] add commands to BIOS --- litex/soc/integration/soc.py | 11 +- litex/soc/software/bios/cmds/cmd_litedram.c | 162 ++++++++-- litex/soc/software/bios/cmds/cmd_mem.c | 276 ++++++++++++----- litex/soc/software/libbase/Makefile | 3 +- litex/soc/software/libbase/memtest.c | 35 ++- litex/soc/software/libbase/memtest.h | 9 + litex/soc/software/libbase/popc.c | 13 + litex/soc/software/libbase/popc.h | 14 + litex/soc/software/liblitedram/bist.c | 314 +++++++++++++++----- litex/soc/software/liblitedram/bist.h | 10 +- litex/soc/software/liblitedram/sdram.c | 12 +- 11 files changed, 658 insertions(+), 201 deletions(-) create mode 100644 litex/soc/software/libbase/popc.c create mode 100644 litex/soc/software/libbase/popc.h diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index e4fb4560f2..41fde9d456 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -1397,9 +1397,9 @@ def add_jtagbone(self, name="jtagbone", chain=1): self.bus.add_master(name=name, master=jtagbone.wishbone) # Add SDRAM ------------------------------------------------------------------------------------ - def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, + def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, software_debug=False, with_bist = True, - with_ecc = True, + with_ecc = False, with_soc_interconnect = True, l2_cache_size = 8192, l2_cache_min_data_width = 128, @@ -1481,6 +1481,7 @@ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, sdram_checker = LiteDRAMBISTChecker(user_port_r) setattr(self.submodules, f"{name}_generator", sdram_generator) setattr(self.submodules, f"{name}_checker", sdram_checker) + self.add_config("BIST_DATA_WIDTH", user_port_r.data_width) if not with_soc_interconnect: return @@ -1580,7 +1581,7 @@ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, # L2 Cache if l2_cache_size != 0: - # Insert L2 cache inbetween Wishbone bus and LiteDRAM + # Insert L2 cache in between Wishbone bus and LiteDRAM l2_cache_size = max(l2_cache_size, int(2*port.data_width/8)) # Use minimal size if lower l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2 l2_cache_data_width = max(port.data_width, l2_cache_min_data_width) @@ -1605,6 +1606,10 @@ def add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None, base_address = self.bus.regions["main_ram"].origin ) + # Debug. + if software_debug: + self.add_constant("SDRAM_DEBUG") + # Add Ethernet --------------------------------------------------------------------------------- def add_ethernet(self, name="ethmac", phy=None, phy_cd="eth", dynamic_ip=False, software_debug=False, data_width = 8, diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index c0ef049cb2..175aa3b22d 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -4,12 +4,13 @@ #include #include #include -#include -#include #include #include +#include + #include +#include #include #include @@ -52,49 +53,170 @@ define_command(sdram_cal, sdram_cal_handler, "Calibrate SDRAM", LITEDRAM_CMDS); #if defined(CSR_SDRAM_BASE) static void sdram_test_handler(int nb_params, char **params) { - memtest((unsigned int *)MAIN_RAM_BASE, MAIN_RAM_SIZE/32); + memtest((unsigned int *)MAIN_RAM_BASE, MAIN_RAM_SIZE); } define_command(sdram_test, sdram_test_handler, "Test SDRAM", LITEDRAM_CMDS); #endif +#if defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) /** - * Command "sdram_bist" + * Command "sdram_bist_pat" * - * Run SDRAM Build-In Self-Test + * Set SDRAM Build-In Self-Test (BIST) Pattern * */ -#if defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) -static void sdram_bist_handler(int nb_params, char **params) +static void sdram_pat_handler(int nb_params, char **params) { char *c; - int burst_length; - int amode; - if (nb_params < 2) { - printf("sdram_bist "); + uint32_t value; + + if (nb_params < 1) { + printf("sdram_bist_pat "); return; } - burst_length = strtoul(params[0], &c, 0); + value = strtoul(params[0], &c, 0); if (*c != 0) { printf("Incorrect burst_length"); return; } - amode = strtoul(params[1], &c, 0); + sdram_bist_pattern(value); +} +define_command(sdram_bist_pat, sdram_pat_handler, "Set SDRAM Build-In Self-Test Pattern", LITEDRAM_CMDS); + +/** + * Command "sdram_bist_gen" + * + * Run SDRAM Build-In Self-Test (BIST) Generator + * + */ +static void sdram_gen_handler(int nb_params, char **params) +{ + char *c; + uint32_t base; + uint32_t length; + int dmode = 2; /* default: random data*/ + + if (nb_params < 2) { + printf("sdram_bist_gen []\n"); + printf("base : base address (starts at zero)\n"); + printf("length : DMA block size in bytes\n"); + printf("data_mode: 0=pattern, 1=inc, 2=random"); + return; + } + base = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect base"); + return; + } + length = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect length"); + return; + } + if (nb_params > 2) { + dmode = strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect data_mode"); + return; + } + } + sdram_bist_gen(base, length, dmode); +} +define_command(sdram_bist_gen, sdram_gen_handler, "Run SDRAM Build-In Self-Test Generator", LITEDRAM_CMDS); + +/** + * Command "sdram_bist_chk" + * + * Run SDRAM Build-In Self-Test (BIST) Checker + * + */ +static void sdram_chk_handler(int nb_params, char **params) +{ + char *c; + uint32_t base; + uint32_t length; + int dmode = 2; /* default: random data*/ + + if (nb_params < 2) { + printf("sdram_bist_chk []\n"); + printf("base : base address (starts at zero)\n"); + printf("length : DMA block size in bytes\n"); + printf("data_mode: 0=pattern, 1=inc, 2=random"); + return; + } + base = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect base"); + return; + } + length = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect length"); + return; + } + if (nb_params > 2) { + dmode = strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect data_mode"); + return; + } + } + sdram_bist_chk(base, length, dmode); +} +define_command(sdram_bist_chk, sdram_chk_handler, "Run SDRAM Build-In Self-Test Checker", LITEDRAM_CMDS); + +/** + * Command "sdram_bist" + * + * Run SDRAM Build-In Self-Test (BIST) + * + */ +static void sdram_bist_handler(int nb_params, char **params) +{ + char *c; + uint32_t length; + int amode = 1; /* default: increment */ + int dmode = 2; /* default: random data*/ + int wmode = 2; /* default: write before each read */ + + if (nb_params < 1) { + printf("sdram_bist [] [] []\n"); + printf("length : DMA block size in bytes\n"); + printf("addr_mode : 0=fixed (starts at zero), 1=inc, 2=random\n"); + printf("data_mode : 0=pattern, 1=inc, 2=random\n"); + printf("write_mode: 0=no_write, 1=write_once, 2=write_and_read"); + return; + } + length = strtoul(params[0], &c, 0); if (*c != 0) { - printf("Incorrect addr_mode"); + printf("Incorrect length"); return; } - if (nb_params == 3) { - extern uint32_t rand_data; - rand_data = strtoul(params[2], &c, 0); + if (nb_params > 1) { + amode = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect addr_mode"); + return; + } + } + if (nb_params > 2) { + dmode = strtoul(params[2], &c, 0); if (*c != 0) { - printf("Incorrect rand_data"); + printf("Incorrect data_mode"); return; } } - sdram_bist(burst_length, amode); + if (nb_params > 3) { + wmode = strtoul(params[3], &c, 0); + if (*c != 0) { + printf("Incorrect write_once"); + return; + } + } + sdram_bist(length, amode, dmode, wmode); } define_command(sdram_bist, sdram_bist_handler, "Run SDRAM Build-In Self-Test", LITEDRAM_CMDS); -#endif +#endif /* defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) */ #ifdef CSR_DDRPHY_RDPHASE_ADDR /** diff --git a/litex/soc/software/bios/cmds/cmd_mem.c b/litex/soc/software/bios/cmds/cmd_mem.c index 02a1220ada..d8cf0655a1 100644 --- a/litex/soc/software/bios/cmds/cmd_mem.c +++ b/litex/soc/software/bios/cmds/cmd_mem.c @@ -11,6 +11,8 @@ #include "../command.h" #include "../helpers.h" +#define MAX_ERR_PRINT 10 + /** * Command "mem_list" * @@ -22,7 +24,6 @@ static void mem_list_handler(int nb_params, char **params) printf("Available memory regions:\n"); puts(MEM_REGIONS); } - define_command(mem_list, mem_list_handler, "List available memory regions", MEM_CMDS); /** @@ -35,10 +36,10 @@ static void mem_read_handler(int nb_params, char **params) { char *c; unsigned int *addr; - unsigned int length; + unsigned int length = 4; if (nb_params < 1) { - printf("mem_read
[length]"); + printf("mem_read
[]"); return; } addr = (unsigned int *)strtoul(params[0], &c, 0); @@ -46,19 +47,16 @@ static void mem_read_handler(int nb_params, char **params) printf("Incorrect address"); return; } - if (nb_params == 1) { - length = 4; - } else { + if (nb_params > 1) { length = strtoul(params[1], &c, 0); - if(*c != 0) { - printf("\nIncorrect length"); + if (*c != 0) { + printf("Incorrect length"); return; } } dump_bytes(addr, length, (unsigned long)addr); } - define_command(mem_read, mem_read_handler, "Read address space", MEM_CMDS); /** @@ -72,65 +70,143 @@ static void mem_write_handler(int nb_params, char **params) char *c; void *addr; unsigned int value; - unsigned int count; - unsigned int size; - unsigned int i; + unsigned int count = 1; + unsigned int size = 4; if (nb_params < 2) { - printf("mem_write
[count] [size]"); + printf("mem_write
[] []"); return; } - size = 4; addr = (void *)strtoul(params[0], &c, 0); - if (*c != 0) { printf("Incorrect address"); return; } value = strtoul(params[1], &c, 0); - if(*c != 0) { + if (*c != 0) { printf("Incorrect value"); return; } - if (nb_params == 2) { - count = 1; - } else { + if (nb_params > 2) { count = strtoul(params[2], &c, 0); - if(*c != 0) { + if (*c != 0) { printf("Incorrect count"); return; } } - if (nb_params == 4) + if (nb_params > 3) { size = strtoul(params[3], &c, 0); - - for (i = 0; i < count; i++) { - switch (size) { - case 1: - *(uint8_t *)addr = value; - addr += 1; - break; - case 2: - *(uint16_t *)addr = value; - addr += 2; - break; - case 4: - *(uint32_t *)addr = value; - addr += 4; - break; - default: + if (*c != 0) { printf("Incorrect size"); return; } } -} + switch (size) { + case 1: { + uint8_t *ptr = addr, *end = ptr+count; + do *ptr++ = value; while (ptr < end); + break;} + case 2: { + uint16_t *ptr = addr, *end = ptr+count; + do *ptr++ = value; while (ptr < end); + break;} + case 4: { + uint32_t *ptr = addr, *end = ptr+count; + do *ptr++ = value; while (ptr < end); + break;} + default: + printf("Only a size of 1, 2, or 4 is supported"); + return; + } +} define_command(mem_write, mem_write_handler, "Write address space", MEM_CMDS); +/** + * Command "mem_check" + * + * Memory check + * + */ +static void mem_check_handler(int nb_params, char **params) +{ + char *c; + void *addr; + unsigned int value; + unsigned int count = 1; + unsigned int size = 4; + uint32_t errors = 0; + + if (nb_params < 2) { + printf("mem_check
[] []"); + return; + } + + addr = (void *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + + value = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect value"); + return; + } + + if (nb_params > 2) { + count = strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect count"); + return; + } + } + + if (nb_params > 3) { + size = strtoul(params[3], &c, 0); + if (*c != 0) { + printf("Incorrect size"); + return; + } + } + + switch (size) { + case 1: { + uint8_t *ptr = addr, *end = ptr+count, val = value; + do { + if (*ptr++ != val && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%02x, expected: 0x%02x\n", + (unsigned long)(ptr - 1), *(ptr - 1), val); + } while (ptr < end); + break;} + case 2: { + uint16_t *ptr = addr, *end = ptr+count, val = value; + do { + if (*ptr++ != val && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%04x, expected: 0x%04x\n", + (unsigned long)(ptr - 1), *(ptr - 1), val); + } while (ptr < end); + break;} + case 4: { + uint32_t *ptr = addr, *end = ptr+count, val = value; + do { + if (*ptr++ != val && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08lx\n", + (unsigned long)(ptr - 1), *(ptr - 1), val); + } while (ptr < end); + break;} + default: + printf("Only a size of 1, 2, or 4 is supported"); + return; + } + printf("ERRORS: %lu\n", errors); +} +define_command(mem_check, mem_check_handler, "Check address space", MEM_CMDS); + /** * Command "mem_copy" * @@ -142,11 +218,11 @@ static void mem_copy_handler(int nb_params, char **params) char *c; unsigned int *dstaddr; unsigned int *srcaddr; - unsigned int count; + unsigned int count = 1; unsigned int i; if (nb_params < 2) { - printf("mem_copy [count]"); + printf("mem_copy []"); return; } @@ -162,9 +238,7 @@ static void mem_copy_handler(int nb_params, char **params) return; } - if (nb_params == 2) { - count = 1; - } else { + if (nb_params > 2) { count = strtoul(params[2], &c, 0); if (*c != 0) { printf("Incorrect count"); @@ -175,9 +249,50 @@ static void mem_copy_handler(int nb_params, char **params) for (i = 0; i < count; i++) *dstaddr++ = *srcaddr++; } - define_command(mem_copy, mem_copy_handler, "Copy address space", MEM_CMDS); +/** + * Command "mem_inject_err" + * + * Memory Inject Errors, flip random bits + * + */ +static void mem_inject_handler(int nb_params, char **params) +{ + char *c; + unsigned int *addr; + unsigned long size; + unsigned int errors = 1; + + if (nb_params < 2) { + printf("mem_inject_err
[]"); + return; + } + + addr = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + + size = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect size"); + return; + } + + if (nb_params > 2) { + errors = strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect error count"); + return; + } + } + + memtest_inject_errors(addr, size, errors, 0x1, 1); +} +define_command(mem_inject_err, mem_inject_handler, "Inject errors in address space", MEM_CMDS); + /** * Command "mem_test" * @@ -191,7 +306,7 @@ static void mem_test_handler(int nb_params, char **params) unsigned long maxsize = ~0uL; if (nb_params < 1) { - printf("mem_test []"); + printf("mem_test
[]"); return; } @@ -201,7 +316,7 @@ static void mem_test_handler(int nb_params, char **params) return; } - if (nb_params >= 2) { + if (nb_params > 1) { maxsize = strtoul(params[1], &c, 0); if (*c != 0) { printf("Incorrect size"); @@ -229,7 +344,7 @@ static void mem_speed_handler(int nb_params, char **params) bool random = false; if (nb_params < 2) { - printf("mem_speed [] []"); + printf("mem_speed
[] []"); return; } @@ -245,16 +360,16 @@ static void mem_speed_handler(int nb_params, char **params) return; } - if (nb_params >= 3) { - read_only = (bool) strtoul(params[2], &c, 0); + if (nb_params > 2) { + read_only = (bool)strtoul(params[2], &c, 0); if (*c != 0) { printf("Incorrect readonly value"); return; } } - if (nb_params >= 4) { - random = (bool) strtoul(params[3], &c, 0); + if (nb_params > 3) { + random = (bool)strtoul(params[3], &c, 0); if (*c != 0) { printf("Incorrect random value"); return; @@ -273,40 +388,41 @@ define_command(mem_speed, mem_speed_handler, "Test memory speed", MEM_CMDS); */ static void mem_cmp_handler(int nb_params, char **params) { - char *c; - unsigned int *addr1; - unsigned int *addr2; - unsigned int count; - unsigned int i; + char *c; + unsigned int *addr1; + unsigned int *addr2; + unsigned int count; + unsigned int i; bool same = true; - if (nb_params < 3) { - printf("mem_cmp "); - return; - } - - addr1 = (unsigned int *)strtoul(params[0], &c, 0); - if (*c != 0) { - printf("Incorrect addr1"); - return; - } - - addr2 = (unsigned int *)strtoul(params[1], &c, 0); - if (*c != 0) { - printf("Incorrect addr2"); - return; - } - - count = strtoul(params[2], &c, 0); - if (*c != 0) { + + if (nb_params < 3) { + printf("mem_cmp "); + return; + } + + addr1 = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect addr1"); + return; + } + + addr2 = (unsigned int *)strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect addr2"); + return; + } + + count = strtoul(params[2], &c, 0); + if (*c != 0) { printf("Incorrect count"); return; - } + } - for (i = 0; i < count; i++) - if (*addr1++ != *addr2++){ + for (i = 0; i < count; i++) + if (*addr1++ != *addr2++){ printf("Different memory content:\naddr1: 0x%08lx, content: 0x%08x\naddr2: 0x%08lx, content: 0x%08x\n", - (long unsigned int)(addr1 - 1), *(addr1 - 1), - (long unsigned int)(addr2 - 1), *(addr2 - 1)); + (long unsigned int)(addr1 - 1), *(addr1 - 1), + (long unsigned int)(addr2 - 1), *(addr2 - 1)); same = false; } diff --git a/litex/soc/software/libbase/Makefile b/litex/soc/software/libbase/Makefile index 6b8736b171..7c46c94855 100755 --- a/litex/soc/software/libbase/Makefile +++ b/litex/soc/software/libbase/Makefile @@ -11,7 +11,8 @@ OBJECTS = \ uart.o \ spiflash.o \ i2c.o \ - isr.o + isr.o \ + popc.o all: libbase.a diff --git a/litex/soc/software/libbase/memtest.c b/litex/soc/software/libbase/memtest.c index 273a8b025f..4e396f5d9f 100644 --- a/litex/soc/software/libbase/memtest.c +++ b/litex/soc/software/libbase/memtest.c @@ -3,6 +3,7 @@ #include #include +#include /* rand */ #include #include @@ -38,6 +39,9 @@ #endif #define MEMTEST_ADDR_RANDOM 0 +#define PROGRESS_CNT 0x80000 + + static unsigned int seed_to_data_32(unsigned int seed, int random) { return random ? lfsr(32, seed) : seed + 1; @@ -206,11 +210,13 @@ int memtest_data(unsigned int *addr, unsigned long size, int random, struct memt for(i=0; i 0) { + size_t offset = rand() & (size/sizeof(word)-1); /* word offset */ + unsigned shift = rand() % ((sizeof(word)*8+1)-bits); + array[offset] ^= (word)pat << shift; + cnt--; + } + flush_cpu_dcache(); + flush_l2_cache(); +} + void memspeed(unsigned int *addr, unsigned long size, bool read_only, bool random) { volatile unsigned long *array = (unsigned long *)addr; diff --git a/litex/soc/software/libbase/memtest.h b/litex/soc/software/libbase/memtest.h index 230598ce86..1718a16c07 100644 --- a/litex/soc/software/libbase/memtest.h +++ b/litex/soc/software/libbase/memtest.h @@ -30,6 +30,15 @@ int memtest_bus(unsigned int *addr, unsigned long size); int memtest_addr(unsigned int *addr, unsigned long size, int random); int memtest_data(unsigned int *addr, unsigned long size, int random, struct memtest_config *config); +/* + Description: inject errors (flip bits) in a memory range + addr (word aligned) and size (in bytes): determines the memory range + cnt: number of errors to inject + pat: bit pattern that determines the bits to flip in a word + bits: width of pattern in bits +*/ +void memtest_inject_errors(unsigned int *addr, unsigned long size, unsigned cnt, unsigned pat, unsigned bits); + void memspeed(unsigned int *addr, unsigned long size, bool read_only, bool random); int memtest(unsigned int *addr, unsigned long maxsize); diff --git a/litex/soc/software/libbase/popc.c b/litex/soc/software/libbase/popc.c new file mode 100644 index 0000000000..cffe38dede --- /dev/null +++ b/litex/soc/software/libbase/popc.c @@ -0,0 +1,13 @@ +// Count number of bits in a 32-bit word, faster version than a while loop +// see: https://www.johndcook.com/blog/2020/02/21/popcount/ + +#include "popc.h" + +unsigned int popcount(unsigned int x) { + x -= ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += (x >> 8); + x += (x >> 16); + return x & 0x0000003F; +} diff --git a/litex/soc/software/libbase/popc.h b/litex/soc/software/libbase/popc.h new file mode 100644 index 0000000000..db397f440d --- /dev/null +++ b/litex/soc/software/libbase/popc.h @@ -0,0 +1,14 @@ +#ifndef __POPC_H +#define __POPC_H + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int popcount(unsigned int x); + +#ifdef __cplusplus +} +#endif + +#endif /* __POPC_H */ diff --git a/litex/soc/software/liblitedram/bist.c b/litex/soc/software/liblitedram/bist.c index 782070bf23..3d3a4a374d 100644 --- a/litex/soc/software/liblitedram/bist.c +++ b/litex/soc/software/liblitedram/bist.c @@ -2,23 +2,31 @@ // License: BSD #include -#include #if defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) #include -#include #include #include #include #include +#include /* MAIN_RAM_BASE, MAIN_RAM_SIZE */ +#include /* CONFIG_BIST_DATA_WIDTH */ +#include /* popcount */ +#include /* memtest_inject_errors */ #include +/* un-comment to flip random bits in SDRAM */ +#define INJECT_ERRORS + +/* un-comment to print detail about errors */ +#define DISPLAY_ERRORS + #if !defined(MAIN_RAM_SIZE) -#define MAIN_RAM_SIZE 0x08000000 +#define MAIN_RAM_SIZE 0x01000000 #endif #define SDRAM_TEST_BASE 0x00000000 -#define SDRAM_TEST_DATA_BYTES (CSR_SDRAM_DFII_PI0_RDDATA_SIZE*4) +#define SDRAM_TEST_DATA_BYTES (CONFIG_BIST_DATA_WIDTH/8) #if defined(CSR_SDRAM_ECCR_BASE) #define SDRAM_TEST_SIZE MAIN_RAM_SIZE/2 @@ -26,17 +34,24 @@ #define SDRAM_TEST_SIZE MAIN_RAM_SIZE #endif -/* If 0, generate sequential data; if 1, generate random data */ -uint32_t rand_data = 1; +#define MAX_ERR_PRINT 10 + +static uint32_t wr_ticks; +static uint32_t wr_length; +static uint32_t rd_ticks; +static uint32_t rd_length; + +static uint32_t rd_errors; +#if defined(CSR_SDRAM_ECCR_BASE) +static uint32_t sec_errors; +static uint32_t ded_errors; +#endif -uint32_t wr_ticks; -uint32_t wr_length; -uint32_t rd_ticks; -uint32_t rd_length; -uint32_t rd_errors; +static int wr_once = 2; /* if 1, BIST will only write once to SDRAM */ +static uint32_t base; +static uint32_t rand_cnt; /* used for random address table lookup */ -__attribute__((unused)) static void cdelay(int i) -{ +__attribute__((unused)) static void cdelay(int i) { #ifndef CONFIG_BIOS_NO_DELAYS while(i > 0) { __asm__ volatile(CONFIG_CPU_NOP); @@ -45,6 +60,7 @@ __attribute__((unused)) static void cdelay(int i) #endif } +/* 128 entries, range 0x0 - 0x3FF000 (4M-4K) */ #define PRB_SIZE (sizeof(pseudo_random_bases)/sizeof(pseudo_random_bases[0])) static uint32_t pseudo_random_bases[] = { 0x000e4018,0x0003338d,0x00233429,0x001f589d, @@ -81,114 +97,248 @@ static uint32_t pseudo_random_bases[] = { 0x00027e36,0x000e51ae,0x002e7627,0x00275c9f, }; -static inline void sdram_generator_init(uint32_t base, uint32_t length) { +#define RANDOM_ADDR (SDRAM_TEST_BASE + pseudo_random_bases[(rand_cnt++)%PRB_SIZE]*SDRAM_TEST_DATA_BYTES) + +/* data_mode: 0=pattern, 1=inc, 2=random */ +static unsigned char mtab[] = {4, 0, 1}; /* translate from mode to register bits */ + +/* base and length are in bytes. */ +/* They will be truncated by the bist hardware to data word alignment */ +static inline void sdram_generator_init(uint32_t base, uint32_t length, int dmode) { sdram_generator_reset_write(1); sdram_generator_reset_write(0); - sdram_generator_random_write(rand_data); sdram_generator_base_write(base); sdram_generator_end_write(base + length); sdram_generator_length_write(length); + sdram_generator_mode_write(mtab[dmode]); cdelay(100); } -static inline void sdram_checker_init(uint32_t base, uint32_t length) { - sdram_checker_reset_write(1); +static inline void sdram_checker_init(uint32_t base, uint32_t length, int dmode) { +#if defined(CSR_SDRAM_ECCR_BASE) + sdram_eccr_clear_write(1); /* clears sdram_eccr_sec_errors & sdram_eccr_ded_errors */ + sdram_eccr_clear_write(0); +#endif + sdram_checker_reset_write(1); /* clears sdram_checker_errors */ sdram_checker_reset_write(0); - sdram_checker_random_write(rand_data); sdram_checker_base_write(base); sdram_checker_end_write(base + length); sdram_checker_length_write(length); + sdram_checker_mode_write(mtab[dmode]); cdelay(100); } +/* for bandwidth calculations */ static void sdram_clear_counts(void) { wr_length = 0; wr_ticks = 0; rd_length = 0; rd_ticks = 0; +} + +static void sdram_clear_errors(void) { +#if defined(CSR_SDRAM_ECCR_BASE) + sec_errors = 0; + ded_errors = 0; +#endif rd_errors = 0; } +static uint32_t compute_speed_mibs(uint32_t length, uint32_t ticks) { + if (ticks == 0) return 0; + return (uint64_t)length*(CONFIG_CLOCK_FREQUENCY/(1024*1024))/ticks; +} + +static void display_errors(uint32_t base, uint32_t length, int dmode) { + uint32_t *ptr = (uint32_t *)(MAIN_RAM_BASE+base); + uint32_t *end = (uint32_t *)(MAIN_RAM_BASE+base+length); + uint32_t errors = 0; + /* Flush caches */ + flush_cpu_dcache(); + flush_l2_cache(); + switch (dmode) { + default: + case 0: { + uint32_t val = sdram_generator_pattern_read() * 0x01010101U; /* expand byte to word */ + do { + if (*ptr++ != val && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08lx\n", + (unsigned long)(ptr - 1), *(ptr - 1), val); + } while (ptr < end); + break;} + case 1: { + uint32_t val = 0; + do { + if (*ptr++ != val++ && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08lx\n", + (unsigned long)(ptr - 1), *(ptr - 1), val - 1); + if (ptr >= end) break; +#if SDRAM_TEST_DATA_BYTES == 8 + if (*ptr++ != 0 && errors++ < MAX_ERR_PRINT) + printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08x\n", + (unsigned long)(ptr - 1), *(ptr - 1), 0); +#endif + } while (ptr < end); + break;} + case 2: + /* not implemented */ + break; + } + printf("ERRORS: %lu\n", errors); +} + #define TARGET_BYTES 0x20000 -/* burst_length of 1, DMA transaction is about 468 cycles */ -/* burst_length > 0x400, DMA transaction is about 1 cycle per word */ +/* One DMA transaction is about 468 cycles */ +/* For a burst > 0x400 trans., each DMA transaction is about 1 cycle per word */ -void sdram_bist_loop(uint32_t burst_length, uint32_t amode) { - int i; - uint32_t base = SDRAM_TEST_BASE; - uint32_t length = burst_length*SDRAM_TEST_DATA_BYTES; - uint32_t loop_cnt = (TARGET_BYTES > length) ? TARGET_BYTES / length : 1; +static void sdram_bist_loop(uint32_t length, int amode, int dmode) { + uint32_t next_base; + uint32_t errors; +#if defined(CSR_SDRAM_ECCR_BASE) + uint32_t sec, ded; +#endif - rd_errors = 0; /* Prepare first write */ - sdram_generator_init(base, length); - for (i = 0; i < loop_cnt; i++) { - /* Start write */ - sdram_generator_start_write(1); + if (wr_once) + sdram_generator_init(base, length, dmode); + do { + if (wr_once) { + /* Start write */ + sdram_generator_start_write(1); + wr_length += length; + } /* Prepare next read */ - sdram_checker_init(base, length); - /* Wait write */ - while (sdram_generator_done_read() == 0); - /* Get write results */ - wr_ticks += sdram_generator_ticks_read(); - wr_length += length; + sdram_checker_init(base, length, dmode); + if (wr_once) { + /* Wait write */ + while (sdram_generator_done_read() == 0); + wr_ticks += sdram_generator_ticks_read(); + } /* Start read */ sdram_checker_start_write(1); - if (i != loop_cnt-1) { - switch (amode) { - case 1: - base = base + length; - if (base >= (SDRAM_TEST_BASE+SDRAM_TEST_SIZE)) base = SDRAM_TEST_BASE; - break; - case 2: - base = SDRAM_TEST_BASE + pseudo_random_bases[i%PRB_SIZE]*SDRAM_TEST_DATA_BYTES; - break; + rd_length += length; + /* Calculate next address */ + switch (amode) { + default: + case MD_FIX: + next_base = base; + wr_once &= -2; /* Clear LSB, e.g. 1 goes to 0, 2 no change */ + break; + case MD_INC: + next_base = base + length; + if (next_base >= (SDRAM_TEST_BASE+SDRAM_TEST_SIZE)) { + next_base = SDRAM_TEST_BASE; + wr_once &= -2; } - /* Prepare next write */ - sdram_generator_init(base, length); + break; + case MD_RAN: + next_base = RANDOM_ADDR; + /* Write once mode not compatible with random address */ + /* mode when DMA blocks overlap. */ + if (rand_cnt == PRB_SIZE+1) wr_once &= -2; + break; } + /* Prepare next write */ + if (wr_once && rd_length+wr_length < TARGET_BYTES) + sdram_generator_init(next_base, length, dmode); /* Wait read */ while (sdram_checker_done_read() == 0); - /* Get read results */ - rd_ticks += sdram_checker_ticks_read(); - rd_errors += sdram_checker_errors_read(); - rd_length += length; - } + rd_ticks += sdram_checker_ticks_read(); + /* Report any errors, then fix them */ + rd_errors += errors = sdram_checker_errors_read(); +#if defined(CSR_SDRAM_ECCR_BASE) + sec_errors += sec = sdram_eccr_sec_errors_read(); + ded_errors += ded = sdram_eccr_ded_errors_read(); + errors += sec + ded; +#endif + if (errors) { +#if defined(DISPLAY_ERRORS) && !defined(CSR_SDRAM_ECCR_BASE) + display_errors(base, length, dmode); +#endif + /* Re-write memory block to fix errors */ + sdram_bist_gen(base, length, dmode); + /* Re-setup next memory block */ + if (wr_once && rd_length+wr_length < TARGET_BYTES) + sdram_generator_init(next_base, length, dmode); + } + base = next_base; + } while (rd_length+wr_length < TARGET_BYTES); } -static uint32_t compute_speed_mibs(uint32_t length, uint32_t ticks) { - uint32_t speed; - //printf("(%u, %u)", length, ticks); - speed = (uint64_t)length*(CONFIG_CLOCK_FREQUENCY/(1024*1024))/ticks; - return speed; +void sdram_bist_pattern(uint32_t value) { + sdram_generator_pattern_write(value); + sdram_checker_pattern_write(value); +} + +void sdram_bist_gen(uint32_t base, uint32_t length, int dmode) { + sdram_generator_init(base, length, dmode); + sdram_generator_start_write(1); + while (sdram_generator_done_read() == 0); +} + +void sdram_bist_chk(uint32_t base, uint32_t length, int dmode) { + uint32_t errors; +#if defined(CSR_SDRAM_ECCR_BASE) + uint32_t sec, ded; +#endif + + sdram_checker_init(base, length, dmode); + sdram_checker_start_write(1); + while (sdram_checker_done_read() == 0); + + errors = sdram_checker_errors_read(); +#if defined(CSR_SDRAM_ECCR_BASE) + sec = sdram_eccr_sec_errors_read(); + ded = sdram_eccr_ded_errors_read(); + printf("ERRORS: %lu, SEC: %lu, DED: %lu\n", errors, sec, ded); + errors += sec + ded; +#else + printf("ERRORS: %lu\n", errors); +#endif + + if (errors) { +#if defined(DISPLAY_ERRORS) && !defined(CSR_SDRAM_ECCR_BASE) + display_errors(base, length, dmode); +#endif + } } -void sdram_bist(uint32_t burst_length, uint32_t amode) -{ +void sdram_bist(uint32_t length, int amode, int dmode, int wmode) { uint32_t i; uint32_t uc; /* update count */ uint32_t uf; /* update frequency */ uint32_t total_length; - uint32_t total_errors; - printf("Starting SDRAM BIST with burst_length=%lu and addr_mode=%lu\n", burst_length, amode); -#if defined(CSR_SDRAM_ECCR_BASE) - sdram_eccr_clear_write(1); - sdram_eccr_clear_write(0); -#endif + if (wmode < 2 && amode == MD_RAN) { + printf("Write once mode not compatible with random address mode"); + return; + } + if (length < SDRAM_TEST_DATA_BYTES) + length = SDRAM_TEST_DATA_BYTES; + if (popcount(length) != 1) { + printf("Value of length must be a power of 2"); + return; + } + + printf("Starting SDRAM BIST with length=%lu, addr_mode=%d, data_mode=%d wmode=%d\n", + length, amode, dmode, wmode); sdram_clear_counts(); + sdram_clear_errors(); + wr_once = wmode; i = 0; uc = 0; - uf = burst_length * 2 + 2; + uf = length * 3 + 2; total_length = 0; - total_errors = 0; + rand_cnt = 0; + if (amode == MD_RAN) base = RANDOM_ADDR; + else base = SDRAM_TEST_BASE; for (;;) { /* Exit on key pressed */ if (readchar_nonblock()) break; /* Header */ - if (uc%8 == 0 && wr_length == 0) { + if (uc%8 == 0 && rd_length == 0) { #if defined(CSR_SDRAM_ECCR_BASE) printf("WR-BW(MiB/s) RD-BW(MiB/s) TESTED(MiB) ERRORS SEC DED\n"); #else @@ -196,37 +346,41 @@ void sdram_bist(uint32_t burst_length, uint32_t amode) #endif } - /* Bist loop */ - sdram_bist_loop(burst_length, amode); + /* BIST loop */ + sdram_bist_loop(length, amode, dmode); i++; /* Results, update about once per second */ - if ((burst_length < 0x100 && i%uf == 0) || - (wr_ticks >= CONFIG_CLOCK_FREQUENCY/2)) { + if ((length < 0x400 && i%uf == 0) || + (rd_ticks+wr_ticks >= CONFIG_CLOCK_FREQUENCY)) { uc++; - total_length += wr_length; - total_errors += rd_errors; + total_length += rd_length; #if defined(CSR_SDRAM_ECCR_BASE) printf("%12lu %12lu %12lu %10lu %10lu %10lu\n", compute_speed_mibs(wr_length, wr_ticks), compute_speed_mibs(rd_length, rd_ticks), total_length/(1024*1024), - total_errors, - sdram_eccr_sec_errors_read(), - sdram_eccr_ded_errors_read()); + rd_errors, sec_errors, ded_errors); #else printf("%12lu %12lu %12lu %10lu\n", compute_speed_mibs(wr_length, wr_ticks), compute_speed_mibs(rd_length, rd_ticks), total_length/(1024*1024), - total_errors); + rd_errors); #endif - /* Clear length/ticks/errors */ + /* Clear bandwidth counts */ sdram_clear_counts(); + +#if defined(INJECT_ERRORS) + /* Inject SDRAM errors */ + if (wr_once < 2) { + memtest_inject_errors((unsigned *)MAIN_RAM_BASE, MAIN_RAM_SIZE, 1, 0x1, 1); + } +#endif /* INJECT_ERRORS */ } } } -#endif +#endif /* defined(CSR_SDRAM_GENERATOR_BASE) && defined(CSR_SDRAM_CHECKER_BASE) */ diff --git a/litex/soc/software/liblitedram/bist.h b/litex/soc/software/liblitedram/bist.h index b8ac653215..cba8a3d052 100644 --- a/litex/soc/software/liblitedram/bist.h +++ b/litex/soc/software/liblitedram/bist.h @@ -4,7 +4,13 @@ #ifndef __SDRAM_BIST_H #define __SDRAM_BIST_H -void sdram_bist_loop(uint32_t burst_length, uint32_t amode); -void sdram_bist(uint32_t burst_length, uint32_t amode); +#include + +typedef enum {MD_FIX, MD_INC, MD_RAN} sequence_t; + +void sdram_bist_pattern(uint32_t value); +void sdram_bist_gen(uint32_t base, uint32_t length, int dmode); +void sdram_bist_chk(uint32_t base, uint32_t length, int dmode); +void sdram_bist(uint32_t length, int amode, int dmode, int wmode); #endif /* __SDRAM_BIST_H */ diff --git a/litex/soc/software/liblitedram/sdram.c b/litex/soc/software/liblitedram/sdram.c index 53acec8291..4bbf82f825 100644 --- a/litex/soc/software/liblitedram/sdram.c +++ b/litex/soc/software/liblitedram/sdram.c @@ -17,6 +17,7 @@ #include #include +#include #ifdef CSR_SDRAM_BASE #include @@ -271,17 +272,6 @@ static void sdram_precharge_test_row(void) { cdelay(15); } -// Count number of bits in a 32-bit word, faster version than a while loop -// see: https://www.johndcook.com/blog/2020/02/21/popcount/ -static unsigned int popcount(unsigned int x) { - x -= ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x + (x >> 4)) & 0x0F0F0F0F; - x += (x >> 8); - x += (x >> 16); - return x & 0x0000003F; -} - static void print_scan_errors(unsigned int errors) { #ifdef SDRAM_LEVELING_SCAN_DISPLAY_HEX_DIV // Display '.' for no errors, errors/div in hex if it is a single char, else show 'X' From a6b4832af5ca49dfd782b8604aa96863ee2dd394 Mon Sep 17 00:00:00 2001 From: gslloyd Date: Sun, 7 Aug 2022 00:10:11 -0600 Subject: [PATCH 5/7] Specify branch for litedram --- litex_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex_setup.py b/litex_setup.py index 55b6e5b820..dbae4000e9 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -81,7 +81,7 @@ def __init__(self, url, clone="regular", develop=True, sha1=None, branch="master # LiteX Cores Ecosystem. # ---------------------- "liteeth": GitRepo(url="https://github.com/enjoy-digital/", tag=True), - "litedram": GitRepo(url="https://github.com/TommyCox/", tag=True), + "litedram": GitRepo(url="https://github.com/TommyCox/", branch="bist_ecc", tag=True), "litepcie": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesata": GitRepo(url="https://github.com/enjoy-digital/", tag=True), "litesdcard": GitRepo(url="https://github.com/enjoy-digital/", tag=True), From 99c192cce12725c1b9e405e1acb160463c4f5cf3 Mon Sep 17 00:00:00 2001 From: Scott Lloyd Date: Sun, 7 Aug 2022 00:51:32 -0600 Subject: [PATCH 6/7] remove duplicate error print --- litex/soc/software/liblitedram/bist.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/litex/soc/software/liblitedram/bist.c b/litex/soc/software/liblitedram/bist.c index 3d3a4a374d..4f05015623 100644 --- a/litex/soc/software/liblitedram/bist.c +++ b/litex/soc/software/liblitedram/bist.c @@ -290,17 +290,14 @@ void sdram_bist_chk(uint32_t base, uint32_t length, int dmode) { #if defined(CSR_SDRAM_ECCR_BASE) sec = sdram_eccr_sec_errors_read(); ded = sdram_eccr_ded_errors_read(); - printf("ERRORS: %lu, SEC: %lu, DED: %lu\n", errors, sec, ded); errors += sec + ded; -#else - printf("ERRORS: %lu\n", errors); #endif if (errors) { #if defined(DISPLAY_ERRORS) && !defined(CSR_SDRAM_ECCR_BASE) display_errors(base, length, dmode); #endif - } + } else printf("ERRORS: 0\n"); } void sdram_bist(uint32_t length, int amode, int dmode, int wmode) { From 46af66cc033ef9b36dc98be0ccce078fced90d57 Mon Sep 17 00:00:00 2001 From: Scott Lloyd Date: Tue, 22 Nov 2022 16:48:44 -0700 Subject: [PATCH 7/7] update bist to handle wider native ports to memory --- litex/soc/software/liblitedram/bist.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/litex/soc/software/liblitedram/bist.c b/litex/soc/software/liblitedram/bist.c index 4f05015623..229c2164d6 100644 --- a/litex/soc/software/liblitedram/bist.c +++ b/litex/soc/software/liblitedram/bist.c @@ -16,7 +16,7 @@ #include /* un-comment to flip random bits in SDRAM */ -#define INJECT_ERRORS +// #define INJECT_ERRORS /* un-comment to print detail about errors */ #define DISPLAY_ERRORS @@ -27,6 +27,7 @@ #define SDRAM_TEST_BASE 0x00000000 #define SDRAM_TEST_DATA_BYTES (CONFIG_BIST_DATA_WIDTH/8) +#define SDRAM_TEST_DATA_WORDS (SDRAM_TEST_DATA_BYTES/sizeof(uint32_t)) #if defined(CSR_SDRAM_ECCR_BASE) #define SDRAM_TEST_SIZE MAIN_RAM_SIZE/2 @@ -167,24 +168,19 @@ static void display_errors(uint32_t base, uint32_t length, int dmode) { } while (ptr < end); break;} case 1: { - uint32_t val = 0; + uint32_t i = 0, cnt = 0; do { - if (*ptr++ != val++ && errors++ < MAX_ERR_PRINT) + uint32_t val = ((i++ % SDRAM_TEST_DATA_WORDS) ? 0 : cnt++); + if (*ptr++ != val && errors++ < MAX_ERR_PRINT) printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08lx\n", - (unsigned long)(ptr - 1), *(ptr - 1), val - 1); - if (ptr >= end) break; -#if SDRAM_TEST_DATA_BYTES == 8 - if (*ptr++ != 0 && errors++ < MAX_ERR_PRINT) - printf("error addr: 0x%08lx, content: 0x%08lx, expected: 0x%08x\n", - (unsigned long)(ptr - 1), *(ptr - 1), 0); -#endif + (unsigned long)(ptr - 1), *(ptr - 1), val); } while (ptr < end); break;} case 2: /* not implemented */ break; } - printf("ERRORS: %lu\n", errors); + printf("ERRORS (32-bit words): %lu\n", errors); } #define TARGET_BYTES 0x20000