mirror of https://github.com/contiki-ng/mspsim
Emulation support for the Microchip ENC28J60 Ethernet transceiver
This commit is contained in:
parent
e66a9c7125
commit
5d60f2736c
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Thingsquare.
|
||||
* 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 is a stub implementation for the Enc28j60 Ethernet chip. This ignores
|
||||
* the majority of configuration and setup, and only provides the very minimum
|
||||
* functionality to support a simple bit-banged Thingsquare Mist driver. */
|
||||
|
||||
package se.sics.mspsim.chip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import se.sics.mspsim.core.Chip;
|
||||
import se.sics.mspsim.core.IOPort;
|
||||
import se.sics.mspsim.core.IOPort.PinState;
|
||||
import se.sics.mspsim.core.MSP430Core;
|
||||
|
||||
public class Enc28J60 extends Chip {
|
||||
protected boolean DEBUG = false;
|
||||
|
||||
public static final int EIE = 0x1b;
|
||||
public static final int EIR = 0x1c;
|
||||
public static final int ESTAT = 0x1d;
|
||||
public static final int ECON2 = 0x1e;
|
||||
public static final int ECON1 = 0x1f;
|
||||
|
||||
public static final int ESTAT_CLKRDY = 0x01;
|
||||
public static final int ESTAT_TXABRT = 0x02;
|
||||
|
||||
public static final int ECON1_RXEN = 0x04;
|
||||
public static final int ECON1_TXRTS = 0x08;
|
||||
|
||||
public static final int ECON2_AUTOINC = 0x80;
|
||||
public static final int ECON2_PKTDEC = 0x40;
|
||||
|
||||
public static final int EPKTCNT = 0x19;
|
||||
|
||||
private static final int WBM_COMMAND = 0x3a; /* Note: 0x7a with write bit set */
|
||||
private static final int RBM_COMMAND = 0x3a; /* Note: 0x7a with write bit set */
|
||||
|
||||
private IOPort myPort;
|
||||
|
||||
private int myClk;
|
||||
private int myMosi;
|
||||
private int myChipSelect;
|
||||
private int myMisoBit;
|
||||
|
||||
public Enc28J60(MSP430Core cpu, IOPort port, int clk, int mosi,
|
||||
int miso, int chipSelect) {
|
||||
super("Enc28J60", "Ethernet", cpu);
|
||||
|
||||
myPort = port;
|
||||
myClk = (1 << clk);
|
||||
myMosi = (1 << mosi);
|
||||
myChipSelect = (1 << chipSelect);
|
||||
|
||||
myMisoBit = miso;
|
||||
}
|
||||
|
||||
public void log(String msg) {
|
||||
if (DEBUG) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writingToWBM = false;
|
||||
private boolean readingFromRBM = false;
|
||||
private boolean nextEcon1 = false;
|
||||
private boolean nextEcon2 = false;
|
||||
|
||||
private ArrayList<Byte> wbmData = new ArrayList<Byte>();
|
||||
|
||||
private ArrayList<RbmPacket> rbmPackets = new ArrayList<RbmPacket>();
|
||||
private static class RbmPacket {
|
||||
ArrayList<Byte> data = new ArrayList<Byte>();
|
||||
boolean wasRead = false;
|
||||
}
|
||||
|
||||
public void writePacket(byte[] data) {
|
||||
RbmPacket p = new RbmPacket();
|
||||
int len = data.length;
|
||||
|
||||
p.data.add(new Byte((byte) 0x00)); /* ignored: next packet pointer */
|
||||
p.data.add(new Byte((byte) 0x00)); /* ignored: next packet pointer */
|
||||
|
||||
p.data.add(new Byte((byte) (len & 0xff))); /* length */
|
||||
p.data.add(new Byte((byte) ((len >> 8) & 0xff))); /* length */
|
||||
|
||||
p.data.add(new Byte((byte) 0x00)); /* ignored: status */
|
||||
p.data.add(new Byte((byte) 0x00)); /* ignored: status */
|
||||
|
||||
for (byte b : data) {
|
||||
p.data.add(new Byte(b)); /* data */
|
||||
}
|
||||
|
||||
rbmPackets.add(p);
|
||||
log("Enc28j60: nr pending packets increased to: " + rbmPackets.size());
|
||||
}
|
||||
|
||||
private PacketListener listener = null;
|
||||
public static interface PacketListener {
|
||||
public void packetSent(Byte[] packetData);
|
||||
}
|
||||
public void setPacketListener(PacketListener l) {
|
||||
listener = l;
|
||||
}
|
||||
|
||||
private int inputByte(int data) {
|
||||
int val = 0x00;
|
||||
|
||||
if (writingToWBM) {
|
||||
wbmData.add(new Byte((byte) data));
|
||||
val = 0x00;
|
||||
return val;
|
||||
} else if (readingFromRBM) {
|
||||
if (rbmPackets.size() > 0) {
|
||||
if (rbmPackets.get(0).data.isEmpty()) {
|
||||
log("Enc28j60: warning, packet data is already consumed, returning 0");
|
||||
} else {
|
||||
val = rbmPackets.get(0).data.remove(0);
|
||||
rbmPackets.get(0).wasRead = true;
|
||||
}
|
||||
} else {
|
||||
log("Enc28j60: warning, no packet in rbm, returning 0");
|
||||
val = 0x00;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/* Strip optional write flag */
|
||||
boolean writing = (data & 0x40) != 0;
|
||||
data = (data & ~0x40);
|
||||
|
||||
if (nextEcon1) {
|
||||
if ((data & ECON1_TXRTS) != 0) {
|
||||
log("Transmitting enc28j60 packet, size: " + wbmData.size());
|
||||
if (listener != null) {
|
||||
wbmData.remove(0);
|
||||
listener.packetSent(wbmData.toArray(new Byte[0]));
|
||||
}
|
||||
wbmData.clear();
|
||||
}
|
||||
nextEcon1 = false;
|
||||
} else if (nextEcon2) {
|
||||
if (!rbmPackets.isEmpty() && rbmPackets.get(0).wasRead) {
|
||||
rbmPackets.remove(0);
|
||||
log("Enc28j60: nr pending packets decreased to: " + rbmPackets.size());
|
||||
}
|
||||
nextEcon2 = false;
|
||||
} else if (data == ECON1) {
|
||||
/* we are awaiting a ECON1 command */
|
||||
nextEcon1 = true;
|
||||
} else if (data == ECON2) {
|
||||
/* we are awaiting a ECON2 command */
|
||||
nextEcon2 = true;
|
||||
} else if (data == EPKTCNT) {
|
||||
return rbmPackets.size();
|
||||
} else if (data == ESTAT) {
|
||||
/* chip is always ready */
|
||||
val = ESTAT_CLKRDY;
|
||||
} else if (writing && data == WBM_COMMAND) {
|
||||
writingToWBM = true;
|
||||
} else if (data == RBM_COMMAND) {
|
||||
if (rbmPackets.size() > 0) {
|
||||
if (rbmPackets.get(0).data.isEmpty()) {
|
||||
log("Enc28j60: warning, packet data is already consumed, returning 0");
|
||||
} else {
|
||||
val = rbmPackets.get(0).data.remove(0);
|
||||
rbmPackets.get(0).wasRead = true;
|
||||
}
|
||||
} else {
|
||||
log("Enc28j60: warning, no packet in rbm, returning 0");
|
||||
val = 0x00;
|
||||
}
|
||||
readingFromRBM = true;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
private int spiOut = 0; /* byte being sent over SPI */
|
||||
private int spiOutCount = 0;
|
||||
private int spiIn = 0; /* byte being received over SPI */
|
||||
private int spiInCount = 0;
|
||||
public void write(IOPort port, int data) {
|
||||
if (port != myPort) {
|
||||
/* ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
boolean chipSelect = (data & myChipSelect) == 0;
|
||||
boolean clk = (data & myClk) != 0;
|
||||
boolean mosi = (data & myMosi) != 0;
|
||||
|
||||
if (!chipSelect) {
|
||||
if (readingFromRBM && spiOutCount == 0 && !rbmPackets.isEmpty()) {
|
||||
/* XXX Hack: pushing back last byte to rbm */
|
||||
rbmPackets.get(0).data.add(0, (byte)spiOut);
|
||||
}
|
||||
writingToWBM = false;
|
||||
readingFromRBM = false;
|
||||
}
|
||||
|
||||
if (!chipSelect || !clk) {
|
||||
/* ignore */
|
||||
//log("write ignored: chipSelect " + chipSelect + ", clk " + clk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare next outgoing bit on miso */
|
||||
if (spiOutCount < 8) {
|
||||
spiOutCount++;
|
||||
|
||||
if ((spiOut & 0x80) != 0) {
|
||||
port.setPinState(myMisoBit, PinState.HI);
|
||||
} else {
|
||||
port.setPinState(myMisoBit, PinState.LOW);
|
||||
}
|
||||
|
||||
spiOut = (spiOut << 1);
|
||||
}
|
||||
|
||||
/* Read next incoming bit on mosi */
|
||||
spiIn = (spiIn << 1);
|
||||
if (mosi) {
|
||||
spiIn |= 0x01;
|
||||
}
|
||||
spiInCount++;
|
||||
|
||||
if (spiInCount >= 8) {
|
||||
/* We've now read all 8 bits on mosi */
|
||||
spiOut = inputByte(spiIn);
|
||||
spiOutCount = 0;
|
||||
spiIn = 0;
|
||||
spiInCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getConfiguration(int parameter) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getModeMax() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue