/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2009, Martin Schoeberl (martin@jopdesign.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package util;
import com.jopdesign.sys.Native;
/**
* Interface to NAND memory on the Cycore board.
*
* NAND organization (8-bit device): page = 2x256 Bytes + 16 Bytes spare, block =
* 32 pages = 16 KB
*
* Addressing is without A8 => for 8-bit devices there are two different address
* commands that contain A8. A0-A7 Column Address, A9-A13 Address in Block, A14-A26
* Block Address, A9-A26 Page Address
*
* Bad blocks are marked with the 6th byte of the spare area with data != 0xff
* on shipment.
*
* All commands use integer arrays. We use the network order with high byte first.
*
* @author Martin Schoeberl
*
* TODO:
* Having the RDY signal as bit 8 is not such a good idea -- change memory
* interface
*
*/
public class NandLowLevel {
final static boolean LOG = true;
public final static int NAND_ADDR = 0x100000; // data port
final static int CLE = NAND_ADDR + 1; // command latch enable
final static int ALE = NAND_ADDR + 2; // address latch enable
final static int RDY = NAND_ADDR + 4; // ready signal
final static int ERASE = 0x60;
final static int ERASE_CONFIRM = 0xD0;
final static int STATUS = 0x70;
final static int SIGNATURE = 0x90;
final static int PROGRAM = 0x80;
final static int PROGRAM_CONFIRM = 0x10;
final static int POINTA = 0x00;
final static int POINTB = 0x01;
final static int POINTC = 0x50;
final static int RDY_MSK = 0x100;
public final static int PAGES_PER_BLOCK = 32;
public final static int WORDS = 128; // one page in 32 bit words
public final static int SPARE_WORDS = 4;
/**
* Size in blocks. One block contains 32 pages or 16 KB.
*/
private int nrOfBlocks;
protected int[] localSpare = new int[SPARE_WORDS];
/**
* Check the size of the NAND flash.
*/
NandLowLevel() {
Native.wrMem(SIGNATURE, CLE);
Native.wrMem(0x00, ALE);
int man = Native.rdMem(NAND_ADDR); // Manufacturer
int size = Native.rdMem(NAND_ADDR); // Size
if (size == 0x73) {
nrOfBlocks = 1024;
} else if (size == 0x75) {
nrOfBlocks = 2048;
} else if (size == 0x76) {
nrOfBlocks = 4096;
} else if (size == 0x79) {
nrOfBlocks = 8192;
} else {
nrOfBlocks = 0;
}
}
/**
* @return true if no error is signaled by the error bit
*/
boolean cmdOk() {
Native.wrMem(STATUS, CLE);
// S0=error bit S6=controller inactive S7=wr protection
// Signal
return !((Native.rdMem(NAND_ADDR) & 0x01) == 0x01);
}
/**
* waits until the NAND is ready
*/
void waitForReady() {
while (Native.rdMem(RDY)==0) {
; // watch rdy signal
}
}
/**
* Erase one block and read back to check if cleaned.
* @param block
* @return true if ok.
*/
boolean eraseBlock(int block) {
int a1, a2;
boolean ret = true;
int cnt;
a1 = block << 5;
a2 = block >> 3;
Native.wrMem(ERASE, CLE);
Native.wrMem(a1, ALE);
Native.wrMem(a2, ALE);
Native.wrMem(ERASE_CONFIRM, CLE);
waitForReady();
ret &= cmdOk();
// check page
for (int seq=0; seq<2; ++seq) {
if (seq==0) {
Native.wrMem(POINTA, CLE);
cnt = WORDS*4;
} else {
Native.wrMem(POINTC, CLE);
cnt = SPARE_WORDS*4;
}
Native.wrMem(0, ALE); // we read a whole page, a0=0
Native.wrMem(a1, ALE);
Native.wrMem(a2, ALE);
waitForReady();
for (int i=0; i<cnt; ++i) {
if (Native.rdMem(NAND_ADDR)!=0xff) {
ret = false;
}
}
if (LOG) {
if (!ret) {
System.out.println("not erased");
}
}
Timer.wd();
}
return ret;
}
/**
* Erase the whole NAND flash.
* @return true if no error.
*/
boolean eraseAll() {
boolean ret = true;
for (int i=0; i<nrOfBlocks; ++i) {
ret &= eraseBlock(i);
System.out.print(".");
}
return ret;
}
/**
* Write one page and spare into the NAND. Also read back for a check.
* @param data
* @param block
* @param page
* @return true if no error.
*/
boolean writePage(int[] data, int[] spare, int block, int page) {
int i, val;
int a1, a2;
int[] buf;
boolean ret = true;
int cnt;
a1 = (block << 5) + page;
a2 = block >> 3;
// write page
for (int seq=0; seq<2; ++seq) {
if (seq==0) {
Native.wrMem(POINTA, CLE);
buf = data;
cnt = WORDS;
} else {
Native.wrMem(POINTC, CLE);
buf = spare;
cnt = SPARE_WORDS;
}
if (buf==null) {
continue;
}
Native.wrMem(PROGRAM, CLE);
Native.wrMem(0, ALE); // we write a whole page, a0=0
Native.wrMem(a1, ALE);
Native.wrMem(a2, ALE);
for (i=0; i<cnt; ++i) {
val = buf[i];
Native.wr(val>>24, NAND_ADDR);
Native.wr(val>>16, NAND_ADDR);
Native.wr(val>>8, NAND_ADDR);
Native.wr(val, NAND_ADDR);
}
Native.wrMem(PROGRAM_CONFIRM, CLE);
waitForReady();
ret &= cmdOk();
}
// check page
for (int seq=0; seq<2; ++seq) {
if (seq==0) {
Native.wrMem(POINTA, CLE);
buf = data;
cnt = WORDS;
} else {
Native.wrMem(POINTC, CLE);
buf = spare;
cnt = SPARE_WORDS;
}
if (buf==null) {
continue;
}
Native.wrMem(0, ALE); // we read a whole page, a0=0
Native.wrMem(a1, ALE);
Native.wrMem(a2, ALE);
waitForReady();
for (i=0; i<cnt; ++i) {
val = Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
ret &= buf[i] == val;
}
ret &= cmdOk();
}
return ret;
}
/**
* Read one page and spare from the NAND.
* @param data
* @param block
* @param page
* @return
*/
boolean readPage(int[] data, int[] spare, int block, int page) {
int i, val;
int a1, a2;
int[] buf = data;
boolean ret = true;
int cnt = WORDS;
a1 = (block << 5) + page;
a2 = block >> 3;
for (int seq=0; seq<2; ++seq) {
if (seq==0) {
Native.wrMem(POINTA, CLE);
} else {
Native.wrMem(POINTC, CLE);
buf = spare;
cnt = SPARE_WORDS;
}
if (buf==null) {
continue;
}
Native.wrMem(0, ALE); // we read a whole page, a0=0
Native.wrMem(a1, ALE);
Native.wrMem(a2, ALE);
waitForReady();
for (i=0; i<cnt; ++i) {
val = Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
val = (val<<8) + Native.rdMem(NAND_ADDR);
buf[i] = val;
}
ret &= cmdOk();
}
return ret;
}
// TODO: add ECC check
boolean readPage(int[] data, int block, int page) {
return readPage(data, localSpare, block, page);
}
/**
* Returns size in number of 16 KB blocks. Zero if no NAND
* Flash is on board.
* @return
*/
public int getNrOfBlocks() {
return nrOfBlocks;
}
}