Commit 63d79e70 authored by Vincent van Beveren's avatar Vincent van Beveren

Initial MultiSFP support

parent 7a60e994
= SFP+ support for PTP-core
Main goals:
- Monitor health of transcier
- Enhanced calibration for improved time accuracy
Means:
- Read out of additional Digital Diagnostic ADC values
- Storing of additional delays in the EEPROM of the SFP transceiver
== Monitoring of health of transceiver
- Reading of voltage / current of transceiver
- Reading of temperature
- Reading of the RX-ed power
Monitoring information must be send and stored in an external server
== Enhanced calibration for improved time accuracy
Additional EEPROM calibration parameter storage:
- Transceiver nominal RX and TX delays
- Optical power delay curve (induced by amplifier)
- Also: Version, Date and UID
B<?xml version="1.0" encoding="utf-16"?>
package nl.nikhef.sfp;
import java.io.IOException;
import com.ftdi.FTD2XXException;
import com.ftdi.FTDevice;
import nl.nikhef.rebus.dev.PCA9848;
import nl.nikhef.rebus.ftdi.I2C;
import nl.nikhef.rebus.ftdi.JavaFTD2xxMPSSE;
import nl.nikhef.rebus.ftdi.MPSSE;
import nl.nikhef.sfp.i2c.I2CLink;
public class MultiSFPDevice extends SFPDeviceBase {
private static final int PCA_RESET = I2C.GPIO_AL0;
private static final int ACTIVE_LED = I2C.GPIO_AL1;
private static final int TX_LED = I2C.GPIO_AL2;
private static final int RX_LED = I2C.GPIO_AL3;
private static final int OUTPUT_MASK = ACTIVE_LED | TX_LED | RX_LED;
private static final int NO_OF_BAYS = 4;
private FTDevice _portA; // FTDI Port A device
private String _serial;
private I2C _i2c;
private MPSSE _mpsse;
private PCA9848 _mux;
private int _selected = -1;
private MultiSFPI2CLink[] _links = new MultiSFPI2CLink[NO_OF_BAYS];
private Thread _thread;
public MultiSFPDevice(String ftdiSerial) throws IOException
{
_serial = ftdiSerial;
try {
_portA = FTDevice.getDevicesBySerialNumber(ftdiSerial + "A").get(0);
} catch (FTD2XXException e) {
throw new IOException("Could not initialize FTDI", e);
}
_mpsse = new JavaFTD2xxMPSSE(_portA);
_i2c = new I2C(_mpsse, 400000, false);
_i2c.setModes(OUTPUT_MASK | PCA_RESET, OUTPUT_MASK | PCA_RESET);
// Reset PCA9848
_i2c.setOutputs(0, PCA_RESET);
_i2c.setOutputs(PCA_RESET, PCA_RESET);
setLeds(false, false);
// _i2c.setOutputs(0xF0, 0xE0);
_i2c.wakeI2C();
_mux = new PCA9848(_i2c, PCA9848.ADDRESS_HHH);
_mux.setSingle(-1);
for (int i = 0; i < _links.length; ++i)
{
_links[i] = new MultiSFPI2CLink(i);
}
_thread = new Thread("MultiSFP " + ftdiSerial) {
public void run() {
multiRun();;
};
};
_thread.setDaemon(true);
_thread.start();
}
private void multiRun()
{
}
@Override
public String getSerial() {
return _serial;
}
@Override
public int getBayCount() {
return NO_OF_BAYS;
}
@Override
public I2CLink getLink(int bay) throws IOException {
return _links[bay];
}
@Override
public boolean isModulePresent(int bay) {
return true;
}
private void setLeds(boolean tx, boolean rx) throws IOException
{
int leds = ACTIVE_LED;
if (tx) leds |= TX_LED;
if (rx) leds |= RX_LED;
_i2c.setOutputs(leds, OUTPUT_MASK);
}
private void select(int bay) throws IOException {
try {
_mux.setSingle(bay);
} catch (IOException ioe) {
_selected = -1;
throw ioe;
}
_selected = bay;
}
private synchronized byte[] i2cWrRd(int addr, byte[] dataWr, boolean contRd, int rdLen) throws IOException {
setLeds(true, true);
byte[] dta = _i2c.writeRead(addr, dataWr, rdLen);
setLeds(false, false);
return dta;
}
private synchronized void i2cWrite(int addr, byte[] data) throws IOException {
setLeds(true, false);
_i2c.write(addr, data);
setLeds(false, false);
}
private synchronized byte[] i2cRead(int addr, int len) throws IOException {
setLeds(false, true);
byte[] dta = _i2c.read(addr, len);
setLeds(false, false);
return dta;
}
private class MultiSFPI2CLink implements I2CLink {
private final int _muxPort;
public MultiSFPI2CLink(int muxPort) {
_muxPort = muxPort;
}
@Override
public void open() throws IOException
{
select(_muxPort);
}
@Override
public void close() throws IOException {
select(-1);
}
@Override
public void shutdown() throws IOException {
}
public void checkSelected() throws IOException
{
if (_muxPort != _selected) {
throw new IOException(String.format("Device bay %d is not selected, %d is", _muxPort, _selected));
}
}
@Override
public void i2cWrite(int addr, byte[] data) throws IOException {
checkSelected();
MultiSFPDevice.this.i2cWrite(addr, data);
}
@Override
public byte[] i2cRead(int addr, int len) throws IOException {
checkSelected();
return MultiSFPDevice.this.i2cRead(addr, len);
}
@Override
public byte[] i2cWrRd(int addr, byte[] dataWr, boolean contRd, int rdLen) throws IOException {
checkSelected();
return MultiSFPDevice.this.i2cWrRd(addr, dataWr, contRd, rdLen);
}
}
}
package nl.nikhef.sfp;
import java.io.IOException;
import java.util.List;
import com.ftdi.DeviceType;
import com.ftdi.FTDevice;
public class MultiSFPProvider extends SFPProviderBase
{
@Override
public String getName() {
return "MultiSFP";
}
public MultiSFPDevice getDeviceBySerial(String serial) {
for (SFPDevice dev : getDevices()) {
if (dev.getSerial().equals(serial)) return MultiSFPDevice.class.cast(dev);
}
return null;
}
@Override
public void scanForDevices() {
try {
List<FTDevice> devs = FTDevice.getDevices();
for (FTDevice devRaw : devs)
{
if (devRaw.getDevType() == DeviceType.DEVICE_2232H && devRaw.getDevDescription().equals("MultiSFP A")) {
// Only get the primary 'A' device, we know the second.
String serial = devRaw.getDevSerialNumber();
// chop of the last part from the serial:
serial = serial.substring(0, serial.length() - 1);
MultiSFPDevice dev = getDeviceBySerial(serial);
if (dev == null)
{
dev = new MultiSFPDevice(serial);
}
updAdd(dev);
}
}
} catch (IOException e) {
e.printStackTrace();
}
updProcess();
}
}
package nl.nikhef.sfp;
public class MultiSFPProviderFactory implements SFPProviderFactory {
@Override
public SFPProvider[] getProviders() {
return new SFPProvider[] { new MultiSFPProvider() };
}
}
......@@ -6,6 +6,7 @@ package nl.nikhef.sfp;
import java.io.IOException;
import java.util.Collection;
import nl.nikhef.sfp.i2c.I2CLink;
import nl.nikhef.tools.ListenerManager;
/**
......@@ -30,9 +31,18 @@ public abstract class SFPDeviceBase implements SFPDevice {
if (isModulePresent(bay))
{
try {
byte[] serialbytes = getLink(bay).i2cWrRd(I2C_EEPROM_ADDR, new byte[] { (byte)I2C_EEPROM_SERIAL_OFFSET }, true, I2C_EEPROM_SERIAL_LENGTH);
I2CLink i2c = getLink(bay);
byte[] serialbytes;
try {
i2c.open();
serialbytes = i2c.i2cWrRd(I2C_EEPROM_ADDR, new byte[] { (byte)I2C_EEPROM_SERIAL_OFFSET }, true, I2C_EEPROM_SERIAL_LENGTH);
} finally {
i2c.close();
}
return new String(serialbytes).trim();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
......
......@@ -25,9 +25,10 @@ public class SFPManager {
private List<SFPProvider> _umProviders = Collections.unmodifiableList(_providers);
private SFPManager() {
private SFPManager()
{
_providers.add(new SimSFPProvider());
_providers.add(new SimpleFTDIProvider());
_providers.add(new MultiSFPProvider());
}
......@@ -58,7 +59,7 @@ public class SFPManager {
public void scanForDevices() {
for (SFPProvider prov : _providers){
prov.scanBays();
prov.scanForDevices();
}
}
......
......@@ -13,6 +13,6 @@ public interface SFPProvider {
public String getName();
public void scanBays();
public void scanForDevices();
}
......@@ -8,7 +8,7 @@ public class SimSFPProvider extends SFPProviderBase {
private SimSFPDevice _device = new SimSFPDevice("SIM1", 4);
@Override
public void scanBays() {
public void scanForDevices() {
if (!_firstRun) return;
addDevice(_device);
......
......@@ -23,7 +23,7 @@ public class SimpleFTDIProvider extends SFPProviderBase {
@Override
public void scanBays()
public void scanForDevices()
{
try {
List<FTDevice> devs = FTDevice.getDevices();
......
......@@ -100,6 +100,7 @@ public class DDMI extends DDMIGroup {
public boolean verifyChecksums(DDMIContext ctx) {
for (DataSource ds : _dataSources.values())
{
if (!ds.isValid(ctx)) continue;
if (!ds.verifyChecksums(ctx)) return false;
}
return true;
......
......@@ -16,6 +16,8 @@ public class DDMIContext
private Globals _globals;
private I2CLink _i2cLink;
private DDMI _ddmi;
public final Map<String, Object> scratch = new HashMap<String, Object>();
private class ElementTable extends LuaValue
{
......
......@@ -181,8 +181,10 @@ public class DDMILoader {
private DataSource _parent = null;
private int _page = -1;
private String _id;
private String _condition;
private List<ChecksumProxy> _checksumProxy = new ArrayList<ChecksumProxy>();
private List<CacheProxy> _cacheProxy = new ArrayList<CacheProxy>();
@Override
public boolean setAttribute(String name, String val)
......@@ -210,7 +212,10 @@ public class DDMILoader {
_parent = SourceProxy.class.cast(proxy).getInstance();
return true;
}
if (name.equals("valid-if")) {
_condition = val;
return true;
}
return false;
}
......@@ -220,6 +225,10 @@ public class DDMILoader {
{
if (obj instanceof ChecksumProxy) {
_checksumProxy.add(ChecksumProxy.class.cast(obj));
} else if (obj instanceof CacheProxy) {
_cacheProxy.add(CacheProxy.class.cast(obj));
} else {
throw new RuntimeException("DataSource can't accept " + obj.getTypeName());
}
}
......@@ -241,6 +250,15 @@ public class DDMILoader {
ds.addChecksum(cp.offset, cp.start, cp.end);
}
for (CacheProxy cp : _cacheProxy) {
ds.addCache(cp.start, cp.end);
}
if (_condition != null) {
ds.setValidIf(_condition);
}
if (_id != null) ds.setId(_id);
return ds;
......@@ -285,16 +303,45 @@ public class DDMILoader {
public String getTypeName() {
return "Checksum";
}
}
public static class CacheProxy extends XOLProxyBase<Void> {
public int start = -1;
public int end = -1;
@Override
public boolean setAttribute(String name, String val)
{
if (name.equals("start")) {
start = XOL.parseInt(val);
return true;
}
if (name.equals("end")) {
end = XOL.parseInt(val);
return true;
}
return false;
}
@Override
public String getTypeName() {
return "Cache";
}
}
public DDMILoader()
{
_xol.setMapping("source", new SimpleProxyFactory<DataSource>(SourceProxy.class));
_xol.setMapping("checksum", new SimpleProxyFactory<Void>(ChecksumProxy.class));
_xol.setMapping("cache", new SimpleProxyFactory<Void>(CacheProxy.class));
_xol.setMapping("group", DDMIGroup.class);
_xol.setMapping("ddmi", DDMI.class);
_xol.setMapping("int", new DDMIValueFactory(DDMIValue.DDMIType.INTEGER_TYPE));
......
package nl.nikhef.sfp.ddmi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
public abstract class DataSource {
private String _condition;
private class Checksum
{
public final int offset;
......@@ -21,21 +27,67 @@ public abstract class DataSource {
public boolean check(DDMIContext ctx)
{
byte[] area = readMedia(ctx, start, ( end - start ) + 1);
byte sum = readMedia(ctx, offset, 1)[0];
byte[] area = read(ctx, start, ( end - start ) + 1);
byte sum = read(ctx, offset, 1)[0];
return SFPUtils.checkSum(area, 0, ( end - start ) + 1) == sum;
}
public void update(DDMIContext ctx)
{
byte[] area = readMedia(ctx, start, ( end - start ) + 1);
writeMedia(ctx, offset, new byte[] { SFPUtils.checkSum(area, 0, ( end - start ) + 1) } );
write(ctx, offset, new byte[] { SFPUtils.checkSum(area, 0, ( end - start ) + 1) } );
}
}
private class Cache
{
public final int start;
public final int end;
public final String key;
public Cache(int start, int end)
{
this.start = start;
this.end = end;
this.key = String.format("%s/%d:%d", _id != null ? _id : "obj:" + System.identityHashCode(DataSource.this), start, end);
}
public boolean appliesTo(int off, int len) {
return off >= start && (off + len - 1) <= end;
}
public byte[] read(DDMIContext ctx, int off, int len) {
byte[] b;
if (!ctx.scratch.containsKey(key))
{
b = readMedia(ctx, start, end - start + 1);
if (b == null) return null;
ctx.scratch.put(key, b);
} else {
b = (byte[])ctx.scratch.get(key);
}
return Arrays.copyOfRange(b, off - start, (off + len) - start);
}
public void write(DDMIContext ctx, int off, byte[] data) {
if (ctx.scratch.containsKey(key)) {
byte[] b = (byte[])ctx.scratch.get(key);
System.arraycopy(data, 0, b, off - start, data.length);
}
writeMedia(ctx, off, data);
}
}
private int _pageSelect = -1;
private List<Checksum> _checkSums;
private List<Cache> _caches;
private String _id;
protected DataSource()
......@@ -92,6 +144,7 @@ public abstract class DataSource {
public boolean verifyChecksums(DDMIContext ctx)
{
if (!isValid(ctx)) return true;
if (_checkSums == null) return true;
for (Checksum c : _checkSums)
{
......@@ -102,6 +155,7 @@ public abstract class DataSource {
public void updateChecksums(DDMIContext ctx)
{
if (!isValid(ctx)) return;
if (_checkSums == null) return;
for (Checksum c : _checkSums)
{
......@@ -111,17 +165,64 @@ public abstract class DataSource {
public byte[] read(DDMIContext ctx, int off, int len)
{
if (_caches != null)
{
for (Cache c : _caches)
{
if (!c.appliesTo(off, len)) continue;
return c.read(ctx, off, len);
}
}
return readMedia(ctx, off, len);
}
public void write(DDMIContext ctx, int off, byte[] data)
{
if (_caches != null)
{
for (Cache c : _caches)
{
if (!c.appliesTo(off, data.length)) continue;
c.write(ctx, off, data);
return;
}
}
writeMedia(ctx, off, data);
}
public void setValidIf(String condition)
{
_condition = condition;
}
public boolean isValid(DDMIContext ctx)
{
if (ctx.getI2CLink() == null) return false;
if (_condition == null) return true;
Globals globals = ctx.getGlobals();
LuaValue func = globals.load("return " + _condition);
LuaValue val = func.call();
boolean b= val.checkboolean();
return b;
}
abstract byte[] readMedia(DDMIContext ctx, int off, int len);
abstract void writeMedia(DDMIContext ctx, int off, byte[] data);
abstract void writeMedia(DDMIContext ctx, int off, byte[] data);
public void addCache(int start, int end)
{
if (_caches == null) _caches = new ArrayList<Cache>();
_caches.add(new Cache(start, end));
}
}
\ No newline at end of file
......@@ -20,13 +20,20 @@ public class I2CDataSource extends DataSource {
if (i2c == null) return new byte[len];
System.out.printf("Read media %02x, off=%d, len=%d\n", _addr, off, len);
byte[] offData = new byte[] { (byte)off };
try {
i2c.open();
return i2c.i2cWrRd(_addr, offData, true, len);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
i2c.close();
} catch (IOException e) {
}
}
}
......@@ -43,9 +50,16 @@ public class I2CDataSource extends DataSource {
out[i + 1] = data[i];
}
try {
i2c.open();
i2c.i2cWrite(_addr, out);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
i2c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
......
package nl.nikhef.sfp.ddmi;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
public class PageDataSource extends DataSource {
private final DataSource _parent;
......@@ -13,6 +16,12 @@ public class PageDataSource extends DataSource {
}
public boolean isValid(DDMIContext ctx)
{
return _parent.isValid(ctx);
}
private void selectPage(DDMIContext ctx)
{
......
......@@ -7,9 +7,11 @@
<source i2c-addr="x50" id="eeprom">
<checksum offset="63" start="0" end="62"/>
<checksum offset="95" start="64" end="94"/>
<cache start="0" end="255"/>
</source>
<source i2c-addr="x51" id="diag" page-select="127">
<source i2c-addr="x51" id="diag" page-select="127" valid-if="isset(id.montype, 6)">
<checksum offset="95" start="0" end="94"/>
<cache start="0" end="127"/>
</source>
<source page="0" parent-id="diag" id="diag_p0" />
<source page="1" parent-id="diag" id="diag_p1" />
......
......@@ -13,7 +13,7 @@ public class FTI2CLink implements I2CLink {
public FTI2CLink(MPSSE mpsse) throws IOException {
_mpsse = mpsse;
_i2c = new I2C(mpsse, I2C.SPEED_NORMAL);
_i2c = new I2C(mpsse, I2C.SPEED_NORMAL, true);
_i2c.wakeI2C();
}
......
package nl.nikhef.sfp.test;
import nl.nikhef.sfp.SFPDevice;
import nl.nikhef.sfp.SFPManager;
import nl.nikhef.sfp.SFPProvider;
import nl.nikhef.sfp.SFPProviderListener;
public class MultiSFPTest {
public static void main(String[] args)
{
SFPManager sfpMgr = SFPManager.get();
sfpMgr.addSFPProviderListener(new SFPProviderListener()
{
@Override
public void sfpDeviceRemoved(SFPProvider provider, SFPDevice dev) {
}
@Override
public void sfpDeviceAdded(SFPProvider provider, SFPDevice dev) {
System.out.printf("Device: %s/%s, Bays = %d\n", dev.getClass().getSimpleName(), dev.getSerial(), dev.getBayCount());
for (int i = 0; i < dev.getBayCount(); i++)
{
System.out.printf(" %d: ", i);
if (dev.isModulePresent(i)) {
System.out.printf("%s\n", dev.getModuleSerial(i));
} else {
System.out.println("Not connected");
}
}
}
});
sfpMgr.scanForDevices();
}
}
package nl.nikhef.rebus.dev;
import java.io.IOException;
import nl.nikhef.rebus.ftdi.I2C;
public class PCA9848 {
public static final int ADDRESS_BASE = 0x70;
public static final int ADDRESS_LLL = 0x70;
public static final int ADDRESS_LLH = 0x71;
public static final int ADDRESS_LHL = 0x72;
public static final int ADDRESS_LHH = 0x73;
public static final int ADDRESS_HLL = 0x74;
public static final int ADDRESS_HLH = 0x75;
public static final int ADDRESS_HHL = 0x76;
public static final int ADDRESS_HHH = 0x77;
private static final int MASK_CHANNEL_0 = 0x01;
private static final int MASK_CHANNEL_1 = 0x02;
private static final int MASK_CHANNEL_2 = 0x04;
private static final int MASK_CHANNEL_3 = 0x08;
private static final int MASK_CHANNEL_4 = 0x10;
private static final int MASK_CHANNEL_5 = 0x20;
private static final int MASK_CHANNEL_6 = 0x40;
private static final int MASK_CHANNEL_7 = 0x80;
private I2C _i2c;
private int _addr;
public PCA9848(I2C i2c, int addr)
{
_i2c = i2c;
_addr = addr;
}
/**
* Enable one or more channels.
*
* @param bitmask the mask
* @throws IOException On error
*/
public void setChannels(int bitmask) throws IOException
{
_i2c.write(_addr, new byte[] { (byte)bitmask });
}
/**
* Set single channel.
*
* @param chNo Channel number (0..7), or -1 to disable all.
*
* @throws IOException On error
*/
public void setSingle(int chNo) throws IOException {
if (chNo == -1) {
setChannels(0);
} else if (chNo >=0 && chNo <= 7) {
setChannels(1 << chNo);
} else {
throw new IOException(String.format("PCA9848 Channel %d does not exist", chNo));
}
}
public int getChannels() throws IOException {
byte[] b = _i2c.read(_addr, 1);
return b[0];
}
}
......@@ -123,8 +123,8 @@ public abstract class BusBase {
*/
public void setOutputs(int portOutputs, int portMask) throws IOException
{
outMask = ( outMask & ~portMask ) | ( portOutputs & portMask );
setGPIO(portMask);
outMask = ( portOutputs & ~portMask ) | ( portOutputs & portMask );
}
public void setClrPort(int port, boolean high) throws IOException
......
......@@ -15,7 +15,7 @@ public class I2C extends BusBase {
public I2C(MPSSE mpsse, int speed) throws IOException
public I2C(MPSSE mpsse, int speed, boolean ft232h) throws IOException
{
super(mpsse);
......@@ -23,8 +23,10 @@ public class I2C extends BusBase {
// configure clock speed
mpsse.cfgClock(false, true, speed);
// set pull down on ports AD0 1 and 2
mpsse.setDrive0Only(0x7);
if (ft232h) {
// set pull down on ports AD0 1 and 2
mpsse.setDrive0Only(0x7);
}
}
@Override
......
......@@ -21,7 +21,7 @@ public class I2CTests {
// MPSSE mpsse = new Ftd2xxjMPSSE(findDevice());
MPSSE mpsse = new JavaFTD2xxMPSSE(findFTDevice());
I2C i2c = new I2C(mpsse, I2C.SPEED_SLOW);
I2C i2c = new I2C(mpsse, I2C.SPEED_SLOW, true);
byte[] resp = i2c.writeRead(0x1D, new byte[] { 0x0D}, 1);
......
......@@ -4,6 +4,7 @@ import java.io.IOException;
import com.ftdi.BitModes;
import com.ftdi.DeviceType;
import com.ftdi.FTD2XXException;
import com.ftdi.FTDevice;
......@@ -50,19 +51,31 @@ public class JavaFTD2xxMPSSE extends MPSSE {
int q = ftdi.getQueueStatus();
if (q == 0) return;
if (q == 2 && ftdi.read() == 0xFA) {
StringBuilder sb = new StringBuilder("MPSSE Unexpected data:");
while (q > 0)
{
int b = ftdi.read();
ftdi.purgeBuffer(true, true);
throw new FTD2XXException(String.format("Bad Command: %02X", b));
if (b == 0xFA) {
if (q == 1) {
sb.append(" BadCommand=TRUNC!!");
} else {
b = ftdi.read();
sb.append(String.format(" BadCommand=%02x", b));
}
} else {
sb.append(String.format(" ???=%02x", b));
}
q = ftdi.getQueueStatus();
}
ftdi.purgeBuffer(true, true);
throw new FTD2XXException("Unexpected pending bytes (Queue status = " + q + ")");
throw new IOException(sb.toString());
}
public void close() throws FTD2XXException {
ftdi.close();
}
......
......@@ -97,7 +97,7 @@ public class SafariPark extends JFrame implements BaySelectionListener {
setDpi();
// setDpi();
SafariPark sfpE = new SafariPark();
sfpE.setVisible(true);
......
......@@ -49,7 +49,7 @@ public abstract class ValueEditor extends JPanel {
dirty = false;
updateLabel();
if (_ctx.getContext() == null)
if (_ctx.getContext() == null || !isValid())
{
rawValue = null;
setEnabled(false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment