Cosmos-OpenSSD / Cosmos-plus-OpenSSD

Cosmos OpenSSD + Hardware and Software source distribution
GNU General Public License v3.0
194 stars 99 forks source link

A read operation doesn't work after an erase or a program operation #13

Closed dvoytik closed 7 years ago

dvoytik commented 7 years ago

Reading operation doesn't work if an erase and/or a program operation was issued before:

#include "low_level_scheduler.h"
#include "fmc_driver.h"
#include "memory_map.h"
#include "init_ftl.h"
#include <stdio.h>
void test_read()
{
    unsigned int chNo;
    unsigned int wayNo;
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);
    void *pageDataBuf = (void *)BUFFER_ADDR;
    void *spareDataBuf = (void *)SPARE_ADDR;
    unsigned int *errorInfo = (unsigned int *)ERROR_INFO_TABLE_ADDR;

    printf("test read start...\r\n");

    InitChCtlReg();

    for(chNo = 0; chNo < CHANNEL_NUM; ++chNo) {
        for(wayNo = 0; wayNo < WAY_NUM; ++wayNo) {
            V2FResetSync(chCtlReg[chNo], wayNo);
            V2FEnterToggleMode(chCtlReg[chNo], wayNo);
        }
    }

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }

#if 0 // REMOVE THIS LINE

    // erase block
    V2FEraseBlockAsync(chCtlReg[0], 0, 0);
    V2FStatusCheckAsync(chCtlReg[0], 0, status);

    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
            // again
        }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }

#endif // REMOVE THIS LINE

    V2FReadPageTransferAsync(chCtlReg[0], 0, pageDataBuf, spareDataBuf,
                             errorInfo, status, 0);

    while (!(*status & 1)) { /* spin */ }

    printf("test read OK\r\n");
}

If you remove lines marked as "REMOVE THIS LINE" in the above code example then the erase command is executed before the read page command. *status variable is never updated and the last while loop always spins.

Could you please check if you also can observe this issue? I test this code on commit 3242242 2017-08-23 openssd J89 (on) -> J82 (off) @ p.63 because the latest commit doesn't work (please see #10)

dvoytik commented 7 years ago

CC @Sangjin-Lee

Cosmos-OpenSSD commented 7 years ago

You should call the function V2FIsControllerBusy and make sure that the channel controller is not busy before an operation. e.g.) while (V2FIsControllerBusy(chCtlReg[0])) { / spin / } V2FEraseBlockAsync(...); while (V2FIsControllerBusy(chCtlReg[0])) { / spin / } V2FStatusCheckAsync(...);

dvoytik commented 7 years ago

Hi @Cosmos-OpenSSD, thank you for your response. I've changed the test code according to your suggestions:

#include "low_level_scheduler.h"
#include "fmc_driver.h"
#include "memory_map.h"
#include "init_ftl.h"
#include <stdio.h>
void test_read()
{
    unsigned int chNo;
    unsigned int wayNo;
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);
    void *pageDataBuf = (void *)BUFFER_ADDR;
    void *spareDataBuf = (void *)SPARE_ADDR;
    unsigned int *errorInfo = (unsigned int *)ERROR_INFO_TABLE_ADDR;

    printf("test read start Nov 8, 10:41...\r\n");

    InitChCtlReg();

    for(chNo = 0; chNo < CHANNEL_NUM; ++chNo) {
        for(wayNo = 0; wayNo < WAY_NUM; ++wayNo) {
            V2FResetSync(chCtlReg[chNo], wayNo);
            V2FEnterToggleMode(chCtlReg[chNo], wayNo);
        }
    }

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }

//#if 0 // UNCOMMENT THIS LINE to disable erase operation

    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
    V2FEraseBlockAsync(chCtlReg[0], 0, 0);

    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
    V2FStatusCheckAsync(chCtlReg[0], 0, status);

    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
            // again
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }

//#endif // UNCOMMENT THIS LINE to disable erase operation

    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FReadPageTransferAsync(chCtlReg[0], 0, pageDataBuf, spareDataBuf,
                             errorInfo, status, 1);

    // HERE: this loop never ends, *status value is always 0x0
    while (!(*status & 1)) { /* spin */ }

    printf("test read OK\r\nReboot me please.\r\n");
    for (;;) {}
}

Unfortunately the result is the same - after executing V2FReadPageTransferAsync() command the *status is never updated. I marked the line where it hangs with the comment "HERE".

Please note, that I don't test the latest commit (i.e. v1.0.2). I use commit 3242242 2017-08-23 openssd J89 (on) -> J82 (off) @ p.63 (i.e. v1.0.0) because of the issue #10.

Cosmos-OpenSSD commented 7 years ago

In addition, you need to call V2FReadPageTriggerAsync before V2FReadPageTransferAsync. Reading a page should be like:

  1. call V2FReadPageTriggerAsync
  2. check the status (V2FStatusCheckAsync)
  3. call V2FReadPageTransferAsync
  4. check the completion flag and the error information
dvoytik commented 7 years ago

@Cosmos-OpenSSD, thank you for the quick response. I could manage to make V2ReadPageTransferAsync to work. I'd add to your list one important detail - we have to send StatusCheck in loop while it response with AGAIN status.

Now I have another problem. I get error - CRC is invalid. My code:

#include "low_level_scheduler.h"
#include "fmc_driver.h"
#include "memory_map.h"
#include "init_ftl.h"
#include <stdio.h>

void erase(unsigned int row_addr)
{
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);

    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
    V2FEraseBlockAsync(chCtlReg[0], 0, row_addr);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FStatusCheckAsync(chCtlReg[0], 0, status);

    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }
}

void test_read()
{
    unsigned int chNo;
    unsigned int wayNo;
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);
    void *pageDataBuf = (void *)BUFFER_ADDR;
    void *spareDataBuf = (void *)SPARE_ADDR;
    unsigned int *errorInfo = (unsigned int *)ERROR_INFO_TABLE_ADDR;

    printf("test read (Nov 9, 11:24)...\r\n");

    InitChCtlReg();

    for(chNo = 0; chNo < CHANNEL_NUM; ++chNo) {
        for(wayNo = 0; wayNo < WAY_NUM; ++wayNo) {
            V2FResetSync(chCtlReg[chNo], wayNo);
            V2FEnterToggleMode(chCtlReg[chNo], wayNo);
        }
    }

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }

    erase(0);
    erase(1);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FReadPageTriggerAsync(chCtlReg[0], 0, 1);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FStatusCheckAsync(chCtlReg[0], 0, status);
    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }

    memset(errorInfo, 0x0, 4 * 12);

    V2FReadPageTransferAsync(chCtlReg[0], 0, pageDataBuf, spareDataBuf,
                             errorInfo, status, 0);

    while (!(*status & 1)) { /* spin */ }

    if(!V2FCrcValid(errorInfo[0])) {            // THIS ERROR
        printf("CRC invalid\r\n");
    }
    if(V2FSpareChunkValid(errorInfo[0])) {
        printf("spare chunk invalid\r\n");
    }
    if(V2FPageChunkValid(errorInfo[1])) {
        printf("page chunk invalid\r\n");
    }
    if(V2FWorstChunkErrorCount(errorInfo[0])> BIT_ERROR_THRESHOLD) {
    printf("ErrofInfo BIT_ERROR_THRESHOLD\r\n");
    }

    printf("test read OK\r\nReboot me please.\r\n");
    for (;;) {}
}

Could it be related to the issue #10?

Cosmos-OpenSSD commented 7 years ago

You erased the block 0 twice (row address 0 and 1 are in a same block) and read the page 1 without any program operation. This might lead to ECC/CRC errors because the page was erased and does not have meaningful data and ECC/CRC information.

The read buffer should be filled with 0xFFs at the error point.

dvoytik commented 7 years ago

@Cosmos-OpenSSD, with only erase and without program after read the buffer is filled some random garbage.

Cosmos-OpenSSD commented 7 years ago

Sorry, I forgot to say that V2FReadPageTransferRawAsync should do that. V2FReadPageTransferAsync includes a descrambling process, so garbage data will be returned.

dvoytik commented 7 years ago

@Cosmos-OpenSSD, and if I program some data before read then it should work? This code I've just tried:

#include "low_level_scheduler.h"
#include "fmc_driver.h"
#include "memory_map.h"
#include "init_ftl.h"
#include <stdio.h>

void erase(unsigned int row_addr)
{
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);

    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
    V2FEraseBlockAsync(chCtlReg[0], 0, row_addr);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FStatusCheckAsync(chCtlReg[0], 0, status);

    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }
}

void program_page_with_zeros(unsigned int row_addr)
{
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);
    void *pageDataBuf = (void *)BUFFER_ADDR;
    void *spareDataBuf = (void *)SPARE_ADDR;

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    // zero memory buffers
    memset(pageDataBuf, 0x0, 16 * 1024);
    memset(spareDataBuf, 0x0, 256);

    V2FProgramPageAsync(chCtlReg[0], 0, row_addr, pageDataBuf, spareDataBuf);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FStatusCheckAsync(chCtlReg[0], 0, status);

    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }
}

void test_read()
{
    unsigned int chNo;
    unsigned int wayNo;
    unsigned int *status = (unsigned int *)(COMPLETE_TABLE_ADDR);
    void *pageDataBuf = (void *)BUFFER_ADDR;
    void *spareDataBuf = (void *)SPARE_ADDR;
    unsigned int *errorInfo = (unsigned int *)ERROR_INFO_TABLE_ADDR;

    printf("test read (Nov 9, 12:42)...\r\n");

    InitChCtlReg();

    for(chNo = 0; chNo < CHANNEL_NUM; ++chNo) {
        for(wayNo = 0; wayNo < WAY_NUM; ++wayNo) {
            V2FResetSync(chCtlReg[chNo], wayNo);
            V2FEnterToggleMode(chCtlReg[chNo], wayNo);
        }
    }

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }

    erase(0);
    erase(1);

    program_page_with_zeros(0);
    program_page_with_zeros(1);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FReadPageTriggerAsync(chCtlReg[0], 0, 1);

    while (!(V2FReadyBusyAsync(chCtlReg[0]) & 1)) { /* spin */ }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }

    V2FStatusCheckAsync(chCtlReg[0], 0, status);
    while (1) {
        while (!(*status & 1)) { /* spin */ }

        if (((*status >> 1) & 0x60) == 0x60) {
            if ((*status >> 1) & 3) {
                printf("FAIL: status: %x", *status);
            }
            break;
        }
    while (V2FIsControllerBusy(chCtlReg[0])) { /* spin */ }
        V2FStatusCheckAsync(chCtlReg[0], 0, status);
    }

    memset(errorInfo, 0x0, 4 * 12);
    memset(pageDataBuf, 0x55, 16 * 1024);

    V2FReadPageTransferAsync(chCtlReg[0], 0, pageDataBuf, spareDataBuf,
                             errorInfo, status, 0);

    while (!(*status & 1)) { /* spin */ }

    dumpmem(pageDataBuf, 16);

    if(!V2FCrcValid(errorInfo[0])) {            // THIS ERROR
        printf("CRC invalid\r\n");
    }
    if(V2FSpareChunkValid(errorInfo[0])) {
        printf("spare chunk invalid\r\n");
    }
    if(V2FPageChunkValid(errorInfo[1])) {
        printf("page chunk invalid\r\n");
    }
    if(V2FWorstChunkErrorCount(errorInfo[0])> BIT_ERROR_THRESHOLD) {
    printf("ErrofInfo BIT_ERROR_THRESHOLD\r\n");
    }

    printf("test read OK\r\nReboot me please.\r\n");
    for (;;) {}
}

Still I get "CRC invalid" and garbage data:

0x10000000 08 08 08 08 10 10 10 10  21 21 21 21 42 42 42 42  |........!!!!BBBB|
Cosmos-OpenSSD commented 7 years ago

It seems that you were trying to read page 1 (V2FReadPageTrigger(..., 1), but V2FReadPageTransferAsync(..., 0) was trying to read page 0. The descrambling process uses a row address for a seed, so you should give correct row address to V2FReadPageTransferAsync.

dvoytik commented 7 years ago

@Cosmos-OpenSSD, oops! You are right. I changed this and now finally it works! :) Thank you for the help!