diff --git a/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/BoardModel.java b/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/BoardModel.java
index f0890eef4..4475dfbfd 100644
--- a/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/BoardModel.java
+++ b/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/BoardModel.java
@@ -444,14 +444,99 @@ public enum BoardModel {
this.usesRP1 = usesRP1;
}
+ /**
+ * Returns the {@link BoardModel} corresponding to the given {@link RevisionCode} by matching on the board type and,
+ * where necessary, the PCB revision or memory size.
+ *
+ * @param revisionCode the parsed revision code to identify
+ * @return the matching {@code BoardModel}, or {@code UNKNOWN} if the type is not recognized
+ */
+ public static BoardModel getByRevisionCode(RevisionCode revisionCode) {
+ switch (revisionCode.type()) {
+ case RPI_A:
+ return BoardModel.MODEL_1_A;
+ case RPI_B:
+ case RPI_ALPHA:
+ return BoardModel.MODEL_1_B;
+ case RPI_A_PLUS:
+ return BoardModel.MODEL_1_A_PLUS;
+ case RPI_B_PLUS:
+ return BoardModel.MODEL_1_B_PLUS;
+ case RPI_2B:
+ if (revisionCode.revision() < 2) {
+ return BoardModel.MODEL_2_B;
+ } else {
+ return BoardModel.MODEL_2_B_V1_2;
+ }
+ case RPI_CM1:
+ return BoardModel.COMPUTE_1;
+ case RPI_3B:
+ return BoardModel.MODEL_3_B;
+ case RPI_ZERO:
+ if (revisionCode.revision() < 3) {
+ return BoardModel.ZERO_PCB_1_2;
+ } else {
+ return BoardModel.ZERO_PCB_1_3;
+ }
+ case RPI_CM3:
+ return BoardModel.COMPUTE_3;
+ case RPI_ZERO_W:
+ return BoardModel.ZERO_W;
+ case RPI_3B_PLUS:
+ return BoardModel.MODEL_3_B_PLUS;
+ case RPI_3A_PLUS:
+ return BoardModel.MODEL_3_A_PLUS;
+ case RPI_CM3_PLUS:
+ return BoardModel.COMPUTE_3_PLUS;
+ case RPI_4B:
+ return BoardModel.MODEL_4_B;
+ case RPI_ZERO_2_W:
+ case RPI_CM0:
+ return BoardModel.ZERO_V2;
+ case RPI_400:
+ return BoardModel.MODEL_400;
+ case RPI_CM4:
+ return BoardModel.COMPUTE_4;
+ case RPI_CM4S:
+ return BoardModel.COMPUTE_4_SODIMM;
+ case RPI_5:
+ return BoardModel.MODEL_5_B;
+ case RPI_CM5:
+ case RPI_CM5_LITE:
+ return BoardModel.COMPUTE_5;
+ case RPI_500_500_PLUS:
+ if (RevisionCode.MemorySize.GB_16.equals(revisionCode.memorySize())) {
+ return BoardModel.MODEL_500_PLUS;
+ } else {
+ return BoardModel.MODEL_500;
+ }
+ default:
+ return UNKNOWN;
+ }
+ }
+
/**
* Retrieves the board model corresponding to the given board code.
*
+ *
Where possible, identification is performed by decoding the structured fields of the revision code per the
+ * Raspberry Pi revision code specification. If the revision code cannot be parsed, falls back to direct string
+ * matching against known board codes.
+ *
* @param boardCode the board code to look up
* @return the matching {@code BoardModel} or {@code UNKNOWN} if no match is found
* @throws Exception if multiple matches are found
*/
public static BoardModel getByBoardCode(String boardCode) throws Exception {
+ try {
+ RevisionCode revisionCode = RevisionCode.of(boardCode);
+ BoardModel result = getByRevisionCode(revisionCode);
+ if (result != UNKNOWN) {
+ return result;
+ }
+ } catch (IllegalArgumentException e) {
+ // Not a valid revision code, fall through to legacy lookup.
+ }
+
var matches = Arrays.stream(BoardModel.values())
.filter(bm -> bm.boardCodes.contains(boardCode))
.collect(Collectors.toList());
diff --git a/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/RevisionCode.java b/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/RevisionCode.java
new file mode 100644
index 000000000..94e93de2b
--- /dev/null
+++ b/pi4j-core/src/main/java/com/pi4j/boardinfo/definition/RevisionCode.java
@@ -0,0 +1,483 @@
+package com.pi4j.boardinfo.definition;
+
+/*-
+ * #%L
+ * **********************************************************************
+ * ORGANIZATION : Pi4J
+ * PROJECT : Pi4J :: LIBRARY :: Java Library (CORE)
+ * FILENAME : RevisionCode.java
+ *
+ * This file is part of the Pi4J project. More information about
+ * this project can be found here: https://pi4j.com/
+ * **********************************************************************
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * Represents a parsed Raspberry Pi 32-bit revision code in the format:
+ * NOQuuuWuFMMMCCCCPPPPTTTTTTTTRRRR
+ * where:
+ *
+ * - N - Overvoltage allowed
+ * - O - OTP program allowed
+ * - Q - OTP read allowed
+ * - u - Unused
+ * - W - Warranty voided
+ * - F - New-style revision code flag
+ * - MMM - Memory size
+ * - CCCC - Manufacturer
+ * - PPPP - Processor
+ * - TTTTTTTT - Type
+ * - RRRR - Revision
+ *
+ *
+ * Both old-style and new-style revision codes are supported. New-style codes are identified
+ * by bit 23 being set. Old-style codes are matched against known revision code tables.
+ *
+ * Instances are obtained via the factory methods {@link #of(int)} and {@link #of(String)}:
+ *
+ * RevisionCode code = RevisionCode.of("a03111");
+ * RevisionCode code = RevisionCode.of(0xa03111);
+ *
+ *
+ * @see
+ * Raspberry Pi New-Style Revision Codes
+ */
+public final class RevisionCode {
+
+ private final MemorySize memorySize;
+ private final Processor processor;
+ private final Type type;
+ private final int revision;
+
+ /**
+ * Private constructor. Use {@link #of(int)} or {@link #of(String)} to obtain instances.
+ *
+ * @param memorySize the memory size decoded from the revision code
+ * @param processor the processor decoded from the revision code
+ * @param type the board type decoded from the revision code
+ * @param revision the PCB revision number decoded from the revision code
+ */
+ private RevisionCode(MemorySize memorySize, Processor processor, Type type, int revision) {
+ this.memorySize = memorySize;
+ this.processor = processor;
+ this.type = type;
+ this.revision = revision;
+ }
+
+ /**
+ * Returns the memory size of the board.
+ *
+ * @return the {@link MemorySize} decoded from the revision code
+ */
+ public MemorySize memorySize() {
+ return memorySize;
+ }
+
+ /**
+ * Returns the processor of the board.
+ *
+ * @return the {@link Processor} decoded from the revision code
+ */
+ public Processor processor() {
+ return processor;
+ }
+
+ /**
+ * Returns the board type.
+ *
+ * @return the {@link Type} decoded from the revision code
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the PCB revision number.
+ * For old-style revision codes, this always returns {@code 1} since old-style boards
+ * do not have revision-specific {@code BoardModel} instances.
+ *
+ * @return the PCB revision number
+ */
+ public int revision() {
+ return revision;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RevisionCode that)) return false;
+ return memorySize == that.memorySize
+ && processor == that.processor
+ && type == that.type
+ && revision == that.revision;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ memorySize,
+ processor,
+ type,
+ revision);
+ }
+
+ @Override
+ public String toString() {
+ return "RevisionCode{" +
+ "memorySize=" + memorySize +
+ ", processor=" + processor +
+ ", type=" + type +
+ ", revision=" + revision +
+ '}';
+ }
+
+ /**
+ * Creates a {@code RevisionCode} by parsing a 32-bit integer revision code.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return a {@code RevisionCode} instance representing the parsed fields
+ */
+ public static RevisionCode of(int revisionCode) {
+ return new RevisionCode(
+ MemorySize.fromRevisionCode(revisionCode),
+ Processor.fromRevisionCode(revisionCode),
+ Type.fromRevisionCode(revisionCode),
+ extractRevision(revisionCode)
+ );
+ }
+
+ /**
+ * Creates a {@code RevisionCode} by parsing a hexadecimal string revision code.
+ * The string may optionally include a {@code 0x} or {@code 0X} prefix and surrounding
+ * whitespace, both of which are stripped before parsing.
+ *
+ * @param revisionCode the revision code as a hex string (e.g. {@code "a03111"} or {@code "0xa03111"})
+ * @return a {@code RevisionCode} instance representing the parsed fields
+ * @throws IllegalArgumentException if the string is {@code null}, blank, or not valid hexadecimal
+ */
+ public static RevisionCode of(String revisionCode) {
+ if (revisionCode == null || revisionCode.isBlank()) {
+ throw new IllegalArgumentException(("Revision code cannot be null or empty."));
+ }
+ String clean = revisionCode.trim();
+ if (clean.startsWith("0X") || clean.startsWith("0x")) {
+ clean = clean.substring(2);
+ }
+
+ if (clean.isEmpty() || clean.length() > 8 || !clean.matches("[0-9a-fA-F]+")) {
+ throw new IllegalArgumentException("Revision code is not valid 32-bit hex value: " + revisionCode);
+ }
+ return of((int) Long.parseLong(clean, 16));
+ }
+
+ /**
+ * Returns {@code true} if the given revision code uses the new-style format,
+ * identified by bit 23 being set.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return {@code true} if new-style, {@code false} if old-style
+ */
+ private static boolean isNewStyleRevisionCode(int revisionCode) {
+ return (revisionCode & 0x800000) != 0;
+ }
+
+ /**
+ * Extracts the PCB revision number (bits 3:0) from a new-style revision code.
+ * For old-style revision codes, returns {@code 1} since old-style boards do not
+ * have revision-specific {@code BoardModel} instances.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return the PCB revision number, or {@code 1} for old-style codes
+ */
+ private static int extractRevision(int revisionCode) {
+ return isNewStyleRevisionCode(revisionCode) ? revisionCode & 0xF : 1;
+ }
+
+ /**
+ * A common interface for the internal enumerations of {@code RevisionCode}.
+ *
+ * Provides access to the numeric field value used in new-style revision codes
+ * and the list of old-style revision codes associated with each constant, allowing
+ * shared lookup logic to operate generically across {@link RevisionCode.MemorySize},
+ * {@link RevisionCode.Processor}, and {@link RevisionCode.Type}.
+ */
+ private interface RevisionCodeEnum {
+ /**
+ * Returns the numeric value of this constant as encoded in the
+ * new-style revision code bit field.
+ *
+ * @return the bit field value
+ */
+ int getValue();
+
+ /**
+ * Returns the old-style revision codes associated with this constant,
+ * or an empty array if this constant has no old-style codes.
+ *
+ * @return the old-style revision codes
+ */
+ int[] getOldStyleRevisionCodes();
+ }
+
+ /**
+ * Represents the memory size field (bits 22:20) of a new-style Raspberry Pi revision code.
+ *
+ * Old-style revision codes are matched against known values. New-style codes are
+ * identified by the 3-bit MMM field. If the value is unrecognized, {@link #UNKNOWN} is returned.
+ *
+ * @see
+ * Raspberry Pi New-Style Revision Codes
+ */
+ public enum MemorySize implements RevisionCodeEnum {
+ MB_256(0, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0012),
+ MB_512(1, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0013, 0x0014),
+ GB_1(2),
+ GB_2(3),
+ GB_4(4),
+ GB_8(5),
+ GB_16(6),
+ OTHER(7, 0x0015),
+ UNKNOWN(8);
+
+ /** A list of old-style revision codes for the enum instance. */
+ private final int[] oldStyleRevisionCodes;
+
+ /** The value of the enum instance. */
+ private final int value;
+
+ MemorySize(int value) {
+ this(value, new int[0]);
+ }
+
+ MemorySize(int value, int ... oldStyleRevisionCodes) {
+ this.oldStyleRevisionCodes = oldStyleRevisionCodes;
+ this.value = value;
+ }
+
+ /**
+ * Returns the {@code MemorySize} corresponding to the given revision code.
+ * Returns {@link #UNKNOWN} if no match is found.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return the matching {@code MemorySize}, or {@link #UNKNOWN}
+ */
+ static MemorySize fromRevisionCode(int revisionCode) {
+ return isNewStyleRevisionCode(revisionCode) ?
+ fromNewStyleCode(revisionCode, 20, 0x7, MemorySize::values, MemorySize.UNKNOWN) :
+ fromOldStyleCode(revisionCode, MemorySize::values, MemorySize.UNKNOWN);
+ }
+
+ @Override
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public int[] getOldStyleRevisionCodes() {
+ return Arrays.copyOf(oldStyleRevisionCodes, oldStyleRevisionCodes.length);
+ }
+ }
+
+ /**
+ * Represents the processor field (bits 15:12) of a new-style Raspberry Pi revision code.
+ *
+ * All boards using old-style revision codes are assumed to use the {@link #BCM2835}.
+ * If the processor value in a new-style code is unrecognized, {@link #UNKNOWN} is returned.
+ *
+ * @see
+ * Raspberry Pi New-Style Revision Codes
+ */
+ public enum Processor implements RevisionCodeEnum {
+ BCM2835(0, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015),
+ BCM2836(1),
+ BCM2837(2),
+ BCM2711(3),
+ BCM2712(4),
+ UNKNOWN(16);
+
+ /** A list of old-style revision codes for the enum instance. */
+ private final int[] oldStyleRevisionCodes;
+
+ /** The value of the enum instance. */
+ private final int value;
+
+ Processor(int value) {
+ this(value, new int[0]);
+ }
+
+ Processor(int value, int ... oldStyleRevisionCodes) {
+ this.oldStyleRevisionCodes = oldStyleRevisionCodes;
+ this.value = value;
+ }
+
+ /**
+ * Returns the {@code Processor} corresponding to the given revision code.
+ * Old-style revision codes always return {@link #BCM2835}.
+ * Returns {@link #UNKNOWN} if no match is found in a new-style code.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return the matching {@code Processor}, or {@link #UNKNOWN}
+ */
+ static Processor fromRevisionCode(int revisionCode) {
+ return isNewStyleRevisionCode(revisionCode) ?
+ fromNewStyleCode(revisionCode, 12, 0xF, Processor::values, Processor.UNKNOWN) :
+ fromOldStyleCode(revisionCode, Processor::values, Processor.UNKNOWN);
+ }
+
+ @Override
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public int[] getOldStyleRevisionCodes() {
+ return Arrays.copyOf(oldStyleRevisionCodes, oldStyleRevisionCodes.length);
+ }
+ }
+
+ /**
+ * Represents the board type field (bits 11:4) of a new-style Raspberry Pi revision code.
+ *
+ * Old-style revision codes are matched against known values per type. New-style codes
+ * are identified by the 8-bit TTTTTTTT field. If the value is unrecognized,
+ * {@link #UNKNOWN} is returned.
+ *
+ * @see
+ * Raspberry Pi New-Style Revision Codes
+ */
+ public enum Type implements RevisionCodeEnum {
+ RPI_A(0, 0x0007, 0x0008, 0x0009),
+ RPI_B(1, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x000d, 0x000e, 0x000f),
+ RPI_A_PLUS(2, 0x0012, 0x0015),
+ RPI_B_PLUS(3, 0x0010, 0x0013),
+ RPI_2B(4),
+ RPI_ALPHA(5),
+ RPI_CM1(6, 0x0011, 0x0014),
+ RPI_3B(8),
+ RPI_ZERO(9),
+ RPI_CM3(10),
+ RPI_ZERO_W(12),
+ RPI_3B_PLUS(13),
+ RPI_3A_PLUS(14),
+ RPI_CM3_PLUS(16),
+ RPI_4B(17),
+ RPI_ZERO_2_W(18),
+ RPI_400(19),
+ RPI_CM4(20),
+ RPI_CM4S(21),
+ RPI_5(23),
+ RPI_CM5(24),
+ RPI_500_500_PLUS(25),
+ RPI_CM5_LITE(26),
+ RPI_CM0(27),
+ UNKNOWN(256);
+
+ /** A list of old-style revision codes for the enum instance. */
+ private final int[] oldStyleRevisionCodes;
+
+ /** The value of the enum instance. */
+ private final int value;
+
+ Type(int value) {
+ this(value, new int[0]);
+ }
+
+ Type(int value, int ... oldStyleRevisionCodes) {
+ this.oldStyleRevisionCodes = oldStyleRevisionCodes;
+ this.value = value;
+ }
+
+ /**
+ * Returns the {@code Type} corresponding to the given revision code.
+ * Returns {@link #UNKNOWN} if no match is found.
+ *
+ * @param revisionCode the raw 32-bit revision code
+ * @return the matching {@code Type}, or {@link #UNKNOWN}
+ */
+ static Type fromRevisionCode(int revisionCode) {
+ return isNewStyleRevisionCode(revisionCode) ?
+ fromNewStyleCode(revisionCode, 4, 0xFF, Type::values, Type.UNKNOWN) :
+ fromOldStyleCode(revisionCode, Type::values, Type.UNKNOWN);
+ }
+
+ @Override
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public int[] getOldStyleRevisionCodes() {
+ return Arrays.copyOf(oldStyleRevisionCodes, oldStyleRevisionCodes.length);
+ }
+ }
+
+ /**
+ * Returns the enum constant of type {@code T} whose bit field value matches the
+ * extracted field from the given new-style revision code.
+ *
+ * The field value is extracted by shifting the revision code right by {@code shift}
+ * bits and masking with {@code mask}. The result is compared against
+ * {@link RevisionCodeEnum#getValue()} for each constant.
+ *
+ * @param the enum type, which must implement {@link RevisionCodeEnum}
+ * @param revisionCode the raw 32-bit new-style revision code
+ * @param shift the number of bits to right-shift before applying the mask
+ * @param mask the bitmask to apply after shifting
+ * @param values a supplier of the enum constants to search (e.g. {@code MemorySize::values})
+ * @param unknown the sentinel value to return if no match is found
+ * @return the matching enum constant, or {@code unknown} if no match is found
+ */
+ private static T fromNewStyleCode(
+ int revisionCode, int shift, int mask,
+ Supplier values, T unknown) {
+ final int value = (revisionCode >> shift) & mask;
+ for (var s : values.get()) {
+ if (s.getValue() == value) {
+ return s;
+ }
+ }
+ return unknown;
+ }
+
+ /**
+ * Returns the enum constant of type {@code T} whose old-style revision code list
+ * contains the given revision code.
+ *
+ * Each constant is checked in declaration order. The first constant whose
+ * {@link RevisionCodeEnum#getOldStyleRevisionCodes()} array contains the given
+ * revision code is returned.
+ *
+ * @param the enum type, which must implement {@link RevisionCodeEnum}
+ * @param revisionCode the raw 32-bit old-style revision code
+ * @param values a supplier of the enum constants to search (e.g. {@code Type::values})
+ * @param unknown the sentinel value to return if no match is found
+ * @return the matching enum constant, or {@code unknown} if no match is found
+ */
+ private static T fromOldStyleCode(
+ int revisionCode, Supplier values, T unknown) {
+ for (var s : values.get()) {
+ for (var r : s.getOldStyleRevisionCodes()) {
+ if (r == revisionCode) {
+ return s;
+ }
+ }
+ }
+ return unknown;
+ }
+}
diff --git a/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/BoardModelTest.java b/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/BoardModelTest.java
index 898a2a505..c34644769 100644
--- a/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/BoardModelTest.java
+++ b/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/BoardModelTest.java
@@ -9,6 +9,94 @@
class BoardModelTest {
+ @Test
+ void getBoardModelByRevisionCode() {
+ assertAll(
+ () -> assertEquals(BoardModel.MODEL_1_A_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("900021"))),
+ () -> assertEquals(BoardModel.MODEL_1_B_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("900032"))),
+ () -> assertEquals(BoardModel.MODEL_2_B, BoardModel.getByRevisionCode(RevisionCode.of("a01040"))),
+ () -> assertEquals(BoardModel.MODEL_2_B, BoardModel.getByRevisionCode(RevisionCode.of("a01041"))),
+ () -> assertEquals(BoardModel.MODEL_2_B, BoardModel.getByRevisionCode(RevisionCode.of("a21041"))),
+ () -> assertEquals(BoardModel.MODEL_2_B_V1_2, BoardModel.getByRevisionCode(RevisionCode.of("a02042"))),
+ () -> assertEquals(BoardModel.MODEL_2_B_V1_2, BoardModel.getByRevisionCode(RevisionCode.of("a22042"))),
+ () -> assertEquals(BoardModel.COMPUTE_1, BoardModel.getByRevisionCode(RevisionCode.of("900061"))),
+ () -> assertEquals(BoardModel.MODEL_3_B, BoardModel.getByRevisionCode(RevisionCode.of("a02082"))),
+ () -> assertEquals(BoardModel.MODEL_3_B, BoardModel.getByRevisionCode(RevisionCode.of("a22082"))),
+ () -> assertEquals(BoardModel.MODEL_3_B, BoardModel.getByRevisionCode(RevisionCode.of("a32082"))),
+ () -> assertEquals(BoardModel.MODEL_3_B, BoardModel.getByRevisionCode(RevisionCode.of("a52082"))),
+ () -> assertEquals(BoardModel.MODEL_3_B, BoardModel.getByRevisionCode(RevisionCode.of("a22083"))),
+ () -> assertEquals(BoardModel.ZERO_PCB_1_2, BoardModel.getByRevisionCode(RevisionCode.of("900092"))),
+ () -> assertEquals(BoardModel.ZERO_PCB_1_2, BoardModel.getByRevisionCode(RevisionCode.of("920092"))),
+ () -> assertEquals(BoardModel.ZERO_PCB_1_3, BoardModel.getByRevisionCode(RevisionCode.of("900093"))),
+ () -> assertEquals(BoardModel.ZERO_PCB_1_3, BoardModel.getByRevisionCode(RevisionCode.of("920093"))),
+ () -> assertEquals(BoardModel.COMPUTE_3, BoardModel.getByRevisionCode(RevisionCode.of("a020a0"))),
+ () -> assertEquals(BoardModel.COMPUTE_3, BoardModel.getByRevisionCode(RevisionCode.of("a220a0"))),
+ () -> assertEquals(BoardModel.ZERO_W, BoardModel.getByRevisionCode(RevisionCode.of("9000c1"))),
+ () -> assertEquals(BoardModel.MODEL_3_B_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("a020d3"))),
+ () -> assertEquals(BoardModel.MODEL_3_B_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("a020d4"))),
+ () -> assertEquals(BoardModel.MODEL_3_A_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("9020e0"))),
+ () -> assertEquals(BoardModel.MODEL_3_A_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("9020e1"))),
+ () -> assertEquals(BoardModel.COMPUTE_3_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("a02100"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("a03111"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("b03111"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("c03111"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("b03112"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("c03112"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("b03114"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("c03114"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("d03114"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("b03115"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("c03115"))),
+ () -> assertEquals(BoardModel.MODEL_4_B, BoardModel.getByRevisionCode(RevisionCode.of("d03115"))),
+ () -> assertEquals(BoardModel.ZERO_V2, BoardModel.getByRevisionCode(RevisionCode.of("902120"))),
+ () -> assertEquals(BoardModel.MODEL_400, BoardModel.getByRevisionCode(RevisionCode.of("c03130"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("a03140"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("b03140"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("c03140"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("d03140"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("a03141"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("b03141"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("c03141"))),
+ () -> assertEquals(BoardModel.COMPUTE_4, BoardModel.getByRevisionCode(RevisionCode.of("d03141"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("b04170"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("c04170"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("d04170"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("a04171"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("b04171"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("c04171"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("d04171"))),
+ () -> assertEquals(BoardModel.MODEL_5_B, BoardModel.getByRevisionCode(RevisionCode.of("e04171"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("b04180"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("c04180"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("d04180"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("e04180"))),
+ () -> assertEquals(BoardModel.MODEL_500, BoardModel.getByRevisionCode(RevisionCode.of("d04190"))),
+ () -> assertEquals(BoardModel.MODEL_500_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("e04190"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("b041a0"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("c041a0"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("d041a0"))),
+ () -> assertEquals(BoardModel.COMPUTE_5, BoardModel.getByRevisionCode(RevisionCode.of("e041a0"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("0002"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("0003"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("0004"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("0005"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("0006"))),
+ () -> assertEquals(BoardModel.MODEL_1_A, BoardModel.getByRevisionCode(RevisionCode.of("0007"))),
+ () -> assertEquals(BoardModel.MODEL_1_A, BoardModel.getByRevisionCode(RevisionCode.of("0008"))),
+ () -> assertEquals(BoardModel.MODEL_1_A, BoardModel.getByRevisionCode(RevisionCode.of("0009"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("000d"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("000e"))),
+ () -> assertEquals(BoardModel.MODEL_1_B, BoardModel.getByRevisionCode(RevisionCode.of("000f"))),
+ () -> assertEquals(BoardModel.MODEL_1_B_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("0010"))),
+ () -> assertEquals(BoardModel.COMPUTE_1, BoardModel.getByRevisionCode(RevisionCode.of("0011"))),
+ () -> assertEquals(BoardModel.MODEL_1_A_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("0012"))),
+ () -> assertEquals(BoardModel.MODEL_1_B_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("0013"))),
+ () -> assertEquals(BoardModel.COMPUTE_1, BoardModel.getByRevisionCode(RevisionCode.of("0014"))),
+ () -> assertEquals(BoardModel.MODEL_1_A_PLUS, BoardModel.getByRevisionCode(RevisionCode.of("0015"))),
+ () -> assertEquals(BoardModel.UNKNOWN, BoardModel.getByRevisionCode(RevisionCode.of("ffffffff")))
+ );
+ }
+
@Test
void getBoardModelByBoardCode() {
assertAll(
diff --git a/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/RevisionCodeTest.java b/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/RevisionCodeTest.java
new file mode 100644
index 000000000..6733ad5e4
--- /dev/null
+++ b/pi4j-core/src/test/java/com/pi4j/boardinfo/definition/RevisionCodeTest.java
@@ -0,0 +1,172 @@
+package com.pi4j.boardinfo.definition;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RevisionCodeTest {
+
+ @Test
+ void getInvalidRevisionCode() {
+ assertAll(
+ () -> assertThrows(IllegalArgumentException.class, () -> RevisionCode.of(null)),
+ () -> assertThrows(IllegalArgumentException.class, () -> RevisionCode.of("")),
+ () -> assertThrows(IllegalArgumentException.class, () -> RevisionCode.of("0X")),
+ () -> assertThrows(IllegalArgumentException.class, () -> RevisionCode.of("0xBadHex")),
+ () -> assertThrows(IllegalArgumentException.class, () -> RevisionCode.of("FFFFFFFFF"))
+ );
+ }
+
+ @Test
+ void testHashCodeEqualsToString() {
+ final Object obj = new Object();
+ final String rc1Str = "e04180";
+ final String rc2Str = "0XE04180";
+ final int rcInt = 0xe04180;
+ final RevisionCode rc1 = RevisionCode.of(rc1Str);
+ final RevisionCode rc2 = RevisionCode.of(rc2Str);
+ assertAll(
+ () -> assertNotEquals(rc1Str, rc2Str),
+ () -> assertNotEquals(rc1, obj),
+ () -> assertEquals(rc1, rc2),
+ () -> assertEquals(rc1.hashCode(), rc2.hashCode()),
+ () -> assertEquals(rc1.toString(), rc2.toString()),
+ () -> assertNotEquals(rc1, RevisionCode.of(rcInt ^ (0x1 << 21))), // Flip a memory size bit.
+ () -> assertNotEquals(rc1, RevisionCode.of(rcInt ^ (0x1 << 5))), // Flip a type bit.
+ () -> assertNotEquals(rc1, RevisionCode.of(rcInt ^ (0x1 << 14))), // Flip a processor bit.
+ () -> assertNotEquals(rc1, RevisionCode.of(rcInt ^ 0x1)) // Flip a revision bit.
+ );
+ }
+
+ @Test
+ void getNewStyleRevisionCode() {
+ int revisionCode = 0x800000;
+ revisionCode |= (0x4 << 20); // Set MemorySize GB_4
+ revisionCode |= (0x4 << 12); // Set Processor: BCM2712
+ revisionCode |= (0x17 << 4); // Set Type: RPI_5
+ revisionCode |= 1; // Set Revision: 1
+ RevisionCode actual = RevisionCode.of(revisionCode);
+ assertAll(
+ () -> assertEquals(RevisionCode.MemorySize.GB_4, actual.memorySize()),
+ () -> assertEquals(RevisionCode.Processor.BCM2712, actual.processor()),
+ () -> assertEquals(RevisionCode.Type.RPI_5, actual.type()),
+ () -> assertEquals(1, actual.revision())
+ );
+ }
+
+ @Test
+ void getOldStyleRevisionCode() {
+ String revisionCode = "0x0002";
+ RevisionCode actual = RevisionCode.of(revisionCode);
+ assertAll(
+ () -> assertEquals(RevisionCode.MemorySize.MB_256, actual.memorySize()),
+ () -> assertEquals(RevisionCode.Processor.BCM2835, actual.processor()),
+ () -> assertEquals(RevisionCode.Type.RPI_B, actual.type()),
+ () -> assertEquals(1, actual.revision())
+ );
+ }
+
+ @Test
+ void getUnknownRevisionCode() {
+ String revisionCode = "0x0001";
+ RevisionCode actual = RevisionCode.of(revisionCode);
+ assertAll(
+ () -> assertEquals(RevisionCode.MemorySize.UNKNOWN, actual.memorySize()),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, actual.processor()),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, actual.type()),
+ () -> assertEquals(1, actual.revision())
+ );
+ }
+
+ @Test
+ void getMemorySizeFromRevisionCode() {
+ assertAll(
+ // New-style
+ () -> assertEquals(RevisionCode.MemorySize.MB_256, RevisionCode.MemorySize.fromRevisionCode(0x800000)),
+ () -> assertEquals(RevisionCode.MemorySize.MB_512, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x1 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.GB_1, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x2 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.GB_2, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x3 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.GB_4, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x4 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.GB_8, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x5 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.GB_16, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x6 << 20))),
+ () -> assertEquals(RevisionCode.MemorySize.OTHER, RevisionCode.MemorySize.fromRevisionCode(0x800000 | (0x7 << 20))),
+ // Old-style
+ () -> assertEquals(RevisionCode.MemorySize.MB_256, RevisionCode.MemorySize.fromRevisionCode(2)),
+ () -> assertEquals(RevisionCode.MemorySize.MB_512, RevisionCode.MemorySize.fromRevisionCode(16)),
+ () -> assertEquals(RevisionCode.MemorySize.OTHER, RevisionCode.MemorySize.fromRevisionCode(21)),
+ () -> assertEquals(RevisionCode.MemorySize.UNKNOWN, RevisionCode.MemorySize.fromRevisionCode(0))
+ );
+ }
+
+ @Test
+ void getTypeFromRevisionCode() {
+ assertAll(
+ // New-style
+ () -> assertEquals(RevisionCode.Type.RPI_A, RevisionCode.Type.fromRevisionCode(0x800000)),
+ () -> assertEquals(RevisionCode.Type.RPI_B, RevisionCode.Type.fromRevisionCode(0x800000 | (0x01 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_A_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x02 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_B_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x03 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_2B, RevisionCode.Type.fromRevisionCode(0x800000 | (0x04 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_ALPHA, RevisionCode.Type.fromRevisionCode(0x800000 | (0x05 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM1, RevisionCode.Type.fromRevisionCode(0x800000 | (0x06 << 4))),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, RevisionCode.Type.fromRevisionCode(0x800000 | (0x07 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_3B, RevisionCode.Type.fromRevisionCode(0x800000 | (0x08 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_ZERO, RevisionCode.Type.fromRevisionCode(0x800000 | (0x09 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM3, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0a << 4))),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0b << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_ZERO_W, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0c << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_3B_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0d << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_3A_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0e << 4))),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, RevisionCode.Type.fromRevisionCode(0x800000 | (0x0f << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM3_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x10 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_4B, RevisionCode.Type.fromRevisionCode(0x800000 | (0x11 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_ZERO_2_W, RevisionCode.Type.fromRevisionCode(0x800000 | (0x12 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_400, RevisionCode.Type.fromRevisionCode(0x800000 | (0x13 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM4, RevisionCode.Type.fromRevisionCode(0x800000 | (0x14 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM4S, RevisionCode.Type.fromRevisionCode(0x800000 | (0x15 << 4))),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, RevisionCode.Type.fromRevisionCode(0x800000 | (0x16 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_5, RevisionCode.Type.fromRevisionCode(0x800000 | (0x17 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM5, RevisionCode.Type.fromRevisionCode(0x800000 | (0x18 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_500_500_PLUS, RevisionCode.Type.fromRevisionCode(0x800000 | (0x19 << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM5_LITE, RevisionCode.Type.fromRevisionCode(0x800000 | (0x1a << 4))),
+ () -> assertEquals(RevisionCode.Type.RPI_CM0, RevisionCode.Type.fromRevisionCode(0x800000 | (0x1b << 4))),
+ // Old-style
+ () -> assertEquals(RevisionCode.Type.RPI_A, RevisionCode.Type.fromRevisionCode(0x7)),
+ () -> assertEquals(RevisionCode.Type.RPI_B, RevisionCode.Type.fromRevisionCode(0x3)),
+ () -> assertEquals(RevisionCode.Type.RPI_A_PLUS, RevisionCode.Type.fromRevisionCode(0x12)),
+ () -> assertEquals(RevisionCode.Type.RPI_B_PLUS, RevisionCode.Type.fromRevisionCode(0x13)),
+ () -> assertEquals(RevisionCode.Type.RPI_CM1, RevisionCode.Type.fromRevisionCode(0x11)),
+ () -> assertEquals(RevisionCode.Type.UNKNOWN, RevisionCode.Type.fromRevisionCode(0x1a))
+ );
+ }
+
+ @Test
+ void getProcessorFromRevisionCode() {
+ assertAll(
+ // New-style
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x800000)),
+ () -> assertEquals(RevisionCode.Processor.BCM2836, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x1 << 12))),
+ () -> assertEquals(RevisionCode.Processor.BCM2837, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x2 << 12))),
+ () -> assertEquals(RevisionCode.Processor.BCM2711, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x3 << 12))),
+ () -> assertEquals(RevisionCode.Processor.BCM2712, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x4 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x5 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x6 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x7 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x8 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0x9 << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xa << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xb << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xc << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xd << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xe << 12))),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x800000 | (0xf << 12))),
+ // Old-style
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x2)),
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x3)),
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x4)),
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x5)),
+ () -> assertEquals(RevisionCode.Processor.BCM2835, RevisionCode.Processor.fromRevisionCode(0x6)),
+ () -> assertEquals(RevisionCode.Processor.UNKNOWN, RevisionCode.Processor.fromRevisionCode(0x1a))
+ );
+ }
+}