/******************************************************************************* * 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.toolinfo.fuses; import java.util.ArrayList; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import de.innot.avreclipse.core.toolinfo.partdescriptionfiles.FusesReader; /** * Describes a BitField which is part of a Fuse byte or a LockBits byte. * <p> * This class represents all properties of a single BitField. They are: * <ul> * <li><code>Name</code>: the name of the BitField, e.g. <code>SPIEN</code>.</li> * <li><code>Description</code>: a more user friendly long description of the bitfield.</li> * <li><code>Mask</code>: an integer value that determines which bits are covered bitfield.</li> * <li><code>Index</code>: the index of the parent byte within its {@link ByteValues} structure. * This is not strictly a BitField property, but it is useful to work with the BitFieldDescription * object</li> * </ul> * Each of these properties has an associated getter method. * </p> * <p> * The interface has also some convenient methods to work with the BitFields. It also knows how to * serialize itself to an XML DOM and can be instantiated from the DOM. * </p> * <p> * This class has two constructors. One to pass the value and description directly and another one * to read the value and description from an XML document node. The first one is used by the * {@link FusesReader} while parsing the <em>part description file</em> while the second one is * used when a {@link ByteDescription} is constructed from an XML file.<br> * </p> * * </p> * * @author Thomas Holland * @since 2.3 * */ public class BitFieldDescription { /** XML element tag name name for a <code>BitFieldValueDescription</code> object. */ public final static String TAG_BITFIELD = "bitfield"; /** XML attribute name for the BitField name */ public final static String ATTR_NAME = "name"; /** XML attribute name for the BitField description */ public final static String ATTR_DESC = "desc"; /** XML attribute name for the BitField mask */ public final static String ATTR_MASK = "mask"; /** XML attribute name for the default value */ public final static String ATTR_DEFAULT = "default"; /** The byte index of the fuse/lock byte this description belongs to */ private final int fIndex; /** The name of this BitField */ private final String fName; /** Description of this BitField */ private final String fDescription; /** The mask for this BitField */ private final int fMask; /** The default value for this BitField. <code>-1</code> if no default available. */ private final int fDefault; /** * A enumeration of all possible values. This is optional and may be empty if no values are * defined in the part description files. */ private final List<BitFieldValueDescription> fValues; /** * Construct a new <code>BitFieldDescription</code> object from the given parameters. * <p> * This constructor is called from {@link FusesReader}. * </p> * * @param index * the fuse byte / locks byte offset * @param name * the name of the BitField * @param description * the description of the BitField * @param mask * the mask defining which bits of the byte are represented by this bitfield. * @param defaultvalue * the default value for the byte containing this BitField. <code>-1</code> means * no default available. The value is converted to a normalized BitField value. * @param values * enumeration of all possible values (can be <code>null</code> if the bitfield has * no predefined values. */ public BitFieldDescription(int index, String name, String description, int mask, int defaultvalue, List<BitFieldValueDescription> values) { fIndex = index; fName = name; fDescription = description; fMask = mask; fValues = values; // convert the default value fDefault = defaultvalue != -1 ? byteToBitField(defaultvalue) : -1; } /** * Construct a new BitFieldDescription from a XML <bitfield> node. * <p> * This constructor will take the node and parse the values from the "name", "desc", "mask" and * "default" attributes. The index is taken from the "index" attribute of the parent node. If * any attribute is missing a default value is used, except for "mask", which is required and * will cause an IllegalArgumentException if missing. * </p> * <p> * Then the list of {@link BitFieldValueDescription}s is filled from all <value> * elements. * </p> * * @param bitfieldnode * A <bitfield> document node. */ protected BitFieldDescription(Node bitfieldnode) { // First get our own attributes NamedNodeMap attrs = bitfieldnode.getAttributes(); Node namenode = attrs.getNamedItem(ATTR_NAME); if (namenode != null) { fName = namenode.getNodeValue(); } else { fName = "???"; } Node descnode = attrs.getNamedItem(ATTR_DESC); if (descnode != null) { fDescription = descnode.getNodeValue(); } else { fDescription = "no description available"; } Node masknode = attrs.getNamedItem(ATTR_MASK); if (masknode == null) { throw new IllegalArgumentException("Required attribute \"" + ATTR_MASK + "\" for element <" + bitfieldnode.getNodeName() + "> missing."); } fMask = Integer.decode(masknode.getTextContent()); Node defaultnode = attrs.getNamedItem(ATTR_DEFAULT); if (defaultnode == null) { fDefault = -1; } else { fDefault = Integer.decode(defaultnode.getTextContent()); } // Then collect the BitFieldValueDescription child nodes fValues = new ArrayList<BitFieldValueDescription>(); NodeList bfvnodes = bitfieldnode.getChildNodes(); for (int i = 0; i < bfvnodes.getLength(); i++) { Node child = bfvnodes.item(i); if (BitFieldValueDescription.TAG_VALUE.equalsIgnoreCase(child.getNodeName())) { BitFieldValueDescription value = new BitFieldValueDescription(child); fValues.add(value); } } // and finally get the index from the parent Node parent = bitfieldnode.getParentNode(); NamedNodeMap parentattrs = parent.getAttributes(); // The next line should not fail, because if the attribute was missing ByteDescription would // have thrown an Exception already. Node indexnode = parentattrs.getNamedItem(ByteDescription.ATTR_BYTE_INDEX); fIndex = Integer.decode(indexnode.getTextContent()); } /** * Get the byte index of the parent fuse or lockbits byte. * * @return <code>int</code> from <code>0</code> up to <code>5</code> for fuse bytes or * <code>0</code> for a lockbits. */ public int getIndex() { return fIndex; } /** * Get the name of the BitField. * <p> * This is the name from the part description file, e.g. <em>"SPIEN"</em> or * <em>"SUT_CKSEL"</em>". * </p> * * @return */ public String getName() { return fName; } /** * Get the long, human readable, description for the BitField. * <p> * This is the description from the part description file. * </p> * * @return */ public String getDescription() { return fDescription; } /** * Get the bitmask for the BitField. * <p> * The value is taken from the part description file. It has ones for all bits that make up the * BitField, and zeros for all bits outside of the BitField. * </p> * * @return byte value between <code>0x00</code> and <code>0xFF</code>. */ public int getMask() { return fMask; } /** * Return the maximum value acceptable for this BitField. * <p> * This is 2^^(number of 1-bits in the mask) minus 1. * </p> * * @return max value */ public int getMaxValue() { return (2 << (Integer.bitCount(fMask) - 1)) - 1; } /** * Returns the default value of this BitField as defined in the part description file. * <p> * Especially newer MCUs do not have default values in the part description files. In this case * <code>-1</code> is returned. The same is true for LockBits, because they are never defined * in the part description files. * </p> * * @return The normalized default value for this BitField (range 0 to {@link #getMaxValue()}, * or <code>-1</code> */ public int getDefaultValue() { return fDefault; } /** * Convert a normalize value to a Byte value. * <p> * This method will left-shift the given value for the required number of places to match the * mask. * </p> * * @param value * the normalized value (range 0 to {@link #getMaxValue()}) * @return <code>int</code> with the shifted value. */ public int bitFieldToByte(int value) { // left-shift the value to the right place (as determined by the mask) return value << Integer.numberOfTrailingZeros(fMask); } /** * Convert a Byte value to a normalized BitField value. * <p> * This method will mask off all bits outside this BitField and then right-shift the result so * that a normalized value (range 0 to {@link #getMaxValue()}) is returned. * </p> * * @param bitfieldvalue * a byte from which to extract and normalize the value of this bitfield. * @return <code>int</code> with the normalized value. */ public int byteToBitField(int bitfieldvalue) { // Mask the appropriate bits and rightshift (normalize) the result int masked = bitfieldvalue & fMask; return masked >> Integer.numberOfTrailingZeros(fMask); } /** * Get a list of all values for this BitField as defined in the part description file. * <p> * The returned list is a copy and any changes to the list will not affect this BitField object. * </p> * * @return List of <code>IBitFieldValueDescription</code> objects, or <code>null</code> if * no values are defined. */ public List<BitFieldValueDescription> getValuesEnumeration() { if (fValues != null) { return new ArrayList<BitFieldValueDescription>(fValues); } return null; } /** * Get the descriptive text for the given BitField value. * <p> * Depending on the type of the BitField different return values are possible: * <ul> * <li> * <p> * The BitField has a some BitFieldValue description objects * <ul> * <li>The value matches one of them: return the value description.</li> * <li>The value matches none of them: return the string "undefined"</li> * </ul> * </p> * </li> * <li> * <p> * The BitField has no BitFieldValue description objects * <ul> * <li>The BitField is a single bit: Return "Yes" for <code>0</code> and <code>"No"</code> * for <code>1</code>. This matches the fuse byte logic used by ATMEL.</li> * <li>The BitField covers multiple bits: return the value as a hex string</li> * </ul> * </p> * </li> * </p> * * @param value * Normalized BitField value (range 0 up to {@link #getMaxValue()}; * @return */ public String getValueText(int value) { for (BitFieldValueDescription desc : fValues) { if (desc.getValue() == value) { return desc.getDescription(); } } if (fValues.size() > 0) { // The value was not in the list. Return an error string return "undefined (" + ByteDescription.toHex(value) + ")"; } // There are no BitFieldValueDescriptions available for this BitField // If if is a single-bit field return "yes"/"no" (fuse-byte logic where 0 means yes), // If it is a multi-bit field just return the value as hex string. if (Integer.bitCount(fMask) == 1) { return value == 0 ? "Yes" : "No"; } return ByteDescription.toHex(value); } /** * Convert to XML. * <p> * Create a new child <bitfield> node in the given parent node with the attributes "name", * "desc" and "mask". All available {@link BitFieldValueDescription} objects are appended as * childs of the the <bitfield> node.<br> * The index is not stored as it is defined by the parent node. * </p> * * @param parentnode * <bitfield> document node. */ protected void toXML(Node parentnode) { Document document = parentnode.getOwnerDocument(); Element element = document.createElement(TAG_BITFIELD); element.setAttribute(ATTR_NAME, fName); element.setAttribute(ATTR_DESC, fDescription); element.setAttribute(ATTR_MASK, ByteDescription.toHex(fMask)); if (fDefault != -1) { element.setAttribute(ATTR_DEFAULT, ByteDescription.toHex(fDefault)); } if (fValues != null && fValues.size() > 0) { for (BitFieldValueDescription bfv : fValues) { bfv.toXML(element); } } parentnode.appendChild(element); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return fName + ": mask=0x" + Integer.toHexString(fMask) + " desc=" + fDescription; } }