/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Thomas Holland - initial API and implementation *******************************************************************************/ package de.innot.avreclipse.core.targets; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Set; import de.innot.avreclipse.core.avrdude.AVRDudeException; import de.innot.avreclipse.core.targets.ITargetConfiguration.Result; import de.innot.avreclipse.core.targets.ITargetConfiguration.ValidationResult; /** * @author Thomas Holland * @since 2.4 * */ public class AVRHardwareConfigValidator implements ITargetConfigConstants { public static List<ValidationResult> validate(ITargetConfiguration config) { List<ValidationResult> allresults = new ArrayList<ValidationResult>(); ValidationResult result; result = checkMCU(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } result = checkJTAGClock(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } result = checkJTAGDaisyChainBitsBefore(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } result = checkJTAGDaisyChainBitsAfter(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } result = checkJTAGDaisyChainUnitsBefore(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } result = checkJTAGDaisyChainUnitsAfter(config); if (!result.result.equals(Result.OK)) { allresults.add(result); } return allresults; } /** * Check if the current MCU is supported by all tools. * <p> * This method will return {@link Result#OK} iff * <ul> * <li>The current MCU is in the list of supported MCUs from both the Programmer tool and the * GDB Server (if they have been set).</li> * </ul> * If either tool has a list of supported mcus and the current mcu is not in them then * {@link Result#ERROR} is returned. * </p> * <p> * Calling this method may cause I/O activity (execution of the selected tools), it should not * be called directly from the UI Thread. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkMCU(ITargetConfiguration config) { try { String currentmcu = config.getMCU(); // Check if the MCU is valid for both the Programmer Tool and the GDBServer // If either tool returns null as a list of supported mcus, then it is // assumed that the tool does not care about mcus and that every mcu is valid. IProgrammerTool progtool = config.getProgrammerTool(); Set<String> progmcus = progtool.getMCUs(); boolean progtoolOK = progmcus != null ? progmcus.contains(currentmcu) : true; IGDBServerTool gdbserver = config.getGDBServerTool(); Set<String> gdbservermcus = gdbserver.getMCUs(); boolean gdbserverOK = gdbservermcus != null ? gdbservermcus.contains(currentmcu) : true; if (!progtoolOK && !gdbserverOK) { // Neither tool supports the mcu String progtoolname = progtool.getName(); String gdbservername = gdbserver.getName(); String msg; if (progtoolname.equals(gdbservername)) { msg = "MCU is not supported by programming tool / gdbserver " + progtoolname; } else { msg = "MCU is not supported by programming tool " + progtool.getName() + " and by gdbserver " + gdbserver.getName(); } return new ValidationResult(Result.ERROR, msg); } if (!progtoolOK) { String msg = "MCU not supported by programming tool " + progtool.getName(); return new ValidationResult(Result.ERROR, msg); } if (!progtoolOK) { String msg = "MCU not supported by gdbserver " + progtool.getName(); return new ValidationResult(Result.ERROR, msg); } } catch (AVRDudeException ade) { // Don't wan't to throw the exception, but we can't ignore it either. // so we just report an error with the exception text as description. String msg = ade.getLocalizedMessage(); return new ValidationResult(Result.ERROR, msg); } return ValidationResult.OK_RESULT; } /** * Check if the current Programmer is supported by all tools. * <p> * This method will return {@link Result#OK} iff * <ul> * <li>The current Programmer is in the list of supported Programmers from both the Programmer * tool and the GDB Server (if they have been set).</li> * </ul> * If either tool has a list of supported programmers and the current programmer is not in them * then {@link Result#ERROR} is returned. * </p> * <p> * Calling this method may cause I/O activity (execution of the selected tools), it should not * be called directly from the UI Thread. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkProgrammer(ITargetConfiguration config) { try { String currentprogger = config.getAttribute(ATTR_PROGRAMMER_ID); // Check if the Programmer is valid for both the Programmer Tool and the GDBServer // If either tool returns null as a list of supported programmers, then it is // assumed that the tool does not care about programmers and that every programmer is // valid. IProgrammerTool progtool = config.getProgrammerTool(); Set<String> progProggers = progtool.getProgrammers(); boolean progtoolOK = progProggers != null ? progProggers.contains(currentprogger) : true; IGDBServerTool gdbserver = config.getGDBServerTool(); Set<String> gdbserverProggers = gdbserver.getProgrammers(); boolean gdbserverOK = gdbserverProggers != null ? gdbserverProggers .contains(currentprogger) : true; if (!progtoolOK && !gdbserverOK) { // Neither tool supports the Programmer String progtoolname = progtool.getName(); String gdbservername = gdbserver.getName(); String msg; if (progtoolname.equals(gdbservername)) { msg = "Programmer interface is not supported by programming tool / gdbserver " + progtoolname; } else { msg = "Programmer interface is not supported by programming tool " + progtool.getName() + " and by gdbserver " + gdbserver.getName(); } return new ValidationResult(Result.ERROR, msg); } if (!progtoolOK) { String msg = "Programmer interface not supported by programming tool " + progtool.getName(); return new ValidationResult(Result.ERROR, msg); } if (!gdbserverOK) { String msg = "Programmer interface not supported by gdbserver " + progtool.getName(); return new ValidationResult(Result.WARNING, msg); } } catch (AVRDudeException ade) { // Don't wan't to throw the exception, but we can't ignore it either. // so we just report an error with the exception text as description. String msg = ade.getLocalizedMessage(); return new ValidationResult(Result.ERROR, msg); } return ValidationResult.OK_RESULT; } /** * Check the JTAG clock frequency. * <p> * This method will return {@link Result#WARN} iff * <ul> * <li>the target interface supports settable clocks</li> * <li>&& the bitclock is not set to the default</li> * <li>&& the bitclock is greater than 1/4th of the current FCPU</li> * </ul> * In all other cases {@link Result#OK} is returned. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkJTAGClock(ITargetConfiguration config) { // Check if the current configuration actually has a settable clock String programmerid = config.getAttribute(ATTR_PROGRAMMER_ID); IProgrammer programmer; programmer = config.getProgrammer(programmerid); int[] clocks = programmer.getTargetInterfaceClockFrequencies(); if (clocks.length > 0) { // OK, the target interface has a selectable clock. // Now check if the default is set ( = ""). The warning is // inhibited with the default because we don't know what value the // default might have. String bitclock = config.getAttribute(ATTR_JTAG_CLOCK); if (bitclock.length() > 0) { // Not the default but an actual value. // Finally check if the selected clock is > 1/4th the target FCPU value int bitclockvalue = Integer.parseInt(bitclock); int targetfcpu = config.getFCPU(); if (bitclockvalue > targetfcpu / 4) { String msg = MessageFormat .format( "selected BitClock Frequency of {0} is greater than 1/4th of the target MCU Clock ({1})", convertFrequencyToString(bitclockvalue), convertFrequencyToString(targetfcpu)); return new ValidationResult(Result.WARNING, msg); } } } // JTAG_CLOCk is valid return ValidationResult.OK_RESULT; } /** * Check the JTAG daisy chain 'bits before' attribute. * <p> * This method will return {@link Result#ERROR} iff * <ul> * <li>The current target interface is JTAG</li> * <li>and daisy chaining is enabled</li> * <li>and 'bits before' > 255</li> * </ul> * In all other cases {@link Result#OK} is returned. * </p> * <p> * Note: The current implementation of AVRDude only accepts instruction bit values < 32. But * this does not seem to be correct because the JTAG protocol accepts an 8-bit field.<br/> * AVaRICE accepts all values but uses only the lower 8 bits. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkJTAGDaisyChainBitsBefore(ITargetConfiguration config) { if (isDaisyChainEnabled(config)) { int bitsbefore = config.getIntegerAttribute(ATTR_DAISYCHAIN_BB); if (bitsbefore > 255) { String msg = "Daisy chain 'bits before' out of range (0 - 255)"; return new ValidationResult(Result.ERROR, msg); } } return ValidationResult.OK_RESULT; } /** * Check the JTAG daisy chain 'bits after' attribute. * <p> * This method will return {@link Result#ERROR} iff * <ul> * <li>The current target interface is JTAG</li> * <li>and daisy chaining is enabled</li> * <li>and 'bits after' > 255</li> * </ul> * In all other cases {@link Result#OK} is returned. * </p> * <p> * Note: The current implementation of AVRDude only accepts instruction bit values < 32. But * this does not seem to be correct because the JTAG protocol accepts an 8-bit field.<br/> * AVaRICE accepts all values but uses only the lower 8 bits. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkJTAGDaisyChainBitsAfter(ITargetConfiguration config) { if (isDaisyChainEnabled(config)) { int bitsafter = config.getIntegerAttribute(ATTR_DAISYCHAIN_BA); if (bitsafter > 255) { String msg = "Daisy chain 'bits after' out of range (0 - 255)"; return new ValidationResult(Result.ERROR, msg); } } return ValidationResult.OK_RESULT; } /** * Check the JTAG daisy chain 'units before' attribute. * <p> * This method will return {@link Result#ERROR} iff * <ul> * <li>The current target interface is JTAG</li> * <li>and daisy chaining is enabled</li> * <li>and 'units before' > 'bits before'</li> * </ul> * In all other cases {@link Result#OK} is returned. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkJTAGDaisyChainUnitsBefore(ITargetConfiguration config) { if (isDaisyChainEnabled(config)) { int unitsbefore = config.getIntegerAttribute(ATTR_DAISYCHAIN_UB); int bitsbefore = config.getIntegerAttribute(ATTR_DAISYCHAIN_BB); if (unitsbefore > bitsbefore) { String msg = "Daisy chain 'Devices before' greater than 'bits before'"; return new ValidationResult(Result.ERROR, msg); } } return ValidationResult.OK_RESULT; } /** * Check the JTAG daisy chain 'units after' attribute. * <p> * This method will return {@link Result#ERROR} iff * <ul> * <li>The current target interface is JTAG</li> * <li>and daisy chaining is enabled</li> * <li>and 'units after' > 'bits after'</li> * </ul> * In all other cases {@link Result#OK} is returned. * </p> * * @param config * The target configuration to check. * @return {@link ValidationResult} with the result code and a human readable description. */ public static ValidationResult checkJTAGDaisyChainUnitsAfter(ITargetConfiguration config) { if (isDaisyChainEnabled(config)) { int unitsafter = config.getIntegerAttribute(ATTR_DAISYCHAIN_UA); int bitsafter = config.getIntegerAttribute(ATTR_DAISYCHAIN_BA); if (unitsafter > bitsafter) { String msg = "Daisy chain 'Devices after' greater than 'bits after'"; return new ValidationResult(Result.ERROR, msg); } } return ValidationResult.OK_RESULT; } /** * Checks if daisy chain is possible and enabled. * <p> * The implementation checks if the selected programmer is capable of daisy chaining and if the * ATTR_DAISYCHAIN_ENABLED flag is set. * </p> * * @see #isDaisyChainCapable(ITargetConfiguration) * * @param config * The target configuration to check. * @return <code>true</code> if the programmer supports daisy chaining and it is enabled. */ private static boolean isDaisyChainEnabled(ITargetConfiguration config) { String programmerid = config.getAttribute(ATTR_PROGRAMMER_ID); IProgrammer programmer = config.getProgrammer(programmerid); if (programmer.isDaisyChainCapable()) { return config.getBooleanAttribute(ATTR_DAISYCHAIN_ENABLE); } return false; } /** * Convert a integer Hz value to a String. * <p> * The result has the unit appended: * <ul> * <li><code>Hz</code> for values below 1KHZ</li> * <li><code>KHz</code> for values between 1 and 1000 KHz</li> * <li><code>MHz</code> for values above 1000 KHz</li> * </ul> * As a special case the value <code>0</code> will result in "default". * </p> * * @param value * integer Hz value * @return */ private static String convertFrequencyToString(int value) { String text; if (value == 0) { text = "default"; } else if (value < 1000) { text = value + " Hz"; } else if (value < 1000000) { float newvalue = value / 1000.0F; text = newvalue + " KHz"; } else { float newvalue = value / 1000000.0F; text = newvalue + " MHz"; } return text; } }