• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SpiNNakerManchester / JavaSpiNNaker / 6233274834

19 Sep 2023 08:46AM UTC coverage: 36.409% (-0.6%) from 36.982%
6233274834

Pull #658

github

dkfellows
Merge branch 'master' into java-17
Pull Request #658: Update Java version to 17

1656 of 1656 new or added lines in 260 files covered. (100.0%)

8373 of 22997 relevant lines covered (36.41%)

0.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.74
/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/FirmwareLoader.java
1
/*
2
 * Copyright (c) 2022 The University of Manchester
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package uk.ac.manchester.spinnaker.alloc.bmp;
17

18
import static java.lang.Integer.parseUnsignedInt;
19
import static java.lang.Math.min;
20
import static java.lang.String.format;
21
import static java.lang.System.currentTimeMillis;
22
import static java.nio.ByteOrder.LITTLE_ENDIAN;
23
import static java.nio.charset.StandardCharsets.UTF_8;
24
import static java.time.Instant.ofEpochSecond;
25
import static java.util.Arrays.stream;
26
import static java.util.stream.Collectors.toList;
27
import static org.apache.commons.io.IOUtils.buffer;
28
import static org.slf4j.LoggerFactory.getLogger;
29
import static uk.ac.manchester.spinnaker.alloc.bmp.DataSectorTypes.BITFILE;
30
import static uk.ac.manchester.spinnaker.alloc.bmp.DataSectorTypes.REGISTER;
31
import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE;
32
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_ALL;
33
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_E_S;
34
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_N_NE;
35
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_SW_W;
36
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.LEDO;
37
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SCRM;
38
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SLEN;
39
import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc;
40
import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly;
41
import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC;
42

43
import java.io.FileNotFoundException;
44
import java.io.IOException;
45
import java.io.InputStream;
46
import java.io.Serial;
47
import java.nio.ByteBuffer;
48
import java.nio.charset.StandardCharsets;
49
import java.util.ArrayList;
50
import java.util.HashMap;
51
import java.util.List;
52
import java.util.Map;
53
import java.util.Properties;
54
import java.util.zip.CRC32;
55

56
import javax.annotation.PostConstruct;
57

58
import org.slf4j.Logger;
59
import org.springframework.beans.factory.annotation.Autowired;
60
import org.springframework.beans.factory.annotation.Value;
61
import org.springframework.core.io.Resource;
62
import org.springframework.stereotype.Component;
63

64
import uk.ac.manchester.spinnaker.alloc.model.Prototype;
65
import uk.ac.manchester.spinnaker.machine.MemoryLocation;
66
import uk.ac.manchester.spinnaker.machine.board.BMPBoard;
67
import uk.ac.manchester.spinnaker.messages.model.FPGA;
68
import uk.ac.manchester.spinnaker.messages.model.FPGALinkRegisters;
69
import uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters;
70
import uk.ac.manchester.spinnaker.transceiver.BMPTransceiverInterface;
71
import uk.ac.manchester.spinnaker.transceiver.ProcessException;
72

73
/**
74
 * Handles loading of firmware into a BMP or an FPGA.
75
 *
76
 * @author Donal Fellows
77
 */
78
@Component
79
@Prototype
80
public class FirmwareLoader {
81
        private static final Logger log = getLogger(FirmwareLoader.class);
1✔
82

83
        private static final int FLASH_DATA_LENGTH = 4096;
84

85
        private static final int CRC_OFFSET = FLASH_DATA_LENGTH - WORD_SIZE;
86

87
        private static final int CRC_BUFFER_LENGTH = 8192;
88

89
        private static final long CRC_MASK = 0xffffffffL;
90

91
        private static final MemoryLocation FLASH_DATA_ADDRESS =
1✔
92
                        new MemoryLocation(0x1000);
93

94
        private static final MemoryLocation BITFILE_BASE =
1✔
95
                        new MemoryLocation(0x200000);
96

97
        private static final int BITFILE_MAX_SIZE = 0x180000;
98

99
        private static final int DATA_SECTOR_CHUNK_SIZE = 128;
100

101
        private static final int NUM_DATA_SECTORS = 16;
102

103
        private static final int FPGA_ID_MASK = 0b00000011;
104

105
        private static final int CHIP_MASK = 0b111;
106

107
        /**
108
         * Location of FPGA register control instructions within the flash data
109
         * sector.
110
         */
111
        private static final int REGISTER_DATA_SECTOR_LOCATION =
112
                        6 * DATA_SECTOR_CHUNK_SIZE;
113

114
        /**
115
         * Location of bitfile control instructions within the flash data sector.
116
         */
117
        private static final int BITFILE_DATA_SECTOR_LOCATION =
118
                        DATA_SECTOR_CHUNK_SIZE;
119

120
        private static final int DATA_SECTOR_LENGTH = 128;
121

122
        private static final int DATA_SECTOR_HEADER_WORDS = 8;
123

124
        private static final int DATA_SECTOR_HEADER_BYTES =
125
                        DATA_SECTOR_HEADER_WORDS * WORD_SIZE;
126

127
        private static final int BITFILE_NAME_MAX_LENGTH = 96;
128

129
        private static final int BITFILE_ENABLED_FLAG = 0x8000;
130

131
        private static final int PAD = 0xffffffff;
132

133
        /** Used to clear all bits in a register. */
134
        private static final int CLEAR = 0;
135

136
        /** Used to set all bits in a register. */
137
        private static final int SET = 0xffffffff;
138

139
        private static final String[] CHIP_LABELS = {
1✔
140
                "", "0", "1", "10", "2", "20", "21", "210"
141
        };
142

143
        private static final String[] SLOT_LABELS = {
1✔
144
                "", "", "S0", "S1", "S2", "S3", "", "", "", "", "", "", "", "", "", ""
145
        };
146

147
        private static final double SMALL_SLEEP = 0.25;
148

149
        private static final double BIG_SLEEP = 12.0;
150

151
        private final BMPBoard board;
152

153
        private final BMPTransceiverInterface txrx;
154

155
        @Autowired
156
        private FirmwareDefinition firmware;
157

158
        /**
159
         * @param txrx
160
         *            How to talk to BMPs. The specific BMP to talk to must have
161
         *            been bound.
162
         * @param board
163
         *            Which board's BMP are we really working with.
164
         */
165
        public FirmwareLoader(BMPTransceiverInterface txrx, BMPBoard board) {
1✔
166
                this.txrx = txrx;
1✔
167
                this.board = board;
1✔
168
        }
1✔
169

170
        /** Base class of exceptions thrown by the firmware loader. */
171
        public abstract static class FirmwareLoaderException
172
                        extends RuntimeException {
173
                @Serial
174
                private static final long serialVersionUID = -7057612243855126410L;
175

176
                FirmwareLoaderException(String msg) {
177
                        super(msg);
×
178
                }
×
179
        }
180

181
        /** An update of the firmware on a BMP failed. */
182
        public static class UpdateFailedException extends FirmwareLoaderException {
183
                @Serial
184
                private static final long serialVersionUID = 7925582707336953554L;
185

186
                /** The data read back from the BMP. */
187
                public final ByteBuffer data;
188

189
                UpdateFailedException(ByteBuffer data) {
190
                        super("failed to update flash data correctly!");
×
191
                        this.data = readOnly(data);
×
192
                }
×
193
        }
194

195
        /** A CRC check failed. */
196
        public static class CRCFailedException extends FirmwareLoaderException {
197
                @Serial
198
                private static final long serialVersionUID = -4111893327837084643L;
199

200
                /** The CRC calculated by the BMP. */
201
                public final int crc;
202

203
                CRCFailedException(int crc) {
204
                        super(format("CRC by BMP failed to match: 0x%08x", crc));
×
205
                        this.crc = crc;
×
206
                }
×
207
        }
208

209
        /** A data chunk was too large for the firmware loader to handle. */
210
        public static class TooLargeException extends FirmwareLoaderException {
211
                @Serial
212
                private static final long serialVersionUID = -9025065456329109710L;
213

214
                TooLargeException(long size) {
215
                        super(format("Bit file is too large for BMP buffer: 0x%08x", size));
×
216
                }
×
217
        }
218

219
        private static final int SUB_WORD_MASK = 3;
220

221
        private static boolean notAligned(int offset) {
222
                return (offset & SUB_WORD_MASK) != 0;
1✔
223
        }
224

225
        private static class FlashDataSector {
226
                final ByteBuffer buf;
227

228
                FlashDataSector() {
1✔
229
                        buf = alloc(DATA_SECTOR_LENGTH);
1✔
230
                }
1✔
231

232
                static FlashDataSector registers(int numItems, List<Integer> data) {
233
                        var fds = new FlashDataSector();
1✔
234
                        fds.registersHeader(numItems);
1✔
235
                        fds.registersPayload(data);
1✔
236
                        fds.buf.flip();
1✔
237
                        return fds;
1✔
238
                }
239

240
                static FlashDataSector bitfile(String name, int mtime, int crc,
241
                                FPGA chip, int timestamp, MemoryLocation baseAddress,
242
                                int length) {
243
                        var fds = new FlashDataSector();
1✔
244
                        fds.bitfileHeader(mtime, crc, chip, timestamp, baseAddress, length);
1✔
245
                        fds.bitfileName(name);
1✔
246
                        fds.buf.flip();
1✔
247
                        return fds;
1✔
248
                }
249

250
                private void pad(int targetLength, int value) {
251
                        while (buf.position() < targetLength
1✔
252
                                        && notAligned(buf.position())) {
1✔
253
                                buf.put((byte) value);
1✔
254
                        }
255
                        while (buf.position() < targetLength) {
1✔
256
                                buf.putInt(value);
1✔
257
                        }
258
                }
1✔
259

260
                private void registersHeader(int numItems) {
261
                        buf.put(REGISTER.value);
1✔
262
                        buf.put((byte) numItems);
1✔
263
                        pad(DATA_SECTOR_HEADER_BYTES, 0);
1✔
264
                }
1✔
265

266
                private void registersPayload(List<Integer> data) {
267
                        for (int item : data) {
1✔
268
                                buf.putInt(item);
1✔
269
                        }
1✔
270
                        pad(buf.capacity(), PAD);
1✔
271
                }
1✔
272

273
                private void bitfileHeader(int mtime, int crc, FPGA chip, int timestamp,
274
                                MemoryLocation baseAddress, int length) {
275
                        buf.put(BITFILE.value);
1✔
276

277
                        buf.put((byte) 0);
1✔
278
                        buf.putShort((short) (BITFILE_ENABLED_FLAG + chip.bits));
1✔
279
                        buf.putInt(timestamp);
1✔
280
                        buf.putInt(crc);
1✔
281
                        buf.putInt(baseAddress.address());
1✔
282
                        buf.putInt(length);
1✔
283
                        buf.putInt(mtime);
1✔
284

285
                        buf.putInt(PAD);
1✔
286
                        buf.putInt(PAD);
1✔
287
                }
1✔
288

289
                private void bitfileName(String name) {
290
                        var namedata = name.getBytes(StandardCharsets.UTF_8);
1✔
291
                        int namesize = min(namedata.length, BITFILE_NAME_MAX_LENGTH);
1✔
292
                        buf.put(1, (byte) namesize);
1✔
293
                        buf.put(namedata, 0, namesize);
1✔
294
                        while (buf.position() < buf.capacity()) {
1✔
295
                                buf.put((byte) 0);
1✔
296
                        }
297
                }
1✔
298
        }
299

300
        /**
301
         * Instructions to set a register on one or more FPGAs. This is done not
302
         * just immediately, but also during BMP boot.
303
         *
304
         * @author Donal Fellows
305
         * @param fpga
306
         *            Which FPGA's register to set
307
         * @param address
308
         *            The location of the register to set in the FPGA address space
309
         * @param value
310
         *            What value to set
311
         */
312
        public record RegisterSet(FPGA fpga, MemoryLocation address, int value) {
1✔
313
                /**
314
                 * @param fpga
315
                 *            Which FPGA's registers to set
316
                 * @param register
317
                 *            Which register is this
318
                 * @param value
319
                 *            The value to set
320
                 */
321
                public RegisterSet(FPGA fpga, FPGAMainRegisters register, int value) {
322
                        this(fpga, register.getAddress(), value);
1✔
323
                }
1✔
324

325
                /**
326
                 * @param fpga
327
                 *            Which FPGA's registers to set
328
                 * @param register
329
                 *            Which register is this
330
                 * @param bank
331
                 *            In which register bank (i.e., for which link)
332
                 * @param value
333
                 *            The value to set
334
                 */
335
                public RegisterSet(FPGA fpga, FPGALinkRegisters register, int bank,
336
                                int value) {
337
                        this(fpga, register.address(bank), value);
×
338
                }
×
339
        }
340

341
        private static void putBuffer(ByteBuffer target, ByteBuffer source,
342
                        int offset) {
343
                // Dupe so we can freely manipulate the position
344
                var slice = target.duplicate();
1✔
345
                slice.position(offset);
1✔
346
                slice.put(source);
1✔
347
        }
1✔
348

349
        private ByteBuffer readFlashData()
350
                        throws ProcessException, IOException, InterruptedException {
351
                return txrx.readBMPMemory(board, FLASH_DATA_ADDRESS, FLASH_DATA_LENGTH);
1✔
352
        }
353

354
        private ByteBuffer readFlashDataHead()
355
                        throws ProcessException, IOException, InterruptedException {
356
                return txrx.readBMPMemory(board, FLASH_DATA_ADDRESS,
1✔
357
                                FLASH_DATA_LENGTH / 2);
358
        }
359

360
        private static int crc(ByteBuffer buffer, int from, int len) {
361
                var crc = new CRC32();
1✔
362
                crc.update(buffer.slice(from, len));
1✔
363
                return (int) (crc.getValue() & CRC_MASK);
1✔
364
        }
365

366
        private static int crc(Resource r) throws IOException {
367
                try (var s = buffer(r.getInputStream())) {
1✔
368
                        return crc(s);
1✔
369
                }
370
        }
371

372
        private static int crc(InputStream s) throws IOException {
373
                var crc = new CRC32();
1✔
374
                var buffer = new byte[CRC_BUFFER_LENGTH];
1✔
375
                while (true) {
376
                        int len = s.read(buffer);
1✔
377
                        if (len < 1) {
1✔
378
                                break;
1✔
379
                        }
380
                        crc.update(buffer, 0, len);
1✔
381
                }
1✔
382
                return (int) (crc.getValue() & CRC_MASK);
1✔
383
        }
384

385
        private void updateFlashData(ByteBuffer data)
386
                        throws ProcessException, IOException, InterruptedException {
387
                data.putInt(CRC_OFFSET, ~crc(data, 0, CRC_OFFSET));
1✔
388
                data.position(0);
1✔
389
                var fb = txrx.getSerialFlashBuffer(board);
1✔
390
                txrx.writeBMPMemory(board, fb, data);
1✔
391
                txrx.writeBMPFlash(board, FLASH_DATA_ADDRESS);
1✔
392
                var newData = readFlashData();
1✔
393
                if (!data.equals(newData)) {
1✔
394
                        throw new UpdateFailedException(newData);
×
395
                }
396
        }
1✔
397

398
        private void logBMPVersion()
399
                        throws ProcessException, IOException, InterruptedException {
400
                var info = txrx.readBMPVersion(board);
1✔
401
                log.info("BMP INFO:       {}",
1✔
402
                                format("%s %s at %s:%s (built %s) [C=%s, F=%s, B=%s]",
1✔
403
                                                info.name, info.versionNumber, info.hardware,
404
                                                info.physicalCPUID, ofEpochSecond(info.buildDate),
1✔
405
                                                info.location.getCabinet(), info.location.getFrame(),
1✔
406
                                                info.location.getBoard()));
1✔
407
        }
1✔
408

409
        /**
410
         * The read part of {@code cmd_xreg} and {@code cmd_xboot} from
411
         * {@code bmpc}. This merges the two because there's little point in keeping
412
         * them separate for our use case and that avoids doing some network
413
         * traffic.
414
         */
415
        private void listFPGABootChunks()
416
                        throws ProcessException, IOException, InterruptedException {
417
                var data = readFlashDataHead();
1✔
418
                for (int i = 0; i < NUM_DATA_SECTORS; i++) {
1✔
419
                        var chunk = data.slice(DATA_SECTOR_CHUNK_SIZE * i,
1✔
420
                                        DATA_SECTOR_CHUNK_SIZE).order(LITTLE_ENDIAN);
1✔
421
                        byte type = chunk.get();
1✔
422
                        switch (DataSectorTypes.get(type)) {
1✔
423
                        case REGISTER -> logRegisterSets(chunk);
1✔
424
                        case BITFILE -> logFPGABootBitfile(chunk, i);
1✔
425
                        default -> log.trace("ignoring chunk with type code {}", type);
1✔
426
                        }
427
                }
428
        }
1✔
429

430
        /**
431
         * Describe the register set control commands in the current chunk of boot
432
         * header.
433
         *
434
         * @param chunk
435
         *            The chunk, positioned immediately after the type byte.
436
         */
437
        private void logRegisterSets(ByteBuffer chunk) {
438
                int size = chunk.get();
1✔
439
                // Position after the header
440
                chunk.position(DATA_SECTOR_HEADER_BYTES);
1✔
441
                for (int j = 0; j < size; j++) {
1✔
442
                        int addr = chunk.getInt();
1✔
443
                        int value = chunk.getInt();
1✔
444
                        log.info("FPGA REGISTERS: {}",
1✔
445
                                        format("%3s %08x %08x", FPGA.values()[addr & FPGA_ID_MASK],
1✔
446
                                                        addr & ~FPGA_ID_MASK, value));
1✔
447
                }
448
        }
1✔
449

450
        /**
451
         * Describe the installed bitfile used to boot an FPGA, using the
452
         * information in the current chunk of boot header.
453
         *
454
         * @param data
455
         *            The chunk, positioned immediately after the type byte.
456
         * @param i
457
         *            The index of the chunk
458
         */
459
        private void logFPGABootBitfile(ByteBuffer data, int i) {
460
                int size = data.get();
1✔
461
                int flags = data.getShort();
1✔
462
                int time = data.getInt();
1✔
463
                int crc = data.getInt();
1✔
464
                int base = data.getInt();
1✔
465
                int length = data.getInt();
1✔
466
                int mtime = data.getInt();
1✔
467
                var filenameBytes = new byte[size];
1✔
468
                data.position(DATA_SECTOR_HEADER_BYTES);
1✔
469
                data.get(filenameBytes, 0, size);
1✔
470

471
                var state = (flags & BITFILE_ENABLED_FLAG) > 0 ? "ENABLED "
1✔
472
                                : "DISABLED";
1✔
473
                log.info("FPGA BOOT:      {}", format(
1✔
474
                                "%3s  %s  Chips %-3s, Base 0x%06x, Length %8d, CRC 0x%08x",
475
                                SLOT_LABELS[i], state, CHIP_LABELS[flags & CHIP_MASK], base,
1✔
476
                                length, crc));
1✔
477
                log.info("FPGA BOOT:           File      {}",
1✔
478
                                new String(filenameBytes, 0, size, UTF_8).strip());
1✔
479
                log.info("FPGA BOOT:           Written   {}", ofEpochSecond(time));
1✔
480
                log.info("FPGA BOOT:           ModTime   {}", ofEpochSecond(mtime));
1✔
481
        }
1✔
482

483
        /**
484
         * The write part of {@code cmd_xreg} from {@code bmpc}.
485
         *
486
         * @param settings
487
         *            The registers to set.
488
         * @throws IOException
489
         *             If anything goes wrong with networking.
490
         * @throws ProcessException
491
         *             If SpiNNaker rejects a message.
492
         * @throws InterruptedException
493
         *             If the communications were interrupted.
494
         * @throws UpdateFailedException
495
         *             If the flash data sector read back does not match what we
496
         *             wanted to write.
497
         */
498
        public void setupRegisters(RegisterSet... settings)
499
                        throws ProcessException, IOException, InterruptedException {
500
                var data = new ArrayList<Integer>();
1✔
501
                for (var r : settings) {
1✔
502
                        data.add(r.address().address() | r.fpga().value);
1✔
503
                        data.add(r.value());
1✔
504
                }
505
                var sector = FlashDataSector.registers(settings.length, data);
1✔
506

507
                var flashData = readFlashData();
1✔
508
                putBuffer(flashData, sector.buf, REGISTER_DATA_SECTOR_LOCATION);
1✔
509
                updateFlashData(flashData);
1✔
510
        }
1✔
511

512
        /**
513
         * Set a bitfile to be loaded.
514
         *
515
         * @param handle
516
         *            The bitfile handle, matching one of the resources known to
517
         *            this bean.
518
         * @param slot
519
         *            Which slot to install the bitfile in.
520
         * @param chip
521
         *            Which chip or chips are to load the bitfile.
522
         * @throws IOException
523
         *             If anything goes wrong with networking.
524
         * @throws ProcessException
525
         *             If SpiNNaker rejects a message.
526
         * @throws TooLargeException
527
         *             If the bitfile is too large for the BMP's buffer.
528
         * @throws CRCFailedException
529
         *             If the written bitfile fails its CRC check.
530
         * @throws UpdateFailedException
531
         *             If the flash data sector read back does not match what we
532
         *             wanted to write.
533
         * @throws InterruptedException
534
         *             If the communications were interrupted.
535
         */
536
        public void setupBitfile(String handle, int slot, FPGA chip)
537
                        throws IOException, ProcessException, InterruptedException {
538
                var resource = firmware.resource(handle);
1✔
539
                int mtime = firmware.mtime(handle);
1✔
540
                var name = resource.getFilename();
1✔
541
                int size = (int) resource.contentLength();
1✔
542
                int crc = crc(resource);
1✔
543

544
                if (size > BITFILE_MAX_SIZE) {
1✔
545
                        throw new TooLargeException(size);
×
546
                }
547

548
                var base = BITFILE_BASE.add(slot * BITFILE_MAX_SIZE);
1✔
549
                try (var s = buffer(resource.getInputStream())) {
1✔
550
                        txrx.writeSerialFlash(board, base, size, s);
1✔
551
                }
552
                int otherCRC = txrx.readSerialFlashCRC(board, base, size);
1✔
553
                if (otherCRC != crc) {
1✔
554
                        throw new CRCFailedException(otherCRC);
×
555
                }
556

557
                int timestamp = (int) (currentTimeMillis() / MSEC_PER_SEC);
1✔
558

559
                var sector = FlashDataSector.bitfile(name, mtime, crc, chip,
1✔
560
                                timestamp, base, size);
561

562
                var flashData = readFlashData();
1✔
563
                putBuffer(flashData, sector.buf, BITFILE_DATA_SECTOR_LOCATION);
1✔
564
                updateFlashData(flashData);
1✔
565
        }
1✔
566

567
        private void sleep(double secs) throws InterruptedException {
568
                Thread.sleep((long) (secs * MSEC_PER_SEC));
1✔
569
        }
1✔
570

571
        /**
572
         * Load the FPGA definitions.
573
         *
574
         * @param postDelay
575
         *            Whether to do the long delay after the FPGA boot/check
576
         * @throws InterruptedException
577
         *             If interrupted while sleeping
578
         * @throws ProcessException
579
         *             If a BMP rejects a message
580
         * @throws IOException
581
         *             If the network fails or the packaged bitfiles are unreadable
582
         * @throws FirmwareLoaderException
583
         *             If something goes wrong.
584
         */
585
        public void bitLoad(boolean postDelay)
586
                        throws InterruptedException, ProcessException, IOException {
587
                // Bleah
588
                int idx = 0;
1✔
589

590
                setupRegisters(new RegisterSet(FPGA_ALL, SCRM, CLEAR),
1✔
591
                                new RegisterSet(FPGA_ALL, SLEN, SET),
592
                                new RegisterSet(FPGA_ALL, LEDO, CLEAR));
593
                sleep(SMALL_SLEEP);
1✔
594

595
                var nameDef = firmware.bitfileNames.get(idx);
1✔
596
                setupBitfile(nameDef, 0, FPGA_ALL);
1✔
597
                idx++;
1✔
598

599
                sleep(SMALL_SLEEP);
1✔
600

601
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_E_S);
1✔
602
                idx++;
1✔
603
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_SW_W);
1✔
604
                idx++;
1✔
605
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_N_NE);
1✔
606

607
                // TODO these read the configuration... but are they necessary?
608
                listFPGABootChunks();
1✔
609
                logBMPVersion();
1✔
610

611
                if (postDelay) {
1✔
612
                        log.info("beginning post-load delay");
1✔
613
                        sleep(BIG_SLEEP);
1✔
614
                        log.info("completed post-load delay");
1✔
615
                }
616
        }
1✔
617
}
618

619
enum DataSectorTypes {
1✔
620
        /** Chunk describes a bitfile to load. */
621
        BITFILE(3),
1✔
622
        /** Chunk describes some registers to set. */
623
        REGISTER(4),
1✔
624
        /** Chunk is not recognised. */
625
        @Deprecated
1✔
626
        UNKNOWN(-1);
627

628
        /** The value of the chunk's type code. */
629
        final byte value;
630

631
        DataSectorTypes(int value) {
1✔
632
                this.value = (byte) value;
1✔
633
        }
1✔
634

635
        static DataSectorTypes get(byte val) {
636
                if (val == BITFILE.value) {
1✔
637
                        return BITFILE;
1✔
638
                } else if (val == REGISTER.value) {
1✔
639
                        return REGISTER;
1✔
640
                } else {
641
                        return UNKNOWN;
1✔
642
                }
643
        }
644
}
645

646
@Component
647
class FirmwareDefinition {
1✔
648
        private static final Logger log = getLogger(FirmwareDefinition.class);
1✔
649

650
        @Value("classpath:bitfiles/manifest.properties")
651
        private Resource manifestLocation;
652

653
        /** The <em>ordered</em> list of firmware filenames from the manifest. */
654
        List<String> bitfileNames;
655

656
        /** Where to load each of the bitfiles from. */
657
        private final Map<String, Resource> bitFiles = new HashMap<>();
1✔
658

659
        /**
660
         * What the intended modification time of each of the bitfiles is. From the
661
         * manifest, because actual file modification times are broken.
662
         */
663
        private final Map<String, Integer> modTimes = new HashMap<>();
1✔
664

665
        @PostConstruct
666
        private void loadManifest() throws IOException {
667
                var props = new Properties();
1✔
668
                try (var is = manifestLocation.getInputStream()) {
1✔
669
                        props.load(is);
1✔
670
                }
671
                bitfileNames = stream(props.getProperty("bitfiles").split(","))
1✔
672
                                .map(String::strip).collect(toList());
1✔
673
                for (var f : bitfileNames) {
1✔
674
                        modTimes.put(f, parseUnsignedInt(props.getProperty(f)));
1✔
675
                        var r = manifestLocation.createRelative(f);
1✔
676
                        try (var dummy = r.getInputStream()) {
1✔
677
                                // We do this to check that the bit file is readable at all
678
                                bitFiles.put(f, r);
1✔
679
                                log.info("loaded firmware definition: {}", f);
1✔
680
                        } catch (IOException e) {
×
681
                                var fnf = new FileNotFoundException(
×
682
                                                "failed to open bitfile resource: " + r);
683
                                fnf.initCause(e);
×
684
                                throw fnf;
×
685
                        }
1✔
686
                }
1✔
687
        }
1✔
688

689
        Resource resource(String handle) {
690
                return bitFiles.get(handle);
1✔
691
        }
692

693
        int mtime(String handle) {
694
                return modTimes.get(handle);
1✔
695
        }
696
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc