mirror of https://github.com/contiki-ng/mspsim
404 lines
11 KiB
Java
404 lines
11 KiB
Java
/**
|
|
* Copyright (c) 2007, Swedish Institute of Computer Science.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* This file is part of MSPSim.
|
|
*
|
|
* $Id: M25P80.java 177 2008-03-11 15:32:12Z nifi $
|
|
*
|
|
* -----------------------------------------------------------------
|
|
*
|
|
* ExtFlash
|
|
*
|
|
* Author : Joakim Eriksson, Fredrik Osterlind
|
|
* Created : Sun Oct 21 22:00:00 2007
|
|
* Updated : $Date: 2008-03-11 16:32:12 +0100 (ti, 11 mar 2008) $
|
|
* $Revision: 177 $
|
|
*/
|
|
|
|
package se.sics.mspsim.chip;
|
|
import java.io.IOException;
|
|
import se.sics.mspsim.core.*;
|
|
import se.sics.mspsim.util.Utils;
|
|
|
|
public abstract class M25P80 extends Chip implements USARTListener, PortListener, Memory {
|
|
|
|
public static final int WRITE_STATUS = 0x01;
|
|
public static final int PAGE_PROGRAM = 0x02;
|
|
public static final int READ_DATA = 0x03;
|
|
public static final int WRITE_DISABLE = 0x04;
|
|
public static final int READ_STATUS = 0x05;
|
|
public static final int WRITE_ENABLE = 0x06;
|
|
public static final int READ_DATA_FAST = 0x0b;
|
|
public static final int READ_IDENT = 0x9f;
|
|
public static final int SECTOR_ERASE = 0xd8;
|
|
public static final int BULK_ERASE = 0xc7;
|
|
public static final int DEEP_POWER_DOWN = 0xb9;
|
|
public static final int WAKE_UP = 0xab;
|
|
|
|
public static final int STATUS_MASK = 0x9C;
|
|
|
|
public static final int MEMORY_SIZE = 1024 * 1024;
|
|
|
|
public static final int CHIP_SELECT = 0x10;
|
|
|
|
private static final double PROGRAM_PAGE_MILLIS = 1.0; // 0.8 - 5 ms
|
|
private static final double SECTOR_ERASE_MILLIS = 800; // 800 - 3 000 ms
|
|
|
|
private int state = 0;
|
|
private boolean chipSelect;
|
|
|
|
private int pos;
|
|
private int status = 0;
|
|
|
|
private boolean writeEnable = false;
|
|
private boolean writing = false;
|
|
|
|
private int[] identity = new int[] {
|
|
0x20,0x20,0x14,0x10,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
|
};
|
|
private int readAddress;
|
|
private int loadedAddress = -1;
|
|
private int blockWriteAddress;
|
|
private byte[] readMemory = new byte[256];
|
|
private byte[] buffer = new byte[256];
|
|
|
|
private TimeEvent writeEvent = new TimeEvent(0, "M25P80 Writing") {
|
|
public void execute(long t) {
|
|
writing = false;
|
|
}};
|
|
|
|
public M25P80(MSP430Core cpu) {
|
|
super("M25P80", "External Flash", cpu);
|
|
}
|
|
|
|
@Override
|
|
public void notifyReset() {
|
|
writing = false;
|
|
}
|
|
|
|
public int getStatus() {
|
|
return status | (writeEnable ? 0x02 : 0x00) | (writing ? 0x01 : 0x00);
|
|
}
|
|
|
|
public void stateChanged(int state) {
|
|
}
|
|
|
|
public void dataReceived(USARTSource source, int data) {
|
|
if (chipSelect) {
|
|
if (DEBUG) {
|
|
log("byte received: " + data);
|
|
}
|
|
switch(state) {
|
|
case READ_STATUS:
|
|
if (DEBUG) {
|
|
log("Read status => " + getStatus() + " from $" + Utils.hex(cpu.getPC(), 4));
|
|
}
|
|
source.byteReceived(getStatus());
|
|
return;
|
|
case READ_IDENT:
|
|
source.byteReceived(identity[pos]);
|
|
pos++;
|
|
if (pos >= identity.length) {
|
|
pos = 0;
|
|
}
|
|
return;
|
|
case WRITE_STATUS:
|
|
status = data & STATUS_MASK;
|
|
source.byteReceived(0);
|
|
return;
|
|
case READ_DATA:
|
|
if (pos < 3) {
|
|
readAddress = (readAddress << 8) + data;
|
|
source.byteReceived(0);
|
|
pos++;
|
|
if (DEBUG && pos == 3) {
|
|
log("reading from $" + Integer.toHexString(readAddress));
|
|
}
|
|
} else {
|
|
source.byteReceived(readMemory(readAddress++));
|
|
if (readAddress > 0xfffff) {
|
|
readAddress = 0;
|
|
}
|
|
}
|
|
return;
|
|
case SECTOR_ERASE:
|
|
if (pos < 3) {
|
|
readAddress = (readAddress << 8) + data;
|
|
source.byteReceived(0);
|
|
pos++;
|
|
if (pos == 3) {
|
|
// Clear buffer
|
|
sectorErase(readAddress);
|
|
}
|
|
}
|
|
return;
|
|
case PAGE_PROGRAM:
|
|
if (pos < 3) {
|
|
readAddress = (readAddress << 8) + data;
|
|
source.byteReceived(0);
|
|
pos++;
|
|
if (pos == 3) {
|
|
// Clear buffer
|
|
for (int i = 0; i < buffer.length; i++) {
|
|
buffer[i] = (byte) 0xff;
|
|
}
|
|
blockWriteAddress = readAddress & 0xfff00;
|
|
if (DEBUG) {
|
|
log("programming at $" + Integer.toHexString(readAddress));
|
|
}
|
|
}
|
|
} else {
|
|
// Do the programming!!!
|
|
source.byteReceived(0);
|
|
writeBuffer((readAddress++) & 0xff, data);
|
|
}
|
|
return;
|
|
}
|
|
if (DEBUG) {
|
|
log("new command: " + data);
|
|
}
|
|
switch (data) {
|
|
case WRITE_ENABLE:
|
|
if (DEBUG) {
|
|
log("Write Enable");
|
|
}
|
|
writeEnable = true;
|
|
break;
|
|
case WRITE_DISABLE:
|
|
if (DEBUG) {
|
|
log("Write Disable");
|
|
}
|
|
writeEnable = false;
|
|
break;
|
|
case READ_IDENT:
|
|
if (DEBUG) {
|
|
log("Read ident.");
|
|
}
|
|
state = READ_IDENT;
|
|
pos = 0;
|
|
source.byteReceived(0);
|
|
return;
|
|
case READ_STATUS:
|
|
state = READ_STATUS;
|
|
source.byteReceived(0);
|
|
return;
|
|
case WRITE_STATUS:
|
|
if (DEBUG) {
|
|
log("Write status");
|
|
}
|
|
state = WRITE_STATUS;
|
|
break;
|
|
case READ_DATA:
|
|
if (DEBUG) {
|
|
log("Read Data");
|
|
}
|
|
state = READ_DATA;
|
|
pos = readAddress = 0;
|
|
break;
|
|
case PAGE_PROGRAM:
|
|
if (DEBUG) {
|
|
log("Page Program");
|
|
}
|
|
state = PAGE_PROGRAM;
|
|
pos = readAddress = 0;
|
|
break;
|
|
case SECTOR_ERASE:
|
|
if (DEBUG) {
|
|
log("Sector Erase");
|
|
}
|
|
state = SECTOR_ERASE;
|
|
pos = 0;
|
|
break;
|
|
case BULK_ERASE:
|
|
log("Bulk Erase");
|
|
break;
|
|
}
|
|
source.byteReceived(0);
|
|
}
|
|
}
|
|
|
|
/***********************************************
|
|
* Memory interface methods
|
|
***********************************************/
|
|
public int readByte(int address) {
|
|
byte[] data = new byte[256];
|
|
try {
|
|
loadMemory(address, data);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return ~data[address & 0xff];
|
|
}
|
|
|
|
public void writeByte(int address, int data) {
|
|
byte[] mem = new byte[256];
|
|
try {
|
|
loadMemory(address, mem);
|
|
mem[address & 0xff] = (byte) ~data;
|
|
writeBack(address, mem);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
if (loadedAddress >= 0
|
|
&& ((loadedAddress & 0xfff00) == (address & 0xfff00))) {
|
|
loadedAddress = -1;
|
|
}
|
|
}
|
|
|
|
public int getSize() {
|
|
return MEMORY_SIZE;
|
|
}
|
|
|
|
// Should return correct data!
|
|
private int readMemory(int address) {
|
|
if (DEBUG) {
|
|
log("Reading memory address: " + Integer.toHexString(address));
|
|
}
|
|
ensureLoaded(address);
|
|
return readMemory[address & 0xff];
|
|
}
|
|
|
|
private void writeBuffer(int address, int data) {
|
|
buffer[address] = (byte) data;
|
|
}
|
|
|
|
private void ensureLoaded(int address) {
|
|
if (loadedAddress < 0
|
|
|| ((loadedAddress & 0xfff00) != (address & 0xfff00))) {
|
|
try {
|
|
if (DEBUG) {
|
|
log("Loading memory: " + (address & 0xfff00));
|
|
}
|
|
loadMemory(address, readMemory);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
loadedAddress = address & 0xfff00;
|
|
}
|
|
}
|
|
|
|
private void loadMemory(int address, byte[] readMemory) throws IOException {
|
|
seek(address & 0xfff00);
|
|
readFully(readMemory);
|
|
for (int i = 0; i < readMemory.length; i++) {
|
|
readMemory[i] = (byte) (~readMemory[i] & 0xff);
|
|
}
|
|
}
|
|
|
|
public boolean getChipSelect() {
|
|
return chipSelect;
|
|
}
|
|
|
|
public void portWrite(IOPort source, int data) {
|
|
// Chip select = active low...
|
|
if (chipSelect && (data & CHIP_SELECT) != 0) {
|
|
// Chip select will go "off"
|
|
switch(state) {
|
|
case PAGE_PROGRAM:
|
|
programPage();
|
|
break;
|
|
}
|
|
}
|
|
chipSelect = (data & CHIP_SELECT) == 0;
|
|
// if (DEBUG) log("write to Port4: " +
|
|
// Integer.toString(data, 16)
|
|
// + " CS:" + chipSelect);
|
|
state = 0;
|
|
}
|
|
|
|
private void writeStatus(double time) {
|
|
writing = true;
|
|
cpu.scheduleTimeEventMillis(writeEvent, time);
|
|
}
|
|
|
|
private void programPage() {
|
|
if (writing) logw("Can not set program page while already writing... from $" + Utils.hex(cpu.getPC(), 4));
|
|
writeStatus(PROGRAM_PAGE_MILLIS);
|
|
ensureLoaded(blockWriteAddress);
|
|
for (int i = 0; i < readMemory.length; i++) {
|
|
readMemory[i] &= buffer[i];
|
|
}
|
|
writeBack(blockWriteAddress, readMemory);
|
|
}
|
|
|
|
private void sectorErase(int address) {
|
|
writeStatus(SECTOR_ERASE_MILLIS);
|
|
int sectorAddress = address & 0xf0000;
|
|
loadedAddress = -1;
|
|
for (int i = 0; i < buffer.length; i++) {
|
|
buffer[i] = (byte)0xff;
|
|
}
|
|
// Erase a complete sector
|
|
blockWriteAddress = sectorAddress;
|
|
for (int i = 0; i < 0x100; i++) {
|
|
if (DEBUG) {
|
|
log("erasing at $" + Integer.toHexString(blockWriteAddress));
|
|
}
|
|
writeBack(blockWriteAddress, buffer);
|
|
blockWriteAddress += 0x100;
|
|
}
|
|
}
|
|
|
|
|
|
private void writeBack(int address, byte[] data) {
|
|
try {
|
|
byte[] tmp = new byte[data.length];
|
|
if (DEBUG) {
|
|
log("Writing data to disk at $" + Integer.toHexString(address));
|
|
}
|
|
seek(address & 0xfff00);
|
|
for (int i = 0; i < data.length; i++) {
|
|
tmp[i] = (byte) (~data[i] & 0xff);
|
|
}
|
|
write(tmp);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public int getModeMax() {
|
|
return 0;
|
|
}
|
|
|
|
public abstract void seek(long pos) throws IOException;
|
|
public abstract int readFully(byte[] b) throws IOException;
|
|
public abstract void write(byte[] b) throws IOException;
|
|
|
|
/* by default - there is not configuration to return for m25p80 */
|
|
public int getConfiguration(int param) {
|
|
return 0;
|
|
}
|
|
|
|
public String info() {
|
|
return " Status: " + getStatus() + " Write Enabled: " + writeEnable
|
|
+ " Write in Progress: " + writing + '\n' + " Chip Select: " + chipSelect;
|
|
}
|
|
|
|
} // M25P80
|