/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2009, Jens Kager, Fritz Praus
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 fat;
import util.Nand;
import util.NandLowLevel;
public class FatNand implements FatLowLevel {
static final int block_offset = 0; // where in NAND first block starts; for testing to avoid wrinting to same block again and again
static final boolean LOG = false; // LOG output on/off
static final boolean useBuffer = true; // use buffering of blocks
static final boolean readBeforeWrite = true; // apply readBeforeWrite method
static Nand nand;
static boolean avail;
// A 3-dimensional array would have been nice, unfortunately
// this is unsupported by the JOP. :-(
final static int NumBufferedBlocks = 5;
static boolean[][] BufferFilled = new boolean[NumBufferedBlocks][NandLowLevel.PAGES_PER_BLOCK];
static int[][] BufferedBlocks = new int[NumBufferedBlocks][NandLowLevel.PAGES_PER_BLOCK*NandLowLevel.WORDS];
final static int size = 512;
static int[] readBuffer = new int[size];
static int[] tmpBuffer = new int[NandLowLevel.WORDS];
public int Init() {
if (nand==null) {
nand = new Nand();
}
avail = nand.isAvailable();
if (avail) {
return 0;
}
else return -1; // unknown error
}
public void Flush() {
if (useBuffer == false)
return;
if (LOG)
System.out.println("Flushing buffers");
for (int block = 0; block < NumBufferedBlocks; block++) {
for (int page = 0; page < NandLowLevel.PAGES_PER_BLOCK; page++) {
if (BufferFilled[block][page]) {
if (LOG)
System.out.println("Flushing block " + block + ", page " + page);
for (int i = 0; i < NandLowLevel.WORDS; i++) {
tmpBuffer[i] = BufferedBlocks[block][(page*NandLowLevel.WORDS)+i];
}
nand.write(tmpBuffer, block + block_offset, page, size);
}
}
}
}
public int ReadSector(int addr, int[] buffer) {
int ret=-1;
// NAND available, continue
if (avail) {
int block = addr / NandLowLevel.PAGES_PER_BLOCK;
int page = addr % NandLowLevel.PAGES_PER_BLOCK;
// Read first sectors from buffer
if (useBuffer && block < NumBufferedBlocks) {
// Get page from buffer
if (BufferFilled[block][page]) {
if (LOG)
System.out.println("Using cached block " + block + ", page " + page);
for (int i = 0; i < NandLowLevel.WORDS; i++) {
tmpBuffer[i] = BufferedBlocks[block][(page*NandLowLevel.WORDS)+i];
}
ret = 1; // read successful; (not 0!, simulate nand.read() behavior)
// Page is not yet in buffer, fill from medium
} else {
if (LOG)
System.out.println("Filling block " + block + ", page " + page);
ret = nand.read(tmpBuffer, block + block_offset, page);
// Fill buffer from medium
for (int i = 0; i < NandLowLevel.WORDS; i++) {
BufferedBlocks[block][(page*NandLowLevel.WORDS)+i] = tmpBuffer[i];
}
BufferFilled[block][page] = true;
}
} else {
if (LOG)
System.out.print("Reading unbuffered block " + block + ", page " + page);
ret = nand.read(tmpBuffer, block + block_offset, page);
}
if (ret>0) {
// NAND read successful
// Spread the bytes which are combined into one 32 bit integer in tmpBuffer
// into separate fields of buffer.
int tmpIndex = 0;
for (int i = 0; i < size; i += 4) {
// Invert bytes before writing
// (see ClearMedium() for explanation)
buffer[i+3] = ((~tmpBuffer[tmpIndex]) & 0xFF000000) >>> 24;
buffer[i+2] = ((~tmpBuffer[tmpIndex]) & 0x00FF0000) >>> 16;
buffer[i+1] = ((~tmpBuffer[tmpIndex]) & 0x0000FF00) >>> 8;
buffer[i+0] = ((~tmpBuffer[tmpIndex]) & 0x000000FF);
tmpIndex++;
}
ret=0;
}
if (LOG)
System.out.println(" ...done.");
} else {
System.out.println("NAND Flash not available!");
ret = -1;
}
return ret;
}
public int WriteSector(int addr, int[] buffer) {
return WriteSector(addr, buffer, true);
}
public int WriteSector(int addr, int[] buffer, boolean buffered) {
int ret=-1;
// Compare current content of NAND to contents to be written.
if (readBeforeWrite) {
ReadSector(addr, readBuffer);
int readEqualsWrite = 1;
for (int i = 0; i < size; i++) {
if (readBuffer[i] != buffer[i]) {
readEqualsWrite = 0;
break;
}
}
// Sector already contains the data we wanted to write -
// abort write and report success.
if (readEqualsWrite == 1)
return 0;
}
// according to FatLowLevel interface the buffer size must be 512
if (buffer.length!=size) {
System.out.println("FatNand.WriteSector: buffer size " + size + " not correct!");
return -1;
}
// Combine every four entries of the buffer (each of which must be no larger than
// one byte) into one entry of compressedBuffer.
int[] compressedBuffer = new int[NandLowLevel.WORDS];
int compIndex = 0;
for (int i = 0; i < size; i += 4) {
compressedBuffer[compIndex] = (buffer[i]) +
(buffer[i+1] << 8) +
(buffer[i+2] << 16) +
(buffer[i+3] << 24);
// Invert bytes before reading (see ClearMedium() for explanation)
compressedBuffer[compIndex] = ~compressedBuffer[compIndex];
compIndex++;
}
// NAND available, continue
if (avail) {
int block = addr / NandLowLevel.PAGES_PER_BLOCK;
int page = addr % NandLowLevel.PAGES_PER_BLOCK;
// Write first sectors only into buffer
if (useBuffer && (buffered == true) && (block < NumBufferedBlocks)) {
if (LOG)
System.out.print("Writing buffered block " + block + ", page " + page);
for (int i = 0; i < NandLowLevel.WORDS; i++) {
BufferedBlocks[block][(page*NandLowLevel.WORDS)+i] = compressedBuffer[i];
}
ret = 0;
} else {
if (LOG)
System.out.print("Writing unbuffered block " + block + ", page " + page);
if (nand.write(compressedBuffer, block + block_offset, page, size)) {
// NAND write successful
ret=0;
}
}
}
if (LOG)
System.out.println(" ...done.");
return ret;
}
public void ClearMedium() {
for (int i = block_offset; i < nand.getNrOfBlocks(); i++) {
// Erasing sets all the bytes to 0xFF. To compensate for that,
// the ReadSector() and WriteSector() methods invert the
// read or written bytes.
nand.erase(i);
}
}
public int GetTotalBytes() {
return (nand.size()-block_offset)*16*1024;
}
}